/** * Resolve parameters referencing other services * * @param array $params * @return array */ protected function resolveMethodParameters($class, $method, array $userParams, $isInstantiator, $alias) { $resolvedParams = array(); $injectionMethodParameters = $this->definition->getInjectionMethodParameters($class, $method); $computedValueParams = array(); $computedLookupParams = array(); foreach ($injectionMethodParameters as $name => $type) { //$computedValueParams[$name] = null; // first consult user provided parameters if (isset($userParams[$name])) { if (is_string($userParams[$name])) { if ($this->instanceManager->hasAlias($userParams[$name])) { $computedLookupParams[$name] = array($userParams[$name], $this->instanceManager->getClassFromAlias($userParams[$name])); } elseif ($this->definition->hasClass($userParams[$name])) { $computedLookupParams[$name] = array($userParams[$name], $userParams[$name]); } else { $computedValueParams[$name] = $userParams[$name]; } } else { $computedValueParams[$name] = $userParams[$name]; } continue; } // next consult alias specific properties if ($alias && $this->instanceManager->hasProperty($alias, $name)) { $computedValueParams[$name] = $this->instanceManager->getProperty($alias, $name); continue; } // next consult alias level preferred instances if ($alias && $this->instanceManager->hasPreferredInstances($alias)) { $pInstances = $this->instanceManager->getPreferredInstances($alias); foreach ($pInstances as $pInstance) { $pInstanceClass = ($this->instanceManager->hasAlias($pInstance)) ? $this->instanceManager->getClassFromAlias($pInstance) : $pInstance; if ($pInstanceClass === $type || is_subclass_of($pInstanceClass, $type)) { $computedLookupParams[$name] = array($pInstance, $pInstanceClass); continue; } } } // next consult class level preferred instances if ($type && $this->instanceManager->hasPreferredInstances($type)) { $pInstances = $this->instanceManager->getPreferredInstances($type); foreach ($pInstances as $pInstance) { $pInstanceClass = ($this->instanceManager->hasAlias($pInstance)) ? $this->instanceManager->getClassFromAlias($pInstance) : $pInstance; if ($pInstanceClass === $type || is_subclass_of($pInstanceClass, $type)) { $computedLookupParams[$name] = array($pInstance, $pInstanceClass); continue; } } } // finally consult alias specific properties if ($this->instanceManager->hasProperty($class, $name)) { $computedValueParams[$name] = $this->instanceManager->getProperty($class, $name); continue; } if ($type) { $computedLookupParams[$name] = array($type, $type); } } $index = 0; foreach ($injectionMethodParameters as $name => $value) { if (isset($computedValueParams[$name])) { $resolvedParams[$index] = $computedValueParams[$name]; } elseif (isset($computedLookupParams[$name])) { if ($isInstantiator && in_array($computedLookupParams[$name][1], $this->currentDependencies)) { throw new Exception\CircularDependencyException("Circular dependency detected: $class depends on $value and viceversa"); } array_push($this->currentDependencies, $class); $resolvedParams[$index] = $this->get($computedLookupParams[$name][0], $userParams); array_pop($this->currentDependencies); } else { throw new Exception\MissingPropertyException('Missing parameter named ' . $name . ' for ' . $class . '::' . $method); } $index++; } return $resolvedParams; }
/** * Resolve parameters referencing other services * * @param array $params * @return array */ protected function resolveMethodParameters($class, $method, array $callTimeUserParams, $isInstantiator, $alias) { static $isSubclassFunc = null; static $isSubclassFuncCache = null; $isSubclassFunc = function($class, $type) use (&$isSubclassFuncCache) { /* @see https://bugs.php.net/bug.php?id=53727 */ if ($isSubclassFuncCache === null) { $isSubclassFuncCache = array(); } if (!array_key_exists($class, $isSubclassFuncCache)) { $isSubclassFuncCache[$class] = class_parents($class, true) + class_implements($class, true); } return (isset($isSubclassFuncCache[$class][$type])); }; // parameters for this method, in proper order, to be returned $resolvedParams = array(); // parameter requirements from the definition $injectionMethodParameters = $this->definition->getInjectionMethodParameters($class, $method); // computed parameters array $computedParams = array( 'value' => array(), 'lookup' => array(), 'optional' => array() ); // retrieve instance configurations for all contexts $iConfig = array(); $aliases = $this->instanceManager->getAliases(); // for the alias in the dependency tree if ($alias && $this->instanceManager->hasConfiguration($alias)) { $iConfig['thisAlias'] = $this->instanceManager->getConfiguration($alias); } // for the current class in the dependency tree if ($this->instanceManager->hasConfiguration($class)) { $iConfig['thisClass'] = $this->instanceManager->getConfiguration($class); } // 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 ($requestedClass != $class && $this->instanceManager->hasConfiguration($requestedClass)) { $iConfig['requestedClass'] = $this->instanceManager->getConfiguration($requestedClass); if ($requestedAlias) { $iConfig['requestedAlias'] = $this->instanceManager->getConfiguration($requestedAlias); } } // This is a 2 pass system for resolving parameters // first pass will find the sources, the second pass will order them and resolve lookups if they exist // MOST methods will only have a single parameters to resolve, so this should be fast foreach ($injectionMethodParameters as $name => $info) { list($type, $isOptional, $isTypeInstantiable) = $info; // PRIORITY 1 - consult user provided parameters if (isset($callTimeUserParams[$name])) { if (is_string($callTimeUserParams[$name])) { if ($this->instanceManager->hasAlias($callTimeUserParams[$name])) { // was an alias provided? $computedParams['lookup'][$name] = array( $callTimeUserParams[$name], $this->instanceManager->getClassFromAlias($callTimeUserParams[$name]) ); } elseif ($this->definition->hasClass($callTimeUserParams[$name])) { // was a known class provided? $computedParams['lookup'][$name] = array( $callTimeUserParams[$name], $callTimeUserParams[$name] ); } else { // must be a value $computedParams['value'][$name] = $callTimeUserParams[$name]; } } else { // int, float, null, object, etc $computedParams['value'][$name] = $callTimeUserParams[$name]; } continue; } // PRIORITY 2 -specific instance configuration (thisAlias) - this alias // PRIORITY 3 -THEN specific instance configuration (thisClass) - this class // PRIORITY 4 -THEN specific instance configuration (requestedAlias) - requested alias // PRIORITY 5 -THEN specific instance configuration (requestedClass) - requested class foreach (array('thisAlias', 'thisClass', 'requestedAlias', 'requestedClass') as $thisIndex) { // check the provided parameters config if (isset($iConfig[$thisIndex]['parameters'][$name])) { if (is_string($iConfig[$thisIndex]['parameters'][$name]) && isset($aliases[$iConfig[$thisIndex]['parameters'][$name]])) { $computedParams['lookup'][$name] = array( $iConfig[$thisIndex]['parameters'][$name], $this->instanceManager->getClassFromAlias($iConfig[$thisIndex]['parameters'][$name]) ); } elseif (is_string($iConfig[$thisIndex]['parameters'][$name]) && $this->definition->hasClass($iConfig[$thisIndex]['parameters'][$name])) { $computedParams['lookup'][$name] = array( $iConfig[$thisIndex]['parameters'][$name], $iConfig[$thisIndex]['parameters'][$name] ); } else { $computedParams['value'][$name] = $iConfig[$thisIndex]['parameters'][$name]; } continue 2; } // check the provided method config if (isset($iConfig[$thisIndex]['methods'][$method][$name])) { if (is_string(is_string($iConfig[$thisIndex]['methods'][$method][$name])) && isset($aliases[$iConfig[$thisIndex]['methods'][$method][$name]])) { $computedParams['lookup'][$name] = array( $iConfig[$thisIndex]['methods'][$method][$name], $this->instanceManager->getClassFromAlias($iConfig[$thisIndex]['methods'][$method][$name]) ); } elseif (is_string(is_string($iConfig[$thisIndex]['methods'][$method][$name])) && $this->definition->hasClass($iConfig[$thisIndex]['methods'][$method][$name])) { $computedParams['lookup'][$name] = array( $iConfig[$thisIndex]['methods'][$method][$name], $iConfig[$thisIndex]['methods'][$method][$name] ); } else { $computedParams['value'][$name] = $iConfig[$thisIndex]['methods'][$method][$name]; } continue 2; } } // PRIORITY 6 - globally preferred implementations // next consult alias level preferred instances if ($alias && $this->instanceManager->hasTypePreferences($alias)) { $pInstances = $this->instanceManager->getTypePreferences($alias); foreach ($pInstances as $pInstance) { $pInstanceClass = ($this->instanceManager->hasAlias($pInstance)) ? $this->instanceManager->getClassFromAlias($pInstance) : $pInstance; if ($pInstanceClass === $type || $isSubclassFunc($pInstanceClass, $type)) { $computedParams['lookup'][$name] = array($pInstance, $pInstanceClass); continue 2; } } } // next consult class level preferred instances if ($type && $this->instanceManager->hasTypePreferences($type)) { $pInstances = $this->instanceManager->getTypePreferences($type); foreach ($pInstances as $pInstance) { $pInstanceClass = ($this->instanceManager->hasAlias($pInstance)) ? $this->instanceManager->getClassFromAlias($pInstance) : $pInstance; if ($pInstanceClass === $type || $isSubclassFunc($pInstanceClass, $type)) { $computedParams['lookup'][$name] = array($pInstance, $pInstanceClass); continue 2; } } } if ($isOptional) { $computedParams['optional'][$name] = true; } if ($type && $isTypeInstantiable === true && !$isOptional) { $computedParams['lookup'][$name] = array($type, $type); } } $index = 0; foreach ($injectionMethodParameters as $name => $value) { if (isset($computedParams['value'][$name])) { $resolvedParams[$index] = $computedParams['value'][$name]; } elseif (isset($computedParams['lookup'][$name])) { if ($isInstantiator && in_array($computedParams['lookup'][$name][1], $this->currentDependencies)) { throw new Exception\CircularDependencyException( "Circular dependency detected: $class depends on {$value[0]} and viceversa" ); } array_push($this->currentDependencies, $class); $resolvedParams[$index] = $this->get($computedParams['lookup'][$name][0], $callTimeUserParams); array_pop($this->currentDependencies); } elseif (!array_key_exists($name, $computedParams['optional'])) { throw new Exception\MissingPropertyException( 'Missing parameter named ' . $name . ' for ' . $class . '::' . $method ); } else { $resolvedParams[$index] = null; } $index++; } return $resolvedParams; }