PHP Classes

File: lib/Injector/Injector.php

Recommend this page to a friend!
  Classes of Emmanuel Antico   Injector   lib/Injector/Injector.php   Download  
File: lib/Injector/Injector.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Injector
Inject dependencies defined in annotation comments
Author: By
Last change:
Date: 10 years ago
Size: 7,535 bytes
 

Contents

Class file image Download
<?php
namespace Injector;

use
Pimple\Container;
use
Pimple\ServiceProviderInterface;

/**
 * Dependency injection class
 * @author emaphp
 * @package Injector
 */
class Injector {
   
/**
     * Providers array
     * @var array
     */
   
protected static $providers = array();
   
   
/**
     * Obtains a provider class instance
     * @param string $classname
     * @throws \InvalidArgumentException
     * @return ServiceProviderInterface
     */
   
public static function getProvider($classname) {
        if (
array_key_exists($classname, self::$providers)) {
            return
self::$providers[$classname];
        }
       
       
$provider = new $classname;
       
        if (!(
$provider instanceof ServiceProviderInterface)) {
            throw new \
InvalidArgumentException("$classname is not a valid ServiceProviderInterface instance");
        }

       
self::$providers[$classname] = $provider;
        return
self::$providers[$classname];
    }
   
   
/**
     * Creates a new instance of $classname with the specified arguments
     * @param string $classname
     * @param Container $container
     * @param string $args
     * @param array $filter
     * @param array $override
     * @throws \InvalidArgumentException
     * @throws \RuntimeException
     * @return object
     */
   
public static function createWith($classname, Container $container, $args = null, $filter = null, $override = null) {
        if (!
is_string($classname) || empty($classname)) {
            throw new \
InvalidArgumentException("Argument is not a valid class name");
        }
       
       
//obtain provider list for this class
       
$profile = Profiler::getClassProfile($classname);
               
        if (isset(
$profile->constructor)) {
           
//build constructor parameter list
           
$parameters = $profile->constructor->getParameters();
           
            if (
is_null($args)) {
               
$args = [];
            }
            else {
               
$args = is_array($args) ? $args : [$args];
            }
               
            foreach (
$parameters as $param) {
                if (!empty(
$args)) {
                   
$params[] = array_shift($args);
                }
                elseif (
is_array($override) && array_key_exists($param->getName(), $profile->constructorParams) && array_key_exists($profile->constructorParams[$param->getName()], $override)) {
                   
$params[] = $override[$profile->constructorParams[$param->getName()]];
                }
                elseif (
array_key_exists($param->getName(), $profile->constructorParams)) {
                   
//get parameter id
                   
$parameterId = $profile->constructorParams[$param->getName()];
                   
                    if (
is_array($filter) && !in_array($parameterId, $filter)) {
                        if (
$param->isOptional()) {
                           
$params[] = $param->getDefaultValue();
                        }
                        elseif (!
$profile->isStrict) {
                           
$params[] = null;
                        }
                        else {
                            throw new \
RuntimeException(sprintf("Argument %s in class '%s' constructor is associated to a filtered service '%s'", $param->getName(), $classname, $parameterId));
                        }
                    }
                    elseif (
$container->offsetExists($parameterId)) {
                       
//add service to constructor arguments
                       
$params[] = $container->offsetGet($parameterId);
                    }
                    elseif (
$param->isOptional()) {
                       
$params[] = $param->getDefaultValue();
                    }
                    elseif (!
$profile->isStrict) {
                       
$params[] = null;
                    }
                    else {
                        throw new \
RuntimeException(sprintf("Argument %s in class '%s' constructor is associated to a unknown service '%s'", $param->getName(), $classname, $parameterId));
                    }
                }
                elseif (
$param->isOptional()) {
                   
$params[] = $param->getDefaultValue();
                }
                else {
                    throw new \
RuntimeException("Not enough arguments provided for '$classname' constructor");
                }
            }
           
           
$instance = $profile->class->newInstanceArgs($params);
        }
        else {
           
$instance = new $classname;
        }
       
       
self::inject($instance, $container, $filter, $override);
        return
$instance;
    }
   
   
/**
     * Creates a new instance of $classname from the associated providers
     * @param string $classname
     * @param array $args
     * @param array $filter
     * @param array $override
     * @throws \RuntimeException
     * @return object
     */
   
public static function create($classname, $args = null, $filter = null, $override = null) {
       
//obtain provider list for this class
       
$profile = Profiler::getClassProfile($classname);
       
$providers = $profile->providers;
       
        if (empty(
$providers)) {
            throw new \
RuntimeException("Class $classname does not have any provider associated with it");
        }

       
//create new container and register all providers
       
$container = new Container();
       
        foreach (
$providers as $provider) {
           
$providerInstance = self::getProvider($provider);
           
$providerInstance->register($container);
        }

        return
self::createWith($classname, $container, $args, $filter, $override);
    }
   
   
/**
     * Injects a set of dependencies into an instance
     * @param object $instance
     * @param Pimple\Container $container
     * @param array $filter Which dependencies must be injected
     * @param array $override An associative array that overrides a set of injected properties
     * @throws \InvalidArgumentException
     * @throws \RuntimeException
     */
   
public static function inject(&$instance, $container, $filter = null, $override = null) {
        if (!
is_object($instance)) {
            throw new \
InvalidArgumentException("Argument is not a valid object");
        }
       
        if (!
is_object($container)) {
            throw new \
InvalidArgumentException("Container is not a valid object");
        }
        elseif (!(
$container instanceof Container)) {
            throw new \
InvalidArgumentException("Container is not a valid container");
        }
       
       
//check if objects is a stdClass instance
       
if ($instance instanceof \stdClass) {
           
$services = $container->keys();
               
            if (empty(
$filter)) {
               
//inject all
               
for ($i = 0, $n = count($services); $i < $n; $i++) {
                    if (
is_array($override) && array_key_exists($services[$i], $override)) {
                        continue;
                    }
                       
                   
$instance->$services[$i] = $container->offsetGet($services[$i]);
                }
            }
            else {
                foreach (
$filter as $service) {
                   
//check if service exists
                   
if (!in_array($service, $services)) {
                        throw new \
RuntimeException(sprintf("Service '$service' does not exists in container class '%s'", get_class($container)));
                    }
                       
                    if (
is_array($override) && array_key_exists($service, $override)) {
                        continue;
                    }
                       
                   
$instance->$service = $container->offsetGet($service);
                }
            }
               
           
//override values
           
if (is_array($override) && !empty($override)) {
                foreach (
$override as $service => $value) {
                    if (
is_int($service)) {
                        continue;
                    }
                       
                   
$instance->$service = $value;
                }
            }
               
            return;
        }
       
       
//build class profile
       
$classname = get_class($instance);
       
$profile = Profiler::getClassProfile($classname);
       
$services = $container->keys();
       
        foreach (
$profile->reflectionProperties as $name => $property) {
           
$serviceId = $profile->properties[$name];
       
            if (
is_array($override) && array_key_exists($serviceId, $override)) {
                if (
$property->isStatic()) {
                   
$property->setValue(null, $override[$serviceId]);
                }
                else {
                   
$property->setValue($instance, $override[$serviceId]);
                }
            }
            else {
                if (
is_array($filter) && !in_array($serviceId, $filter)) {
                    continue;
                }
               
                if (!
$container->offsetExists($serviceId)) {
                    if (
$profile->isStrict) {
                        throw new \
RuntimeException("Property '$name' in class $classname is associated to a unknown service '$serviceId'");
                    }
                   
                    continue;
                }
                else {
                   
$value = $container->offsetGet($serviceId);
                   
                    if (
$property->isStatic()) {
                       
$property->setValue(null, $value);
                    }
                    else {
                       
$property->setValue($instance, $value);
                    }
                }
            }
        }
    }
}