Skip to content

Instantly share code, notes, and snippets.

@chani
Last active May 18, 2016 20:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chani/3ea3e4ce81274292c0da91b9f866a1cb to your computer and use it in GitHub Desktop.
Save chani/3ea3e4ce81274292c0da91b9f866a1cb to your computer and use it in GitHub Desktop.
Just another Dependency Injection Container
<?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