PageRenderTime 58ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Di/Di.php

https://github.com/praveenuniyal/zf2
PHP | 872 lines | 529 code | 93 blank | 250 comment | 116 complexity | 70788d04c7165fc3edc1b170ccc55f83 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Di;
  10. use Closure;
  11. use ReflectionClass;
  12. use Zend\Di\Exception\RuntimeException as DiRuntimeException;
  13. use Zend\ServiceManager\Exception\ExceptionInterface as ServiceManagerException;
  14. /**
  15. * Dependency injector that can generate instances using class definitions and configured instance parameters
  16. */
  17. class Di implements DependencyInjectionInterface
  18. {
  19. /**
  20. * @var DefinitionList
  21. */
  22. protected $definitions = null;
  23. /**
  24. * @var InstanceManager
  25. */
  26. protected $instanceManager = null;
  27. /**
  28. * @var string
  29. */
  30. protected $instanceContext = array();
  31. /**
  32. * All the class dependencies [source][dependency]
  33. *
  34. * @var array
  35. */
  36. protected $currentDependencies = array();
  37. /**
  38. * All the class references [dependency][source]
  39. *
  40. * @var array
  41. */
  42. protected $references = array();
  43. /**
  44. * Resolve method policy
  45. *
  46. * EAGER: explore type preference or go through
  47. */
  48. const RESOLVE_EAGER = 1;
  49. /**
  50. * Resolve method policy
  51. *
  52. * STRICT: explore type preference or throw exception
  53. */
  54. const RESOLVE_STRICT = 2;
  55. /**
  56. *
  57. * use only specified parameters
  58. */
  59. const METHOD_IS_OPTIONAL = 0;
  60. /**
  61. *
  62. * resolve mode RESOLVE_EAGER
  63. */
  64. const METHOD_IS_AWARE = 1;
  65. /**
  66. *
  67. * resolve mode RESOLVE_EAGER | RESOLVE_STRICT
  68. */
  69. const METHOD_IS_CONSTRUCTOR = 3;
  70. /**
  71. *
  72. * resolve mode RESOLVE_EAGER | RESOLVE_STRICT
  73. */
  74. const METHOD_IS_INSTANTIATOR = 3;
  75. /**
  76. *
  77. * resolve mode RESOLVE_EAGER | RESOLVE_STRICT
  78. */
  79. const METHOD_IS_REQUIRED = 3;
  80. /**
  81. *
  82. * resolve mode RESOLVE_EAGER
  83. */
  84. const METHOD_IS_EAGER = 1;
  85. /**
  86. * Constructor
  87. *
  88. * @param null|DefinitionList $definitions
  89. * @param null|InstanceManager $instanceManager
  90. * @param null|Config $config
  91. */
  92. public function __construct(DefinitionList $definitions = null, InstanceManager $instanceManager = null, Config $config = null)
  93. {
  94. $this->definitions = ($definitions) ?: new DefinitionList(new Definition\RuntimeDefinition());
  95. $this->instanceManager = ($instanceManager) ?: new InstanceManager();
  96. if ($config) {
  97. $this->configure($config);
  98. }
  99. }
  100. /**
  101. * Provide a configuration object to configure this instance
  102. *
  103. * @param Config $config
  104. * @return void
  105. */
  106. public function configure(Config $config)
  107. {
  108. $config->configure($this);
  109. }
  110. /**
  111. * @param DefinitionList $definitions
  112. * @return self
  113. */
  114. public function setDefinitionList(DefinitionList $definitions)
  115. {
  116. $this->definitions = $definitions;
  117. return $this;
  118. }
  119. /**
  120. * @return DefinitionList
  121. */
  122. public function definitions()
  123. {
  124. return $this->definitions;
  125. }
  126. /**
  127. * Set the instance manager
  128. *
  129. * @param InstanceManager $instanceManager
  130. * @return Di
  131. */
  132. public function setInstanceManager(InstanceManager $instanceManager)
  133. {
  134. $this->instanceManager = $instanceManager;
  135. return $this;
  136. }
  137. /**
  138. *
  139. * @return InstanceManager
  140. */
  141. public function instanceManager()
  142. {
  143. return $this->instanceManager;
  144. }
  145. /**
  146. * @param $name
  147. * @param array $params
  148. * @param string $method
  149. * @return array
  150. */
  151. protected function getCallParameters($name, array $params, $method = "__construct")
  152. {
  153. $im = $this->instanceManager;
  154. $class = $im->hasAlias($name) ? $im->getClassFromAlias($name) : $name;
  155. if ($this->definitions->hasClass($class)) {
  156. $callParameters = array();
  157. if ($this->definitions->hasMethod($class, $method)) {
  158. foreach ($this->definitions->getMethodParameters($class, $method) as $param) {
  159. if (isset($params[$param[0]])) {
  160. $callParameters[$param[0]] = $params[$param[0]];
  161. }
  162. }
  163. }
  164. return $callParameters;
  165. }
  166. return $params;
  167. }
  168. /**
  169. * Lazy-load a class
  170. *
  171. * Attempts to load the class (or service alias) provided. If it has been
  172. * loaded before, the previous instance will be returned (unless the service
  173. * definition indicates shared instances should not be used).
  174. *
  175. * @param string $name Class name or service alias
  176. * @param null|array $params Parameters to pass to the constructor
  177. * @return object|null
  178. */
  179. public function get($name, array $params = array())
  180. {
  181. array_push($this->instanceContext, array('GET', $name, null));
  182. $im = $this->instanceManager;
  183. $callParameters = $this->getCallParameters($name, $params);
  184. if ($callParameters) {
  185. $fastHash = $im->hasSharedInstanceWithParameters($name, $callParameters, true);
  186. if ($fastHash) {
  187. array_pop($this->instanceContext);
  188. return $im->getSharedInstanceWithParameters(null, array(), $fastHash);
  189. }
  190. }
  191. if ($im->hasSharedInstance($name, $callParameters)) {
  192. array_pop($this->instanceContext);
  193. return $im->getSharedInstance($name, $callParameters);
  194. }
  195. $config = $im->getConfig($name);
  196. $instance = $this->newInstance($name, $params, $config['shared']);
  197. array_pop($this->instanceContext);
  198. return $instance;
  199. }
  200. /**
  201. * Retrieve a new instance of a class
  202. *
  203. * Forces retrieval of a discrete instance of the given class, using the
  204. * constructor parameters provided.
  205. *
  206. * @param mixed $name Class name or service alias
  207. * @param array $params Parameters to pass to the constructor
  208. * @param bool $isShared
  209. * @return object|null
  210. * @throws Exception\ClassNotFoundException
  211. * @throws Exception\RuntimeException
  212. */
  213. public function newInstance($name, array $params = array(), $isShared = true)
  214. {
  215. // localize dependencies
  216. $definitions = $this->definitions;
  217. $instanceManager = $this->instanceManager();
  218. if ($instanceManager->hasAlias($name)) {
  219. $class = $instanceManager->getClassFromAlias($name);
  220. $alias = $name;
  221. } else {
  222. $class = $name;
  223. $alias = null;
  224. }
  225. array_push($this->instanceContext, array('NEW', $class, $alias));
  226. if (!$definitions->hasClass($class)) {
  227. $aliasMsg = ($alias) ? '(specified by alias ' . $alias . ') ' : '';
  228. throw new Exception\ClassNotFoundException(
  229. 'Class ' . $aliasMsg . $class . ' could not be located in provided definitions.'
  230. );
  231. }
  232. $instantiator = $definitions->getInstantiator($class);
  233. $injectionMethods = array();
  234. $injectionMethods[$class] = $definitions->getMethods($class);
  235. foreach ($definitions->getClassSupertypes($class) as $supertype) {
  236. $injectionMethods[$supertype] = $definitions->getMethods($supertype);
  237. }
  238. if ($instantiator === '__construct') {
  239. $instance = $this->createInstanceViaConstructor($class, $params, $alias);
  240. if (array_key_exists('__construct', $injectionMethods)) {
  241. unset($injectionMethods['__construct']);
  242. }
  243. } elseif (is_callable($instantiator, false)) {
  244. $instance = $this->createInstanceViaCallback($instantiator, $params, $alias);
  245. } else {
  246. if (is_array($instantiator)) {
  247. $msg = sprintf(
  248. 'Invalid instantiator: %s::%s() is not callable.',
  249. isset($instantiator[0]) ? $instantiator[0] : 'NoClassGiven',
  250. isset($instantiator[1]) ? $instantiator[1] : 'NoMethodGiven'
  251. );
  252. } else {
  253. $msg = sprintf(
  254. 'Invalid instantiator of type "%s" for "%s".',
  255. gettype($instantiator),
  256. $name
  257. );
  258. }
  259. throw new Exception\RuntimeException($msg);
  260. }
  261. if ($isShared) {
  262. if ($callParameters = $this->getCallParameters($name, $params)) {
  263. $this->instanceManager->addSharedInstanceWithParameters($instance, $name, $callParameters);
  264. } else {
  265. $this->instanceManager->addSharedInstance($instance, $name);
  266. }
  267. }
  268. $this->handleInjectDependencies($instance, $injectionMethods, $params, $class, $alias, $name);
  269. array_pop($this->instanceContext);
  270. return $instance;
  271. }
  272. /**
  273. * Inject dependencies
  274. *
  275. * @param object $instance
  276. * @param array $params
  277. * @return void
  278. */
  279. public function injectDependencies($instance, array $params = array())
  280. {
  281. $definitions = $this->definitions();
  282. $class = $this->getClass($instance);
  283. $injectionMethods = array(
  284. $class => ($definitions->hasClass($class)) ? $definitions->getMethods($class) : array()
  285. );
  286. $parent = $class;
  287. while ($parent = get_parent_class($parent)) {
  288. if ($definitions->hasClass($parent)) {
  289. $injectionMethods[$parent] = $definitions->getMethods($parent);
  290. }
  291. }
  292. foreach (class_implements($class) as $interface) {
  293. if ($definitions->hasClass($interface)) {
  294. $injectionMethods[$interface] = $definitions->getMethods($interface);
  295. }
  296. }
  297. $this->handleInjectDependencies($instance, $injectionMethods, $params, $class, null, null);
  298. }
  299. /**
  300. * @param object $instance
  301. * @param array $injectionMethods
  302. * @param array $params
  303. * @param string|null $instanceClass
  304. * @param string|null$instanceAlias
  305. * @param string $requestedName
  306. * @throws Exception\RuntimeException
  307. */
  308. protected function handleInjectDependencies($instance, $injectionMethods, $params, $instanceClass, $instanceAlias, $requestedName)
  309. {
  310. // localize dependencies
  311. $definitions = $this->definitions;
  312. $instanceManager = $this->instanceManager();
  313. $calledMethods = array('__construct' => true);
  314. if ($injectionMethods) {
  315. foreach ($injectionMethods as $type => $typeInjectionMethods) {
  316. foreach ($typeInjectionMethods as $typeInjectionMethod => $methodRequirementType) {
  317. if (!isset($calledMethods[$typeInjectionMethod])) {
  318. if ($this->resolveAndCallInjectionMethodForInstance($instance, $typeInjectionMethod, $params, $instanceAlias, $methodRequirementType, $type)) {
  319. $calledMethods[$typeInjectionMethod] = true;
  320. }
  321. }
  322. }
  323. }
  324. if ($requestedName) {
  325. $instanceConfig = $instanceManager->getConfig($requestedName);
  326. if ($instanceConfig['injections']) {
  327. $objectsToInject = $methodsToCall = array();
  328. foreach ($instanceConfig['injections'] as $injectName => $injectValue) {
  329. if (is_int($injectName) && is_string($injectValue)) {
  330. $objectsToInject[] = $this->get($injectValue, $params);
  331. } elseif (is_string($injectName) && is_array($injectValue)) {
  332. if (is_string(key($injectValue))) {
  333. $methodsToCall[] = array('method' => $injectName, 'args' => $injectValue);
  334. } else {
  335. foreach ($injectValue as $methodCallArgs) {
  336. $methodsToCall[] = array('method' => $injectName, 'args' => $methodCallArgs);
  337. }
  338. }
  339. } elseif (is_object($injectValue)) {
  340. $objectsToInject[] = $injectValue;
  341. } elseif (is_int($injectName) && is_array($injectValue)) {
  342. throw new Exception\RuntimeException(
  343. 'An injection was provided with a keyed index and an array of data, try using'
  344. . ' the name of a particular method as a key for your injection data.'
  345. );
  346. }
  347. }
  348. if ($objectsToInject) {
  349. foreach ($objectsToInject as $objectToInject) {
  350. $calledMethods = array('__construct' => true);
  351. foreach ($injectionMethods as $type => $typeInjectionMethods) {
  352. foreach ($typeInjectionMethods as $typeInjectionMethod => $methodRequirementType) {
  353. if (!isset($calledMethods[$typeInjectionMethod])) {
  354. $methodParams = $definitions->getMethodParameters($type, $typeInjectionMethod);
  355. if ($methodParams) {
  356. foreach ($methodParams as $methodParam) {
  357. $objectToInjectClass = $this->getClass($objectToInject);
  358. if ($objectToInjectClass == $methodParam[1] || self::isSubclassOf($objectToInjectClass, $methodParam[1])) {
  359. if ($this->resolveAndCallInjectionMethodForInstance($instance, $typeInjectionMethod, array($methodParam[0] => $objectToInject), $instanceAlias, self::METHOD_IS_REQUIRED, $type)) {
  360. $calledMethods[$typeInjectionMethod] = true;
  361. }
  362. continue 3;
  363. }
  364. }
  365. }
  366. }
  367. }
  368. }
  369. }
  370. }
  371. if ($methodsToCall) {
  372. foreach ($methodsToCall as $methodInfo) {
  373. $this->resolveAndCallInjectionMethodForInstance($instance, $methodInfo['method'], $methodInfo['args'], $instanceAlias, self::METHOD_IS_REQUIRED, $instanceClass);
  374. }
  375. }
  376. }
  377. }
  378. }
  379. }
  380. /**
  381. * Retrieve a class instance based on class name
  382. *
  383. * Any parameters provided will be used as constructor arguments. If any
  384. * given parameter is a DependencyReference object, it will be fetched
  385. * from the container so that the instance may be injected.
  386. *
  387. * @param string $class
  388. * @param array $params
  389. * @param string|null $alias
  390. * @return object
  391. */
  392. protected function createInstanceViaConstructor($class, $params, $alias = null)
  393. {
  394. $callParameters = array();
  395. if ($this->definitions->hasMethod($class, '__construct')) {
  396. $callParameters = $this->resolveMethodParameters($class, '__construct', $params, $alias, self::METHOD_IS_CONSTRUCTOR, true);
  397. }
  398. if (!class_exists($class)) {
  399. if (interface_exists($class)) {
  400. throw new Exception\ClassNotFoundException(sprintf(
  401. 'Cannot instantiate interface "%s"',
  402. $class
  403. ));
  404. }
  405. throw new Exception\ClassNotFoundException(sprintf(
  406. 'Class "%s" does not exist; cannot instantiate',
  407. $class
  408. ));
  409. }
  410. // Hack to avoid Reflection in most common use cases
  411. switch (count($callParameters)) {
  412. case 0:
  413. return new $class();
  414. case 1:
  415. return new $class($callParameters[0]);
  416. case 2:
  417. return new $class($callParameters[0], $callParameters[1]);
  418. case 3:
  419. return new $class($callParameters[0], $callParameters[1], $callParameters[2]);
  420. default:
  421. $r = new \ReflectionClass($class);
  422. return $r->newInstanceArgs($callParameters);
  423. }
  424. }
  425. /**
  426. * Get an object instance from the defined callback
  427. *
  428. * @param callable $callback
  429. * @param array $params
  430. * @param string $alias
  431. * @return object
  432. * @throws Exception\InvalidCallbackException
  433. * @throws Exception\RuntimeException
  434. */
  435. protected function createInstanceViaCallback($callback, $params, $alias)
  436. {
  437. if (!is_callable($callback)) {
  438. throw new Exception\InvalidCallbackException('An invalid constructor callback was provided');
  439. }
  440. if (is_array($callback)) {
  441. $class = (is_object($callback[0])) ? $this->getClass($callback[0]) : $callback[0];
  442. $method = $callback[1];
  443. } elseif (is_string($callback) && strpos($callback, '::') !== false) {
  444. list($class, $method) = explode('::', $callback, 2);
  445. } else {
  446. throw new Exception\RuntimeException('Invalid callback type provided to ' . __METHOD__);
  447. }
  448. $callParameters = array();
  449. if ($this->definitions->hasMethod($class, $method)) {
  450. $callParameters = $this->resolveMethodParameters($class, $method, $params, $alias, self::METHOD_IS_INSTANTIATOR, true);
  451. }
  452. return call_user_func_array($callback, $callParameters);
  453. }
  454. /**
  455. * This parameter will handle any injection methods and resolution of
  456. * dependencies for such methods
  457. *
  458. * @param object $instance
  459. * @param string $method
  460. * @param array $params
  461. * @param string $alias
  462. * @param bool $methodRequirementType
  463. * @param string|null $methodClass
  464. * @return bool
  465. */
  466. protected function resolveAndCallInjectionMethodForInstance($instance, $method, $params, $alias, $methodRequirementType, $methodClass = null)
  467. {
  468. $methodClass = ($methodClass) ?: $this->getClass($instance);
  469. $callParameters = $this->resolveMethodParameters($methodClass, $method, $params, $alias, $methodRequirementType);
  470. if ($callParameters == false) {
  471. return false;
  472. }
  473. if ($callParameters !== array_fill(0, count($callParameters), null)) {
  474. call_user_func_array(array($instance, $method), $callParameters);
  475. return true;
  476. }
  477. return false;
  478. }
  479. /**
  480. * Resolve parameters referencing other services
  481. *
  482. * @param string $class
  483. * @param string $method
  484. * @param array $callTimeUserParams
  485. * @param string $alias
  486. * @param int|bolean $methodRequirementType
  487. * @param bool $isInstantiator
  488. * @throws Exception\MissingPropertyException
  489. * @throws Exception\CircularDependencyException
  490. * @return array
  491. */
  492. protected function resolveMethodParameters($class, $method, array $callTimeUserParams, $alias, $methodRequirementType, $isInstantiator = false)
  493. {
  494. //for BC
  495. if (is_bool($methodRequirementType)) {
  496. if ($isInstantiator) {
  497. $methodRequirementType = Di::METHOD_IS_INSTANTIATOR;
  498. } elseif ($methodRequirementType) {
  499. $methodRequirementType = Di::METHOD_IS_REQUIRED;
  500. } else {
  501. $methodRequirementType = Di::METHOD_IS_OPTIONAL;
  502. }
  503. }
  504. // parameters for this method, in proper order, to be returned
  505. $resolvedParams = array();
  506. // parameter requirements from the definition
  507. $injectionMethodParameters = $this->definitions->getMethodParameters($class, $method);
  508. // computed parameters array
  509. $computedParams = array(
  510. 'value' => array(),
  511. 'retrieval' => array(),
  512. 'optional' => array()
  513. );
  514. // retrieve instance configurations for all contexts
  515. $iConfig = array();
  516. $aliases = $this->instanceManager->getAliases();
  517. // for the alias in the dependency tree
  518. if ($alias && $this->instanceManager->hasConfig($alias)) {
  519. $iConfig['thisAlias'] = $this->instanceManager->getConfig($alias);
  520. }
  521. // for the current class in the dependency tree
  522. if ($this->instanceManager->hasConfig($class)) {
  523. $iConfig['thisClass'] = $this->instanceManager->getConfig($class);
  524. }
  525. // for the parent class, provided we are deeper than one node
  526. if (isset($this->instanceContext[0])) {
  527. list($requestedClass, $requestedAlias) = ($this->instanceContext[0][0] == 'NEW')
  528. ? array($this->instanceContext[0][1], $this->instanceContext[0][2])
  529. : array($this->instanceContext[1][1], $this->instanceContext[1][2]);
  530. } else {
  531. $requestedClass = $requestedAlias = null;
  532. }
  533. if ($requestedClass != $class && $this->instanceManager->hasConfig($requestedClass)) {
  534. $iConfig['requestedClass'] = $this->instanceManager->getConfig($requestedClass);
  535. if ($requestedAlias) {
  536. $iConfig['requestedAlias'] = $this->instanceManager->getConfig($requestedAlias);
  537. }
  538. }
  539. // This is a 2 pass system for resolving parameters
  540. // first pass will find the sources, the second pass will order them and resolve lookups if they exist
  541. // MOST methods will only have a single parameters to resolve, so this should be fast
  542. foreach ($injectionMethodParameters as $fqParamPos => $info) {
  543. list($name, $type, $isRequired) = $info;
  544. $fqParamName = substr_replace($fqParamPos, ':' . $info[0], strrpos($fqParamPos, ':'));
  545. // PRIORITY 1 - consult user provided parameters
  546. if (isset($callTimeUserParams[$fqParamPos]) || isset($callTimeUserParams[$name])) {
  547. if (isset($callTimeUserParams[$fqParamPos])) {
  548. $callTimeCurValue =& $callTimeUserParams[$fqParamPos];
  549. } elseif (isset($callTimeUserParams[$fqParamName])) {
  550. $callTimeCurValue =& $callTimeUserParams[$fqParamName];
  551. } else {
  552. $callTimeCurValue =& $callTimeUserParams[$name];
  553. }
  554. if ($type !== false && is_string($callTimeCurValue)) {
  555. if ($this->instanceManager->hasAlias($callTimeCurValue)) {
  556. // was an alias provided?
  557. $computedParams['retrieval'][$fqParamPos] = array(
  558. $callTimeUserParams[$name],
  559. $this->instanceManager->getClassFromAlias($callTimeCurValue)
  560. );
  561. } elseif ($this->definitions->hasClass($callTimeUserParams[$name])) {
  562. // was a known class provided?
  563. $computedParams['retrieval'][$fqParamPos] = array(
  564. $callTimeCurValue,
  565. $callTimeCurValue
  566. );
  567. } else {
  568. // must be a value
  569. $computedParams['value'][$fqParamPos] = $callTimeCurValue;
  570. }
  571. } else {
  572. // int, float, null, object, etc
  573. $computedParams['value'][$fqParamPos] = $callTimeCurValue;
  574. }
  575. unset($callTimeCurValue);
  576. continue;
  577. }
  578. // PRIORITY 2 -specific instance configuration (thisAlias) - this alias
  579. // PRIORITY 3 -THEN specific instance configuration (thisClass) - this class
  580. // PRIORITY 4 -THEN specific instance configuration (requestedAlias) - requested alias
  581. // PRIORITY 5 -THEN specific instance configuration (requestedClass) - requested class
  582. foreach (array('thisAlias', 'thisClass', 'requestedAlias', 'requestedClass') as $thisIndex) {
  583. // check the provided parameters config
  584. if (isset($iConfig[$thisIndex]['parameters'][$fqParamPos])
  585. || isset($iConfig[$thisIndex]['parameters'][$fqParamName])
  586. || isset($iConfig[$thisIndex]['parameters'][$name])) {
  587. if (isset($iConfig[$thisIndex]['parameters'][$fqParamPos])) {
  588. $iConfigCurValue =& $iConfig[$thisIndex]['parameters'][$fqParamPos];
  589. } elseif (isset($iConfig[$thisIndex]['parameters'][$fqParamName])) {
  590. $iConfigCurValue =& $iConfig[$thisIndex]['parameters'][$fqParamName];
  591. } else {
  592. $iConfigCurValue =& $iConfig[$thisIndex]['parameters'][$name];
  593. }
  594. if ($type === false && is_string($iConfigCurValue)) {
  595. $computedParams['value'][$fqParamPos] = $iConfigCurValue;
  596. } elseif (is_string($iConfigCurValue)
  597. && isset($aliases[$iConfigCurValue])) {
  598. $computedParams['retrieval'][$fqParamPos] = array(
  599. $iConfig[$thisIndex]['parameters'][$name],
  600. $this->instanceManager->getClassFromAlias($iConfigCurValue)
  601. );
  602. } elseif (is_string($iConfigCurValue)
  603. && $this->definitions->hasClass($iConfigCurValue)) {
  604. $computedParams['retrieval'][$fqParamPos] = array(
  605. $iConfigCurValue,
  606. $iConfigCurValue
  607. );
  608. } elseif (is_object($iConfigCurValue)
  609. && $iConfigCurValue instanceof Closure
  610. && $type !== 'Closure') {
  611. /* @var $iConfigCurValue Closure */
  612. $computedParams['value'][$fqParamPos] = $iConfigCurValue();
  613. } else {
  614. $computedParams['value'][$fqParamPos] = $iConfigCurValue;
  615. }
  616. unset($iConfigCurValue);
  617. continue 2;
  618. }
  619. }
  620. // PRIORITY 6 - globally preferred implementations
  621. // next consult alias level preferred instances
  622. // RESOLVE_EAGER wants to inject the cross-cutting concerns.
  623. // If you want to retrieve an instance from TypePreferences,
  624. // use AwareInterface or specify the method requirement option METHOD_IS_EAGER at ClassDefinition
  625. if ($methodRequirementType & self::RESOLVE_EAGER) {
  626. if ($alias && $this->instanceManager->hasTypePreferences($alias)) {
  627. $pInstances = $this->instanceManager->getTypePreferences($alias);
  628. foreach ($pInstances as $pInstance) {
  629. if (is_object($pInstance)) {
  630. $computedParams['value'][$fqParamPos] = $pInstance;
  631. continue 2;
  632. }
  633. $pInstanceClass = ($this->instanceManager->hasAlias($pInstance)) ?
  634. $this->instanceManager->getClassFromAlias($pInstance) : $pInstance;
  635. if ($pInstanceClass === $type || self::isSubclassOf($pInstanceClass, $type)) {
  636. $computedParams['retrieval'][$fqParamPos] = array($pInstance, $pInstanceClass);
  637. continue 2;
  638. }
  639. }
  640. }
  641. // next consult class level preferred instances
  642. if ($type && $this->instanceManager->hasTypePreferences($type)) {
  643. $pInstances = $this->instanceManager->getTypePreferences($type);
  644. foreach ($pInstances as $pInstance) {
  645. if (is_object($pInstance)) {
  646. $computedParams['value'][$fqParamPos] = $pInstance;
  647. continue 2;
  648. }
  649. $pInstanceClass = ($this->instanceManager->hasAlias($pInstance)) ?
  650. $this->instanceManager->getClassFromAlias($pInstance) : $pInstance;
  651. if ($pInstanceClass === $type || self::isSubclassOf($pInstanceClass, $type)) {
  652. $computedParams['retrieval'][$fqParamPos] = array($pInstance, $pInstanceClass);
  653. continue 2;
  654. }
  655. }
  656. }
  657. }
  658. if (!$isRequired) {
  659. $computedParams['optional'][$fqParamPos] = true;
  660. }
  661. if ($type && $isRequired && ($methodRequirementType & self::RESOLVE_EAGER)) {
  662. $computedParams['retrieval'][$fqParamPos] = array($type, $type);
  663. }
  664. }
  665. $index = 0;
  666. foreach ($injectionMethodParameters as $fqParamPos => $value) {
  667. $name = $value[0];
  668. if (isset($computedParams['value'][$fqParamPos])) {
  669. // if there is a value supplied, use it
  670. $resolvedParams[$index] = $computedParams['value'][$fqParamPos];
  671. } elseif (isset($computedParams['retrieval'][$fqParamPos])) {
  672. // detect circular dependencies! (they can only happen in instantiators)
  673. if ($isInstantiator && in_array($computedParams['retrieval'][$fqParamPos][1], $this->currentDependencies)) {
  674. throw new Exception\CircularDependencyException(
  675. "Circular dependency detected: $class depends on {$value[1]} and viceversa"
  676. );
  677. }
  678. array_push($this->currentDependencies, $class);
  679. $dConfig = $this->instanceManager->getConfig($computedParams['retrieval'][$fqParamPos][0]);
  680. try {
  681. if ($dConfig['shared'] === false) {
  682. $resolvedParams[$index] = $this->newInstance($computedParams['retrieval'][$fqParamPos][0], $callTimeUserParams, false);
  683. } else {
  684. $resolvedParams[$index] = $this->get($computedParams['retrieval'][$fqParamPos][0], $callTimeUserParams);
  685. }
  686. } catch (DiRuntimeException $e) {
  687. if ($methodRequirementType & self::RESOLVE_STRICT) {
  688. //finally ( be aware to do at the end of flow)
  689. array_pop($this->currentDependencies);
  690. // if this item was marked strict,
  691. // plus it cannot be resolve, and no value exist, bail out
  692. throw new Exception\MissingPropertyException(sprintf(
  693. 'Missing %s for parameter ' . $name . ' for ' . $class . '::' . $method,
  694. (($value[0] === null) ? 'value' : 'instance/object' )
  695. ),
  696. $e->getCode(),
  697. $e);
  698. } else {
  699. //finally ( be aware to do at the end of flow)
  700. array_pop($this->currentDependencies);
  701. return false;
  702. }
  703. } catch (ServiceManagerException $e) {
  704. // Zend\ServiceManager\Exception\ServiceNotCreatedException
  705. if ($methodRequirementType & self::RESOLVE_STRICT) {
  706. //finally ( be aware to do at the end of flow)
  707. array_pop($this->currentDependencies);
  708. // if this item was marked strict,
  709. // plus it cannot be resolve, and no value exist, bail out
  710. throw new Exception\MissingPropertyException(sprintf(
  711. 'Missing %s for parameter ' . $name . ' for ' . $class . '::' . $method,
  712. (($value[0] === null) ? 'value' : 'instance/object' )
  713. ),
  714. $e->getCode(),
  715. $e);
  716. } else {
  717. //finally ( be aware to do at the end of flow)
  718. array_pop($this->currentDependencies);
  719. return false;
  720. }
  721. }
  722. array_pop($this->currentDependencies);
  723. } elseif (!array_key_exists($fqParamPos, $computedParams['optional'])) {
  724. if ($methodRequirementType & self::RESOLVE_STRICT) {
  725. // if this item was not marked as optional,
  726. // plus it cannot be resolve, and no value exist, bail out
  727. throw new Exception\MissingPropertyException(sprintf(
  728. 'Missing %s for parameter ' . $name . ' for ' . $class . '::' . $method,
  729. (($value[0] === null) ? 'value' : 'instance/object' )
  730. ));
  731. } else {
  732. return false;
  733. }
  734. } else {
  735. $resolvedParams[$index] = $value[3];
  736. }
  737. $index++;
  738. }
  739. return $resolvedParams; // return ordered list of parameters
  740. }
  741. /**
  742. * Utility method used to retrieve the class of a particular instance. This is here to allow extending classes to
  743. * override how class names are resolved
  744. *
  745. * @internal this method is used by the ServiceLocator\DependencyInjectorProxy class to interact with instances
  746. * and is a hack to be used internally until a major refactor does not split the `resolveMethodParameters`. Do not
  747. * rely on its functionality.
  748. * @param Object $instance
  749. * @return string
  750. */
  751. protected function getClass($instance)
  752. {
  753. return get_class($instance);
  754. }
  755. /**
  756. * Checks if the object has this class as one of its parents
  757. *
  758. * @see https://bugs.php.net/bug.php?id=53727
  759. * @see https://github.com/zendframework/zf2/pull/1807
  760. *
  761. * @param string $className
  762. * @param $type
  763. * @return bool
  764. */
  765. protected static function isSubclassOf($className, $type)
  766. {
  767. if (is_subclass_of($className, $type)) {
  768. return true;
  769. }
  770. if (version_compare(PHP_VERSION, '5.3.7', '>=')) {
  771. return false;
  772. }
  773. if (!interface_exists($type)) {
  774. return false;
  775. }
  776. $r = new ReflectionClass($className);
  777. return $r->implementsInterface($type);
  778. }
  779. }