Skip to content

Instantly share code, notes, and snippets.

@dedurus
Forked from calvinfroedge/dependencyresolver.php
Created November 30, 2013 22:30
Show Gist options
  • Save dedurus/7725434 to your computer and use it in GitHub Desktop.
Save dedurus/7725434 to your computer and use it in GitHub Desktop.
<?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