Example #1
0
 /**
  * Resolve parameters referencing other services
  *
  * @param string $class
  * @param string $method
  * @param array $callTimeUserParams
  * @param bool $isInstantiator
  * @param string $alias
  * @return array
  * @throws Exception\CircularDependencyException
  */
 protected function resolveMethodParameters($class, $method, array $callTimeUserParams, $alias, $methodIsRequired, $isInstantiator = false)
 {
     // parameters for this method, in proper order, to be returned
     $resolvedParams = array();
     // parameter requirements from the definition
     $injectionMethodParameters = $this->definitions->getMethodParameters($class, $method);
     // computed parameters array
     $computedParams = array('value' => array(), 'required' => 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
     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);
         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 $fqParamPos => $info) {
         list($name, $type, $isRequired) = $info;
         $fqParamName = substr_replace($fqParamPos, ':' . $info[0], strrpos($fqParamPos, ':'));
         // PRIORITY 1 - consult user provided parameters
         if (isset($callTimeUserParams[$fqParamPos]) || isset($callTimeUserParams[$name])) {
             if (isset($callTimeUserParams[$fqParamPos])) {
                 $callTimeCurValue =& $callTimeUserParams[$fqParamPos];
             } elseif (isset($callTimeUserParams[$fqParamName])) {
                 $callTimeCurValue =& $callTimeUserParams[$fqParamName];
             } else {
                 $callTimeCurValue =& $callTimeUserParams[$name];
             }
             if ($type !== false && is_string($callTimeCurValue)) {
                 if ($this->instanceManager->hasAlias($callTimeCurValue)) {
                     // was an alias provided?
                     $computedParams['required'][$fqParamPos] = array($callTimeUserParams[$name], $this->instanceManager->getClassFromAlias($callTimeCurValue));
                 } elseif ($this->definitions->hasClass($callTimeUserParams[$name])) {
                     // was a known class provided?
                     $computedParams['required'][$fqParamPos] = array($callTimeCurValue, $callTimeCurValue);
                 } else {
                     // must be a value
                     $computedParams['value'][$fqParamPos] = $callTimeCurValue;
                 }
             } else {
                 // int, float, null, object, etc
                 $computedParams['value'][$fqParamPos] = $callTimeCurValue;
             }
             unset($callTimeCurValue);
             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'][$fqParamPos]) || isset($iConfig[$thisIndex]['parameters'][$fqParamName]) || isset($iConfig[$thisIndex]['parameters'][$name])) {
                 if (isset($iConfig[$thisIndex]['parameters'][$fqParamPos])) {
                     $iConfigCurValue =& $iConfig[$thisIndex]['parameters'][$fqParamPos];
                 } elseif (isset($iConfig[$thisIndex]['parameters'][$fqParamName])) {
                     $iConfigCurValue =& $iConfig[$thisIndex]['parameters'][$fqParamName];
                 } else {
                     $iConfigCurValue =& $iConfig[$thisIndex]['parameters'][$name];
                 }
                 if ($type === false && is_string($iConfigCurValue)) {
                     $computedParams['value'][$fqParamPos] = $iConfigCurValue;
                 } elseif (is_string($iConfigCurValue) && isset($aliases[$iConfigCurValue])) {
                     $computedParams['required'][$fqParamPos] = array($iConfig[$thisIndex]['parameters'][$name], $this->instanceManager->getClassFromAlias($iConfigCurValue));
                 } elseif (is_string($iConfigCurValue) && $this->definitions->hasClass($iConfigCurValue)) {
                     $computedParams['required'][$fqParamPos] = array($iConfigCurValue, $iConfigCurValue);
                 } elseif (is_object($iConfigCurValue) && $iConfigCurValue instanceof Closure && $type !== 'Closure') {
                     $computedParams['value'][$fqParamPos] = $iConfigCurValue();
                 } else {
                     $computedParams['value'][$fqParamPos] = $iConfigCurValue;
                 }
                 unset($iConfigCurValue);
                 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) {
                 if (is_object($pInstance)) {
                     $computedParams['value'][$fqParamPos] = $pInstance;
                     continue 2;
                 }
                 $pInstanceClass = $this->instanceManager->hasAlias($pInstance) ? $this->instanceManager->getClassFromAlias($pInstance) : $pInstance;
                 if ($pInstanceClass === $type || self::isSubclassOf($pInstanceClass, $type)) {
                     $computedParams['required'][$fqParamPos] = 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) {
                 if (is_object($pInstance)) {
                     $computedParams['value'][$fqParamPos] = $pInstance;
                     continue 2;
                 }
                 $pInstanceClass = $this->instanceManager->hasAlias($pInstance) ? $this->instanceManager->getClassFromAlias($pInstance) : $pInstance;
                 if ($pInstanceClass === $type || self::isSubclassOf($pInstanceClass, $type)) {
                     $computedParams['required'][$fqParamPos] = array($pInstance, $pInstanceClass);
                     continue 2;
                 }
             }
         }
         if (!$isRequired) {
             $computedParams['optional'][$fqParamPos] = true;
         }
         if ($type && $isRequired && $methodIsRequired) {
             $computedParams['required'][$fqParamPos] = array($type, $type);
         }
     }
     $index = 0;
     foreach ($injectionMethodParameters as $fqParamPos => $value) {
         $name = $value[0];
         if (isset($computedParams['value'][$fqParamPos])) {
             // if there is a value supplied, use it
             $resolvedParams[$index] = $computedParams['value'][$fqParamPos];
         } elseif (isset($computedParams['required'][$fqParamPos])) {
             // detect circular dependencies! (they can only happen in instantiators)
             if ($isInstantiator && in_array($computedParams['required'][$fqParamPos][1], $this->currentDependencies)) {
                 throw new Exception\CircularDependencyException("Circular dependency detected: {$class} depends on {$value[1]} and viceversa");
             }
             array_push($this->currentDependencies, $class);
             $dConfig = $this->instanceManager->getConfiguration($computedParams['required'][$fqParamPos][0]);
             if ($dConfig['shared'] === false) {
                 $resolvedParams[$index] = $this->newInstance($computedParams['required'][$fqParamPos][0], $callTimeUserParams, false);
             } else {
                 $resolvedParams[$index] = $this->get($computedParams['required'][$fqParamPos][0], $callTimeUserParams);
             }
             array_pop($this->currentDependencies);
         } elseif (!array_key_exists($fqParamPos, $computedParams['optional'])) {
             if ($methodIsRequired) {
                 // if this item was not marked as optional,
                 // plus it cannot be resolve, and no value exist, bail out
                 throw new Exception\MissingPropertyException(sprintf('Missing %s for parameter ' . $name . ' for ' . $class . '::' . $method, $value[0] === null ? 'value' : 'instance/object'));
             } else {
                 return false;
             }
         } else {
             $resolvedParams[$index] = null;
         }
         $index++;
     }
     return $resolvedParams;
     // return ordered list of parameters
 }