Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'ralphschindler/feature/di-inject-depend…
Browse files Browse the repository at this point in the history
…encies'
  • Loading branch information
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 87 deletions.
184 changes: 97 additions & 87 deletions src/Di.php
Expand Up @@ -34,8 +34,10 @@ class Di implements DependencyInjectionInterface
protected $references = array();

/**
* @param null|DefinitionList $definitions
* @param null|InstanceManager $instanceManager
* @param null|Configuration $config
* @return \Di\Di\DependencyInjector
* @return Di
*/
public function __construct(DefinitionList $definitions = null, InstanceManager $instanceManager = null, Configuration $config = null)
{
Expand All @@ -59,7 +61,7 @@ public function configure(Configuration $config)
}

/**
* @param Definition $definition
* @param DefinitionList $definition
* @return Di
*/
public function setDefinitionList(DefinitionList $definitions)
Expand Down Expand Up @@ -145,8 +147,8 @@ public function get($name, array $params = array())
*/
public function newInstance($name, array $params = array(), $isShared = true)
{
// localize dependencies (this also will serve as poka-yoke)
$definitions = $this->definitions;
// localize dependencies
$definitions = $this->definitions;
$instanceManager = $this->instanceManager();

if ($instanceManager->hasAlias($name)) {
Expand Down Expand Up @@ -195,7 +197,7 @@ public function newInstance($name, array $params = array(), $isShared = true)
$name
);
}
throw new \RuntimeException($msg);
throw new Exception\RuntimeException($msg);
}

if ($isShared) {
Expand All @@ -206,105 +208,110 @@ public function newInstance($name, array $params = array(), $isShared = true)
}
}

$this->handleInjectDependencies($instance, $class, $injectionMethods, $supertypeInjectionMethods, $params, $alias);

array_pop($this->instanceContext);
return $instance;
}

/**
* @param object $instance
* @param array $params
* @return void
*/
public function injectDependencies($instance, array $params = array())
{
$definitions = $this->definitions();
$class = get_class($instance);
$injectionMethods = ($definitions->hasClass($class)) ? $definitions->getMethods($class) : array();
$superTypeInjectionMethods = array();
$parent = $class;
while ($parent = get_parent_class($parent)) {
if ($definitions->hasClass($parent)) {
$superTypeInjectionMethods[$parent] = $definitions->getMethods($parent);
}
}
foreach (class_implements($class) as $interface) {
if ($definitions->hasClass($interface)) {
$superTypeInjectionMethods[$interface] = $definitions->getMethods($interface);
}
}
$this->handleInjectDependencies($instance, null, $injectionMethods, $superTypeInjectionMethods, $params, null);
}


protected function handleInjectDependencies($instance, $name, $injectionMethods, $supertypeInjectionMethods, $params, $alias)
{
$class = get_class($instance);

// localize dependencies
$definitions = $this->definitions;
$instanceManager = $this->instanceManager();

if ($injectionMethods || $supertypeInjectionMethods) {
foreach ($injectionMethods as $injectionMethod => $methodIsRequired) {
if ($injectionMethod !== '__construct') {
$this->handleInjectionMethodForInstance($instance, $injectionMethod, $params, $alias, $methodIsRequired);
if ($injectionMethod !== '__construct'){
$this->resolveAndCallInjectionMethodForInstance($instance, $injectionMethod, $params, $alias, $methodIsRequired, $class);
}
}
foreach ($supertypeInjectionMethods as $supertype => $supertypeInjectionMethod) {
foreach ($supertypeInjectionMethod as $injectionMethod => $methodIsRequired) {
if ($injectionMethod !== '__construct') {
$this->handleInjectionMethodForInstance($instance, $injectionMethod, $params, $alias, $methodIsRequired, $supertype);
$this->resolveAndCallInjectionMethodForInstance($instance, $injectionMethod, $params, $alias, $methodIsRequired, $supertype);
}
}
}

$instanceConfiguration = $instanceManager->getConfiguration($name);

if ($instanceConfiguration['injections']) {
$objectsToInject = $methodsToCall = array();
foreach ($instanceConfiguration['injections'] as $injectName => $injectValue) {
if (is_int($injectName) && is_string($injectValue)) {
$objectsToInject[] = $this->get($injectValue, $params);
} elseif (is_string($injectName) && is_array($injectValue)) {
if (is_string(key($injectValue))) {
$methodsToCall[] = array('method' => $injectName, 'args' => $injectValue);
} else {
foreach ($injectValue as $methodCallArgs) {
$methodsToCall[] = array('method' => $injectName, 'args' => $methodCallArgs);
if ($name) {
$instanceConfiguration = $instanceManager->getConfiguration($name);

if ($instanceConfiguration['injections']) {
$objectsToInject = $methodsToCall = array();
foreach ($instanceConfiguration['injections'] as $injectName => $injectValue) {
if (is_int($injectName) && is_string($injectValue)) {
$objectsToInject[] = $this->get($injectValue, $params);
} elseif (is_string($injectName) && is_array($injectValue)) {
if (is_string(key($injectValue))) {
$methodsToCall[] = array('method' => $injectName, 'args' => $injectValue);
} else {
foreach ($injectValue as $methodCallArgs) {
$methodsToCall[] = array('method' => $injectName, 'args' => $methodCallArgs);
}
}
} elseif (is_object($injectValue)) {
$objectsToInject[] = $injectValue;
} elseif (is_int($injectName) && is_array($injectValue)) {
throw new Exception\RuntimeException(
'An injection was provided with a keyed index and an array of data, try using'
. ' the name of a particular method as a key for your injection data.'
);
}
} elseif (is_object($injectValue)) {
$objectsToInject[] = $injectValue;
} elseif (is_int($injectName) && is_array($injectValue)) {
// @todo must find method name somehow
throw new Exception\RuntimeException(
'An injection was provided with a keyed index and an array of data, try using'
. ' the name of a particular method as a key for your injection data.'
);
}
}
if ($objectsToInject) {
foreach ($objectsToInject as $objectToInject) {
foreach ($injectionMethods as $injectionMethod => $methodIsRequired) {
$methodParams = $definitions->getMethodParameters($class, $injectionMethod);
if ($methodParams) {
foreach ($methodParams as $methodParam) {
if (get_class($objectToInject) == $methodParam[1] ||
$this->isSubclassOf(get_class($objectToInject), $methodParam[1])) {
$callParams = $this->resolveMethodParameters(get_class($instance), $injectionMethod,
array($methodParam[0] => $objectToInject), false, $alias, true
);
if ($callParams) {
call_user_func_array(array($instance, $injectionMethod), $callParams);
if ($objectsToInject) {
foreach ($objectsToInject as $objectToInject) {
foreach ($injectionMethods as $injectionMethod => $methodIsRequired) {
$methodParams = $definitions->getMethodParameters($class, $injectionMethod);
if ($methodParams) {
foreach ($methodParams as $methodParam) {
if (get_class($objectToInject) == $methodParam[1] || $this->isSubclassOf(get_class($objectToInject), $methodParam[1])) {
$this->resolveAndCallInjectionMethodForInstance($instance, $injectionMethod, array($methodParam[0] => $objectToInject), $alias, true, get_class($instance));
continue 3;
}
continue 3;
}
}
}
}
}
}
if ($methodsToCall) {
foreach ($methodsToCall as $methodInfo) {
$callParams = $this->resolveMethodParameters(get_class($instance), $methodInfo['method'],
$methodInfo['args'], false, $alias, true
);
call_user_func_array(array($instance, $methodInfo['method']), $callParams);
if ($methodsToCall) {
foreach ($methodsToCall as $methodInfo) {
$this->resolveAndCallInjectionMethodForInstance($instance, $methodInfo['method'], $methodInfo['args'], $alias, true, get_class($instance));
}
}
}
}
}



array_pop($this->instanceContext);
return $instance;
}

/**
* @todo
* @param unknown_type $object
*/
/*
public function injectObjects($targetObject, array $objects = array())
{
if ($objects === array()) {
throw new \Exception('Not yet implmeneted');
}
$targetClass = get_class($targetObject);
if (!$this->definitions()->hasClass($targetClass)) {
throw new Exception\RuntimeException('A definition for this object type cannot be found');
}
foreach ($objects as $objectToInject) {
}
}
*/

/**
* Retrieve a class instance based on class name
*
Expand All @@ -321,7 +328,7 @@ protected function createInstanceViaConstructor($class, $params, $alias = null)
{
$callParameters = array();
if ($this->definitions->hasMethod($class, '__construct')) {
$callParameters = $this->resolveMethodParameters($class, '__construct', $params, true, $alias, true);
$callParameters = $this->resolveMethodParameters($class, '__construct', $params, $alias, true, true);
}

// Hack to avoid Reflection in most common use cases
Expand Down Expand Up @@ -366,7 +373,7 @@ protected function createInstanceViaCallback($callback, $params, $alias)

$callParameters = array();
if ($this->definitions->hasMethod($class, $method)) {
$callParameters = $this->resolveMethodParameters($class, $method, $params, true, $alias, true);
$callParameters = $this->resolveMethodParameters($class, $method, $params, $alias, true, true);
}
return call_user_func_array($callback, $callParameters);
}
Expand All @@ -380,11 +387,10 @@ protected function createInstanceViaCallback($callback, $params, $alias)
* @param array $params
* @param string $alias
*/
protected function handleInjectionMethodForInstance($instance, $method, $params, $alias, $methodIsRequired, $methodClass = null)
protected function resolveAndCallInjectionMethodForInstance($instance, $method, $params, $alias, $methodIsRequired, $methodClass = null)
{
$methodClass = ($methodClass) ?: get_class($instance);
// @todo make sure to resolve the supertypes for both the object & definition
$callParameters = $this->resolveMethodParameters($methodClass, $method, $params, false, $alias, $methodIsRequired);
$callParameters = $this->resolveMethodParameters($methodClass, $method, $params, $alias, $methodIsRequired);
if ($callParameters == false) {
return;
}
Expand All @@ -403,7 +409,7 @@ protected function handleInjectionMethodForInstance($instance, $method, $params,
* @param string $alias
* @return array
*/
protected function resolveMethodParameters($class, $method, array $callTimeUserParams, $isInstantiator, $alias, $methodIsRequired)
protected function resolveMethodParameters($class, $method, array $callTimeUserParams, $alias, $methodIsRequired, $isInstantiator = false)
{
// parameters for this method, in proper order, to be returned
$resolvedParams = array();
Expand Down Expand Up @@ -433,9 +439,13 @@ protected function resolveMethodParameters($class, $method, array $callTimeUserP
}

// for the parent class, provided we are deeper than one node
list($requestedClass, $requestedAlias) = ($this->instanceContext[0][0] == 'NEW')
? array($this->instanceContext[0][1], $this->instanceContext[0][2])
: array($this->instanceContext[1][1], $this->instanceContext[1][2]);
if (isset($this->instanceContext[0])) {
list($requestedClass, $requestedAlias) = ($this->instanceContext[0][0] == 'NEW')
? array($this->instanceContext[0][1], $this->instanceContext[0][2])
: array($this->instanceContext[1][1], $this->instanceContext[1][2]);
} else {
$requestedClass = $requestedAlias = null;
}

if ($requestedClass != $class && $this->instanceManager->hasConfiguration($requestedClass)) {
$iConfig['requestedClass'] = $this->instanceManager->getConfiguration($requestedClass);
Expand Down
14 changes: 14 additions & 0 deletions test/DiTest.php
Expand Up @@ -610,4 +610,18 @@ public function testMarkingClassAsNotSharedInjectsNewInstanceIntoAllRequestersBu
$this->assertNotSame($movie->lister, $venue->lister);
$this->assertSame($movie->lister->sharedLister, $venue->lister->sharedLister);
}

public function testDiWillInjectDependenciesForInstance()
{
$di = new Di;

// for setter injection, the dependency is not required, thus it must be forced
$classDef = new Definition\ClassDefinition('ZendTest\Di\TestAsset\SetterInjection\B');
$classDef->addMethod('setA', true);
$di->definitions()->addDefinition($classDef, false); // top of stack b/c Runtime is already there

$b = new TestAsset\SetterInjection\B;
$di->injectDependencies($b);
$this->assertInstanceOf('ZendTest\Di\TestAsset\SetterInjection\A', $b->a);
}
}

0 comments on commit df6c341

Please sign in to comment.