Last active
May 18, 2016 20:50
-
-
Save chani/3ea3e4ce81274292c0da91b9f866a1cb to your computer and use it in GitHub Desktop.
Just another Dependency Injection Container
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Class LyraInjector | |
* This dependency injection container was written with the goal to be a pure dependency injection container | |
* opposed to the available service-locator hybrid implementations. | |
* | |
* Example 1 | |
* $inj = new LyraInjector(); | |
* // if Twig_LoaderInterface is found as type in Twig_Environment, Twig_Loader_Filesystem will be injected | |
* $inj->addMap('Twig_Environment', array('Twig_LoaderInterface' => 'Twig_Loader_Filesystem')); | |
* // if Twig_Loader_Filesystem is instanced, the parameters paths with value templates/ is used | |
* $inj->addParameters('Twig_Loader_Filesystem', array('paths' => 'templates/')); | |
* // run dependency injection for Twig_Environment. It will return the ready-made object | |
* $twig = $inj->register('Twig_Environment'); | |
* | |
* Example 2 | |
* $inj = new LyraInjector(); | |
* // run dependency injection for Router | |
* $router = $inj->register('\Your\namespace\Router'); | |
* | |
* @author Jean-Michel Bruenn <himself@jeanbruenn.info> | |
* @copyright Copyright (c) 2016, Jean-Michel Bruenn | |
* @license https://opensource.org/licenses/MIT The MIT License (MIT) | |
* @link http://jeanbruenn.info/2016/03/28/dependency-injection-container/ | |
* @link http://jeanbruenn.info/2016/04/03/dependency-injection-container-part-2/ | |
* @link http://jeanbruenn.info/2016/04/04/dependency-injection-container-part-3/ | |
*/ | |
class LyraInjector | |
{ | |
private $reflected = array(); | |
private $instances = array(); | |
private $mappings = array(); | |
private $parameters = array(); | |
private $disableConstructorInjection = false; | |
private $disableSetterInjection = false; | |
private $disableInterfaceInjection = false; | |
/** | |
* @param bool $disable | |
*/ | |
public function disableConstructorInjection($disable = true) | |
{ | |
$this->disableConstructorInjection = $disable; | |
} | |
/** | |
* @param bool $disable | |
*/ | |
public function disableSetterInjection($disable = true) | |
{ | |
$this->disableSetterInjection = $disable; | |
} | |
/** | |
* @param bool $disable | |
*/ | |
public function disableInterfaceInjection($disable = true) | |
{ | |
$this->disableInterfaceInjection = $disable; | |
} | |
/** | |
* @param $key | |
* @param array $value | |
*/ | |
public function addParameters($key, array $value) | |
{ | |
$this->parameters[$key] = $value; | |
} | |
/** | |
* @param $key | |
* @param array $value | |
*/ | |
public function addMap($key, array $value) | |
{ | |
$this->mappings[$key] = $value; | |
} | |
/** | |
* @param $class | |
* @return mixed | |
*/ | |
public function register($class) | |
{ | |
if (!isset($this->reflected[$class])) | |
$this->reflected[$class] = new \ReflectionClass($class); | |
$reflectionClass = $this->reflected[$class]; | |
if ($reflectionClass instanceof \ReflectionClass && $reflectionClass->isInstantiable()) { | |
if (!isset($this->instances[$reflectionClass->name])) { | |
if (!$this->disableConstructorInjection) | |
$this->constructorInjection($reflectionClass); | |
if (!$this->disableSetterInjection) | |
$this->setterInjection($reflectionClass); | |
if (!$this->disableInterfaceInjection) | |
$this->interfaceInjection($reflectionClass); | |
} | |
} | |
return $this->instances[$reflectionClass->name]; | |
} | |
/** | |
* @param \ReflectionClass $reflectionClass | |
*/ | |
private function constructorInjection(\ReflectionClass $reflectionClass) | |
{ | |
$parameters = array(); | |
$reflectionConstructor = $reflectionClass->getConstructor(); | |
if (!is_null($reflectionConstructor)) | |
$parameters = $this->parseReflectionParameters($reflectionConstructor->getParameters()); | |
if (isset($parameters) && count($parameters) > 0) { | |
$this->instances[$reflectionClass->name] = $reflectionClass->newInstanceArgs($parameters); | |
} else { | |
$this->instances[$reflectionClass->name] = $reflectionClass->newInstance(); | |
} | |
} | |
/** | |
* @param $reflectionParameters | |
* @return array | |
*/ | |
private function parseReflectionParameters($reflectionParameters) | |
{ | |
$parameters = array(); | |
$pattern = '/Parameter #\d+ \[ <(?:[A-Za-z]+)>(?: (?P<class>[a-zA-Z\\\\_\x7f-\xff][a-zA-Z0-9\\\\_\x7f-\xff]*)' | |
. '(?: or [A-Za-z]+)?)? \$(?P<variable>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(?: = [A-Za-z]+)? \]/'; | |
foreach ($reflectionParameters as $reflectionParameter) { | |
if ($reflectionParameter instanceof \ReflectionParameter) { | |
$className = $reflectionParameter->getDeclaringClass()->name; | |
preg_match($pattern, $reflectionParameter, $matches); | |
if (isset($matches['class']) && !empty($matches['class']) && $matches['class'] != "array") { | |
if ((isset($this->mappings[$className])) && isset($this->mappings[$className][$matches['class']])) { | |
if (isset($this->instances[$matches['class']])) { | |
$parameters[] = $this->instances[$this->mappings[$className][$matches['class']]]; | |
} else { | |
$parameters[] = $this->register($this->mappings[$className][$matches['class']]); | |
} | |
} else { | |
if (isset($this->instances[$matches['class']])) { | |
$parameters[] = $this->instances[$matches['class']]; | |
} else { | |
$parameters[] = $this->register($matches['class']); | |
} | |
} | |
} else { | |
if (isset($matches['variable'])) { | |
if (isset($this->parameters[$className][$matches['variable']])) { | |
$parameters[] = $this->parameters[$className][$matches['variable']]; | |
} | |
} | |
} | |
} | |
} | |
return $parameters; | |
} | |
/** | |
* @param \ReflectionClass $reflectionClass | |
* @return void | |
*/ | |
private function setterInjection(\ReflectionClass $reflectionClass) | |
{ | |
$reflectionMethods = $reflectionClass->getMethods(); | |
foreach ($reflectionMethods as $reflectionMethod) { | |
if ($reflectionMethod->isPublic() && !$reflectionMethod->isConstructor() && !$reflectionMethod->isDestructor() && substr($reflectionMethod->name, 0, 6) == "inject") { | |
$parameters = $this->parseReflectionParameters($reflectionMethod->getParameters()); | |
if (isset($parameters) && count($parameters) > 0) | |
$reflectionMethod->invokeArgs($this->instances[$reflectionClass->name], $parameters); | |
} | |
} | |
} | |
/** | |
* @param \ReflectionClass $reflectionClass | |
*/ | |
private function interfaceInjection(\ReflectionClass $reflectionClass) | |
{ | |
$reflectionInterfaces = $reflectionClass->getInterfaces(); | |
foreach ($reflectionInterfaces as $interface) { | |
if (substr($interface->name, -10) == "Dependency" || substr($interface->getShortName(), 0, 9) == 'dependsOn') { | |
$reflectionInterfaceMethods = $interface->getMethods(); | |
foreach ($reflectionInterfaceMethods as $reflectionInterfaceMethod) { | |
if ($reflectionInterfaceMethod->isAbstract()) { | |
$parameters = $this->parseReflectionParameters($reflectionInterfaceMethod->getParameters()); | |
if (isset($parameters) && count($parameters) > 0) { | |
if ($reflectionClass->hasMethod($reflectionInterfaceMethod->name)) { | |
$reflectionMethod = $reflectionClass->getMethod($reflectionInterfaceMethod->name); | |
$reflectionMethod->invokeArgs($this->instances[$reflectionClass->name], $parameters); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
/** | |
* used to register already set up objects into the di-container | |
* which is useful if those objects take care of their dependencies | |
* on their own | |
* | |
* @param $identifier | |
* @param $object | |
*/ | |
public function addInstance($identifier, $object) | |
{ | |
$this->instances[$identifier] = $object; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment