/library/Zend/Di/InstanceManager.php

https://github.com/MontmereLimited/zf2 · PHP · 382 lines · 252 code · 39 blank · 91 comment · 29 complexity · 5c27b5e07a1648bea776d1753658fd11 MD5 · raw file

  1. <?php
  2. namespace Zend\Di;
  3. class InstanceManager /* implements InstanceCollection */
  4. {
  5. /**
  6. * Array of shared instances
  7. * @var array
  8. */
  9. protected $sharedInstances = array();
  10. /**
  11. * Array of shared instances with params
  12. * @var array
  13. */
  14. protected $sharedInstancesWithParams = array('hashShort' => array(), 'hashLong' => array());
  15. /**
  16. * Array of class aliases
  17. * @var array key: alias, value: class
  18. */
  19. protected $aliases = array();
  20. /**
  21. * The template to use for housing configuration information
  22. * @var array
  23. */
  24. protected $configurationTemplate = array(
  25. /**
  26. * alias|class => alias|class
  27. * interface|abstract => alias|class|object
  28. * name => value
  29. */
  30. 'parameters' => array(),
  31. /**
  32. * injection type => array of ordered method params
  33. */
  34. 'injections' => array(),
  35. /**
  36. * alias|class => bool
  37. */
  38. 'shared' => true
  39. );
  40. /**
  41. * An array of instance configuration data
  42. * @var array
  43. */
  44. protected $configurations = array();
  45. /**
  46. * An array of globally preferred implementations for interfaces/abstracts
  47. * @var array
  48. */
  49. protected $typePreferences = array();
  50. /**
  51. * Does this instance manager have this shared instance
  52. * @return bool
  53. */
  54. public function hasSharedInstance($classOrAlias)
  55. {
  56. return isset($this->sharedInstances[$classOrAlias]);
  57. }
  58. /**
  59. * getSharedInstance()
  60. */
  61. public function getSharedInstance($classOrAlias)
  62. {
  63. return $this->sharedInstances[$classOrAlias];
  64. }
  65. /**
  66. * addSharedInstance()
  67. */
  68. public function addSharedInstance($instance, $classOrAlias)
  69. {
  70. if (!is_object($instance)) {
  71. throw new Exception\InvalidArgumentException('This method requires an object to be shared');
  72. }
  73. $this->sharedInstances[$classOrAlias] = $instance;
  74. }
  75. /**
  76. * hasSharedInstanceWithParameters()
  77. *
  78. * @param string $classOrAlias
  79. * @param array $params
  80. * @param bool $returnFashHashLookupKey
  81. * @return bool|string
  82. */
  83. public function hasSharedInstanceWithParameters($classOrAlias, array $params, $returnFashHashLookupKey = false)
  84. {
  85. ksort($params);
  86. $hashKey = $this->createHashForKeys($classOrAlias, array_keys($params));
  87. if (isset($this->sharedInstancesWithParams['hashShort'][$hashKey])) {
  88. $hashValue = $this->createHashForValues($classOrAlias, $params);
  89. if (isset($this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue])) {
  90. return ($returnFashHashLookupKey) ? $hashKey . '/' . $hashValue : true;
  91. }
  92. }
  93. return false;
  94. }
  95. /**
  96. * addSharedInstanceWithParameters()
  97. *
  98. * @param object $instance
  99. * @param string $classOrAlias
  100. * @param array $params
  101. * @return void
  102. */
  103. public function addSharedInstanceWithParameters($instance, $classOrAlias, array $params)
  104. {
  105. ksort($params);
  106. $hashKey = $this->createHashForKeys($classOrAlias, array_keys($params));
  107. $hashValue = $this->createHashForValues($classOrAlias, $params);
  108. if (!isset($this->sharedInstancesWithParams[$hashKey])
  109. || !is_array($this->sharedInstancesWithParams[$hashKey])) {
  110. $this->sharedInstancesWithParams[$hashKey] = array();
  111. }
  112. $this->sharedInstancesWithParams['hashShort'][$hashKey] = true;
  113. $this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue] = $instance;
  114. }
  115. public function getSharedInstanceWithParameters($classOrAlias, array $params, $fastHashFromHasLookup = null)
  116. {
  117. if ($fastHashFromHasLookup) {
  118. return $this->sharedInstancesWithParams['hashLong'][$fastHashFromHasLookup];
  119. }
  120. ksort($params);
  121. $hashKey = $this->createHashForKeys($classOrAlias, array_keys($params));
  122. if (isset($this->sharedInstancesWithParams['hashShort'][$hashKey])) {
  123. $hashValue = $this->createHashForValues($classOrAlias, $params);
  124. if (isset($this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue])) {
  125. return $this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue];
  126. }
  127. }
  128. return false;
  129. }
  130. public function hasAlias($alias)
  131. {
  132. return (isset($this->aliases[$alias]));
  133. }
  134. public function getAliases()
  135. {
  136. return $this->aliases;
  137. }
  138. /**
  139. * getClassFromAlias()
  140. *
  141. * @param string
  142. * @return bool
  143. */
  144. public function getClassFromAlias($alias)
  145. {
  146. if (!isset($this->aliases[$alias])) {
  147. return false;
  148. }
  149. $r = 0;
  150. while (isset($this->aliases[$alias])) {
  151. $alias = $this->aliases[$alias];
  152. $r++;
  153. if ($r > 100) {
  154. throw new Exception\RuntimeException(
  155. sprintf('Possible infinite recursion in DI alias! Max recursion of 100 levels reached at alias "%s".', $alias)
  156. );
  157. }
  158. }
  159. return $alias;
  160. }
  161. protected function getBaseAlias($alias)
  162. {
  163. if (!$this->hasAlias($alias)) {
  164. return false;
  165. }
  166. $lastAlias = false;
  167. $r = 0;
  168. while (isset($this->aliases[$alias])) {
  169. $lastAlias = $alias;
  170. $alias = $this->aliases[$alias];
  171. $r++;
  172. if ($r > 100) {
  173. throw new Exception\RuntimeException(
  174. sprintf('Possible infinite recursion in DI alias! Max recursion of 100 levels reached at alias "%s".', $alias)
  175. );
  176. }
  177. }
  178. return $lastAlias;
  179. }
  180. /**
  181. * addAlias()
  182. *
  183. * @throws Exception\InvalidArgumentException
  184. * @param $alias
  185. * @param $class
  186. * @param array $parameters
  187. * @return void
  188. */
  189. public function addAlias($alias, $class, array $parameters = array())
  190. {
  191. if (!preg_match('#^[a-zA-Z0-9-_]+$#', $alias)) {
  192. throw new Exception\InvalidArgumentException(
  193. 'Aliases must be alphanumeric and can contain dashes and underscores only.'
  194. );
  195. }
  196. $this->aliases[$alias] = $class;
  197. if ($parameters) {
  198. $this->setParameters($alias, $parameters);
  199. }
  200. }
  201. public function hasConfiguration($aliasOrClass)
  202. {
  203. $key = ($this->hasAlias($aliasOrClass)) ? 'alias:' . $this->getBaseAlias($aliasOrClass) : $aliasOrClass;
  204. if (!isset($this->configurations[$key])) {
  205. return false;
  206. }
  207. if ($this->configurations[$key] === $this->configurationTemplate) {
  208. return false;
  209. }
  210. return true;
  211. }
  212. public function setConfiguration($aliasOrClass, array $configuration, $append = false)
  213. {
  214. $key = ($this->hasAlias($aliasOrClass)) ? 'alias:' . $this->getBaseAlias($aliasOrClass) : $aliasOrClass;
  215. if (!isset($this->configurations[$key]) || !$append) {
  216. $this->configurations[$key] = $this->configurationTemplate;
  217. }
  218. // Ignore anything but 'parameters' and 'injections'
  219. $configuration = array(
  220. 'parameters' => isset($configuration['parameters']) ? $configuration['parameters'] : array(),
  221. 'injections' => isset($configuration['injections']) ? $configuration['injections'] : array(),
  222. 'shared' => isset($configuration['shared']) ? $configuration['shared'] : true
  223. );
  224. $this->configurations[$key] = array_replace_recursive($this->configurations[$key], $configuration);
  225. }
  226. public function getClasses()
  227. {
  228. $classes = array();
  229. foreach ($this->configurations as $name => $data) {
  230. if (strpos($name, 'alias') === 0) continue;
  231. $classes[] = $name;
  232. }
  233. return $classes;
  234. }
  235. public function getConfiguration($aliasOrClass)
  236. {
  237. $key = ($this->hasAlias($aliasOrClass)) ? 'alias:' . $this->getBaseAlias($aliasOrClass) : $aliasOrClass;
  238. if (isset($this->configurations[$key])) {
  239. return $this->configurations[$key];
  240. } else {
  241. return $this->configurationTemplate;
  242. }
  243. }
  244. /**
  245. * setParameters() is a convenience method for:
  246. * setConfiguration($type, array('parameters' => array(...)), true);
  247. *
  248. * @param string $type Alias or Class
  249. * @param array $parameters Multi-dim array of parameters and their values
  250. */
  251. public function setParameters($aliasOrClass, array $parameters)
  252. {
  253. return $this->setConfiguration($aliasOrClass, array('parameters' => $parameters), true);
  254. }
  255. /**
  256. * setInjections() is a convenience method for:
  257. * setConfiguration($type, array('injections' => array(...)), true);
  258. *
  259. * @param string $type Alias or Class
  260. * @param array $methods Multi-dim array of methods and their parameters
  261. */
  262. public function setInjections($aliasOrClass, array $injections)
  263. {
  264. return $this->setConfiguration($aliasOrClass, array('injections' => $injections), true);
  265. }
  266. public function setShared($aliasOrClass, $isShared)
  267. {
  268. return $this->setConfiguration($aliasOrClass, array('shared' => $isShared), true);
  269. }
  270. public function hasTypePreferences($interfaceOrAbstract)
  271. {
  272. $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
  273. return (isset($this->typePreferences[$key]) && $this->typePreferences[$key]);
  274. }
  275. public function setTypePreference($interfaceOrAbstract, array $preferredImplementations)
  276. {
  277. $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
  278. foreach ($preferredImplementations as $preferredImplementation) {
  279. $this->addTypePreference($key, $preferredImplementation);
  280. }
  281. return $this;
  282. }
  283. public function getTypePreferences($interfaceOrAbstract)
  284. {
  285. $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
  286. if (isset($this->typePreferences[$key])) {
  287. return $this->typePreferences[$key];
  288. }
  289. return array();
  290. }
  291. public function unsetTypePreferences($interfaceOrAbstract)
  292. {
  293. $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
  294. unset($this->typePreferences[$key]);
  295. }
  296. public function addTypePreference($interfaceOrAbstract, $preferredImplementation)
  297. {
  298. $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
  299. if (!isset($this->typePreferences[$key])) {
  300. $this->typePreferences[$key] = array();
  301. }
  302. $this->typePreferences[$key][] = $preferredImplementation;
  303. return $this;
  304. }
  305. public function removeTypePreference($interfaceOrAbstract, $preferredType)
  306. {
  307. $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
  308. if (!isset($this->typePreferences[$key]) || !in_array($preferredType, $this->typePreferences[$key])) {
  309. return false;
  310. }
  311. unset($this->typePreferences[$key][array_search($key, $this->typePreferences)]);
  312. return $this;
  313. }
  314. protected function createHashForKeys($classOrAlias, $paramKeys)
  315. {
  316. return $classOrAlias . ':' . implode('|', $paramKeys);
  317. }
  318. protected function createHashForValues($classOrAlias, $paramValues)
  319. {
  320. $hashValue = '';
  321. foreach ($paramValues as $param) {
  322. switch (gettype($param)) {
  323. case 'object':
  324. $hashValue .= spl_object_hash($param) . '|';
  325. break;
  326. case 'integer':
  327. case 'string':
  328. case 'boolean':
  329. case 'NULL':
  330. case 'double':
  331. $hashValue .= $param . '|';
  332. break;
  333. case 'array':
  334. $hashValue .= 'Array|';
  335. break;
  336. case 'resource':
  337. $hashValue .= 'resource|';
  338. break;
  339. }
  340. }
  341. return $hashValue;
  342. }
  343. }