-
-
Save dedurus/7725434 to your computer and use it in GitHub Desktop.
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 | |
/* | |
* Dependency injection can help you reduce coupling and increase testability, making your applications more maintainable. | |
* | |
* DependencyResolver gives you the best of both worlds by mixing a registry with call time dependency resolution. In addition, it provides significant resource | |
* usage advantages over other patterns, such as an object pool, because dependencies are only resolved when they are actually needed. | |
* | |
* It supports both constructor injection for class wide dependencies, as well as method injection for method specific dependencies. | |
* | |
* Here's a quick example: | |
* | |
* use DependencyResolver as DR; | |
* $class = '\Namespace\To\Class'; | |
* DR::register($class); | |
* DR::setClassDependencies($class, ['\Second\Class', '\Third\Class']); | |
* DR::setMethodDependencies($class, 'foo', ['\Fourth\Class']); | |
* | |
* Now, when you create an instance of $class using the DependencyResolver, all of your dependencies are passed to the constructor function as instantiated objects... | |
* | |
* $instance = DR::instantiate($class); | |
* | |
* This is the equivalent of: | |
* | |
* $instance = new $class(new \SecondClass, new \ThirdClass); | |
* | |
* Dependencies are similarly resolved at run time when you call a method of the class: | |
* | |
* $optionalArgumentsArray = ['bar', 'baz']; | |
* | |
* DR::call($instance, 'foo', $optionalArgumentsArray); | |
* | |
* The arguments you pass are merged with the dependencies for the method, and the dependencies are prepended after the arguments. This is the equivalent of: | |
* | |
* $instance->foo('bar', 'baz', new \Fourth\Class); | |
* | |
*/ | |
class DependencyResolver { | |
private static $_registry; | |
public static function register($class){ | |
if(self::$_registry[$class]){ | |
throw new \Exception("$class has already been registered with the DependencyResolver!"); | |
} | |
self::$_registry[$class] = []; | |
} | |
/* | |
* Sets dependencies for an entire class. These will use constructor injection. | |
*/ | |
public static function setClassDependencies($class, $dependencies){ | |
foreach($dependencies as $d){ | |
self::_setClassDependency($class, $d); | |
} | |
} | |
/* | |
* Sets a dependency for a class, to be injected with constructor injection. | |
*/ | |
private static function _setClassDependency($class, $dependency){ | |
if(isset(self::$_registry[$class][$dependency])){ | |
throw new \Exception("$dependency has already been registered with DependencyResolver::$class!"); | |
} | |
self::$_registry[$class][$dependency] = true; | |
} | |
/* | |
* Sets a dependency for a particular method of a class, which will be injected as an argument to the method | |
*/ | |
public static function setMethodDependencies($class, $method, $dependencies){ | |
if(!isset(self::$_registry[$class])){ | |
throw new \Exception("$class has not been registered with the DependencyResolver."); | |
} | |
if(isset(self::$_registry[$class]['methods'][$method])){ | |
throw new \Exception("$method method has already been registered with DependencyResolver::$class!"); | |
} | |
foreach($dependencies as $d){ | |
self::_setMethodDependency($class, $method, $d); | |
} | |
} | |
/* | |
* Sets the dependency to inject when the class method is called. | |
*/ | |
private static function _setMethodDependency($class, $method, $dependency){ | |
if(isset(self::$_registry[$class]['methods'][$method])){ | |
throw new \Exception("$dependency has already been registered with DependencyResolver::$class!"); | |
} | |
self::register($dependency); | |
self::$_registry[$class]['methods'][$method][$dependency] = true; | |
} | |
/* | |
* Creates a new instance of a class registered with the resolver | |
*/ | |
public static function instantiate($class){ | |
if(!isset(self::$_registry[$class])){ | |
throw new \Exception("$class is not known to the DependencyResolver"); | |
} | |
if(isset(self::$_registry[$class])){ | |
$dependencies = self::_resolve($class); | |
if(empty($dependencies)){ | |
$instance = new $class; | |
} else { | |
$reflect = new \ReflectionClass($class); | |
$instance = $reflect->newInstanceArgs($dependencies); | |
} | |
return $instance; | |
} else { | |
throw new \Exception("$class has not been registered with the DependencyResolver."); | |
} | |
} | |
/* | |
* Resolve a method's dependencies and call it | |
* | |
* @param $class | |
* @param $method | |
* @param $args Arguments that should be given before injectables | |
*/ | |
public static function call($instance, $method, $args = []){ | |
$class = '\\'.get_class($instance); | |
if(!isset(self::$_registry[$class])){ | |
throw new \Exception("$class does not exist in DependencyResolver!"); | |
} | |
$dependencies = self::_resolveMethod($class, $method); | |
return call_user_func_array(array($instance, $method), array_merge($args, $dependencies)); | |
} | |
/* | |
* Resolve dependencies for a given class | |
*/ | |
private static function _resolve($class){ | |
$resolved = []; | |
foreach(self::$_registry[$class] as $d=>$i){ | |
if($d == 'methods') continue; | |
$resolved[] = self::instantiate($d); | |
} | |
return $resolved; | |
} | |
/* | |
* Resolve dependencies for a given method | |
*/ | |
private static function _resolveMethod($class, $method){ | |
$resolved = []; | |
if(isset(self::$_registry[$class]['methods'][$method])){ | |
foreach(self::$_registry[$class]['methods'][$method] as $d=>$i){ | |
$resolved[] = self::instantiate($d); | |
} | |
} | |
return $resolved; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment