DependencyContainer.php 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. <?php
  2. /*
  3. * This file is part of SwiftMailer.
  4. * (c) 2004-2009 Chris Corbyn
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * Dependency Injection container.
  11. * @package Swift
  12. * @author Chris Corbyn
  13. */
  14. class Swift_DependencyContainer
  15. {
  16. /** Constant for literal value types */
  17. const TYPE_VALUE = 0x0001;
  18. /** Constant for new instance types */
  19. const TYPE_INSTANCE = 0x0010;
  20. /** Constant for shared instance types */
  21. const TYPE_SHARED = 0x0100;
  22. /** Constant for aliases */
  23. const TYPE_ALIAS = 0x1000;
  24. /** Singleton instance */
  25. private static $_instance = null;
  26. /** The data container */
  27. private $_store = array();
  28. /** The current endpoint in the data container */
  29. private $_endPoint;
  30. /**
  31. * Constructor should not be used.
  32. * Use {@link getInstance()} instead.
  33. */
  34. public function __construct() { }
  35. /**
  36. * Returns a singleton of the DependencyContainer.
  37. * @return Swift_DependencyContainer
  38. */
  39. public static function getInstance()
  40. {
  41. if (!isset(self::$_instance))
  42. {
  43. self::$_instance = new self();
  44. }
  45. return self::$_instance;
  46. }
  47. /**
  48. * List the names of all items stored in the Container.
  49. * @return array
  50. */
  51. public function listItems()
  52. {
  53. return array_keys($this->_store);
  54. }
  55. /**
  56. * Test if an item is registered in this container with the given name.
  57. * @param string $itemName
  58. * @return boolean
  59. * @see register()
  60. */
  61. public function has($itemName)
  62. {
  63. return array_key_exists($itemName, $this->_store)
  64. && isset($this->_store[$itemName]['lookupType']);
  65. }
  66. /**
  67. * Lookup the item with the given $itemName.
  68. * @param string $itemName
  69. * @return mixed
  70. * @throws Swift_DependencyException If the dependency is not found
  71. * @see register()
  72. */
  73. public function lookup($itemName)
  74. {
  75. if (!$this->has($itemName))
  76. {
  77. throw new Swift_DependencyException(
  78. 'Cannot lookup dependency "' . $itemName . '" since it is not registered.'
  79. );
  80. }
  81. switch ($this->_store[$itemName]['lookupType'])
  82. {
  83. case self::TYPE_ALIAS:
  84. return $this->_createAlias($itemName);
  85. case self::TYPE_VALUE:
  86. return $this->_getValue($itemName);
  87. case self::TYPE_INSTANCE:
  88. return $this->_createNewInstance($itemName);
  89. case self::TYPE_SHARED:
  90. return $this->_createSharedInstance($itemName);
  91. }
  92. }
  93. /**
  94. * Create an array of arguments passed to the constructor of $itemName.
  95. * @param string $itemName
  96. * @return array
  97. */
  98. public function createDependenciesFor($itemName)
  99. {
  100. $args = array();
  101. if (isset($this->_store[$itemName]['args']))
  102. {
  103. $args = $this->_resolveArgs($this->_store[$itemName]['args']);
  104. }
  105. return $args;
  106. }
  107. /**
  108. * Register a new dependency with $itemName.
  109. * This method returns the current DependencyContainer instance because it
  110. * requires the use of the fluid interface to set the specific details for the
  111. * dependency.
  112. *
  113. * @param string $itemName
  114. * @return Swift_DependencyContainer
  115. * @see asNewInstanceOf(), asSharedInstanceOf(), asValue()
  116. */
  117. public function register($itemName)
  118. {
  119. $this->_store[$itemName] = array();
  120. $this->_endPoint =& $this->_store[$itemName];
  121. return $this;
  122. }
  123. /**
  124. * Specify the previously registered item as a literal value.
  125. * {@link register()} must be called before this will work.
  126. *
  127. * @param mixed $value
  128. * @return Swift_DependencyContainer
  129. */
  130. public function asValue($value)
  131. {
  132. $endPoint =& $this->_getEndPoint();
  133. $endPoint['lookupType'] = self::TYPE_VALUE;
  134. $endPoint['value'] = $value;
  135. return $this;
  136. }
  137. /**
  138. * Specify the previously registered item as an alias of another item.
  139. * @param string $lookup
  140. * @return Swift_DependencyContainer
  141. */
  142. public function asAliasOf($lookup)
  143. {
  144. $endPoint =& $this->_getEndPoint();
  145. $endPoint['lookupType'] = self::TYPE_ALIAS;
  146. $endPoint['ref'] = $lookup;
  147. return $this;
  148. }
  149. /**
  150. * Specify the previously registered item as a new instance of $className.
  151. * {@link register()} must be called before this will work.
  152. * Any arguments can be set with {@link withDependencies()},
  153. * {@link addConstructorValue()} or {@link addConstructorLookup()}.
  154. *
  155. * @param string $className
  156. * @return Swift_DependencyContainer
  157. * @see withDependencies(), addConstructorValue(), addConstructorLookup()
  158. */
  159. public function asNewInstanceOf($className)
  160. {
  161. $endPoint =& $this->_getEndPoint();
  162. $endPoint['lookupType'] = self::TYPE_INSTANCE;
  163. $endPoint['className'] = $className;
  164. return $this;
  165. }
  166. /**
  167. * Specify the previously registered item as a shared instance of $className.
  168. * {@link register()} must be called before this will work.
  169. * @param string $className
  170. * @return Swift_DependencyContainer
  171. */
  172. public function asSharedInstanceOf($className)
  173. {
  174. $endPoint =& $this->_getEndPoint();
  175. $endPoint['lookupType'] = self::TYPE_SHARED;
  176. $endPoint['className'] = $className;
  177. return $this;
  178. }
  179. /**
  180. * Specify a list of injected dependencies for the previously registered item.
  181. * This method takes an array of lookup names.
  182. *
  183. * @param array $lookups
  184. * @return Swift_DependencyContainer
  185. * @see addConstructorValue(), addConstructorLookup()
  186. */
  187. public function withDependencies(array $lookups)
  188. {
  189. $endPoint =& $this->_getEndPoint();
  190. $endPoint['args'] = array();
  191. foreach ($lookups as $lookup)
  192. {
  193. $this->addConstructorLookup($lookup);
  194. }
  195. return $this;
  196. }
  197. /**
  198. * Specify a literal (non looked up) value for the constructor of the
  199. * previously registered item.
  200. *
  201. * @param mixed $value
  202. * @return Swift_DependencyContainer
  203. * @see withDependencies(), addConstructorLookup()
  204. */
  205. public function addConstructorValue($value)
  206. {
  207. $endPoint =& $this->_getEndPoint();
  208. if (!isset($endPoint['args']))
  209. {
  210. $endPoint['args'] = array();
  211. }
  212. $endPoint['args'][] = array('type' => 'value', 'item' => $value);
  213. return $this;
  214. }
  215. /**
  216. * Specify a dependency lookup for the constructor of the previously
  217. * registered item.
  218. *
  219. * @param string $lookup
  220. * @return Swift_DependencyContainer
  221. * @see withDependencies(), addConstructorValue()
  222. */
  223. public function addConstructorLookup($lookup)
  224. {
  225. $endPoint =& $this->_getEndPoint();
  226. if (!isset($this->_endPoint['args']))
  227. {
  228. $endPoint['args'] = array();
  229. }
  230. $endPoint['args'][] = array('type' => 'lookup', 'item' => $lookup);
  231. return $this;
  232. }
  233. // -- Private methods
  234. /** Get the literal value with $itemName */
  235. private function _getValue($itemName)
  236. {
  237. return $this->_store[$itemName]['value'];
  238. }
  239. /** Resolve an alias to another item */
  240. private function _createAlias($itemName)
  241. {
  242. return $this->lookup($this->_store[$itemName]['ref']);
  243. }
  244. /** Create a fresh instance of $itemName */
  245. private function _createNewInstance($itemName)
  246. {
  247. $reflector = new ReflectionClass($this->_store[$itemName]['className']);
  248. if ($reflector->getConstructor())
  249. {
  250. return $reflector->newInstanceArgs(
  251. $this->createDependenciesFor($itemName)
  252. );
  253. }
  254. else
  255. {
  256. return $reflector->newInstance();
  257. }
  258. }
  259. /** Create and register a shared instance of $itemName */
  260. private function _createSharedInstance($itemName)
  261. {
  262. if (!isset($this->_store[$itemName]['instance']))
  263. {
  264. $this->_store[$itemName]['instance'] = $this->_createNewInstance($itemName);
  265. }
  266. return $this->_store[$itemName]['instance'];
  267. }
  268. /** Get the current endpoint in the store */
  269. private function &_getEndPoint()
  270. {
  271. if (!isset($this->_endPoint))
  272. {
  273. throw new BadMethodCallException(
  274. 'Component must first be registered by calling register()'
  275. );
  276. }
  277. return $this->_endPoint;
  278. }
  279. /** Get an argument list with dependencies resolved */
  280. private function _resolveArgs(array $args)
  281. {
  282. $resolved = array();
  283. foreach ($args as $argDefinition)
  284. {
  285. switch ($argDefinition['type'])
  286. {
  287. case 'lookup':
  288. $resolved[] = $this->_lookupRecursive($argDefinition['item']);
  289. break;
  290. case 'value':
  291. $resolved[] = $argDefinition['item'];
  292. break;
  293. }
  294. }
  295. return $resolved;
  296. }
  297. /** Resolve a single dependency with an collections */
  298. private function _lookupRecursive($item)
  299. {
  300. if (is_array($item))
  301. {
  302. $collection = array();
  303. foreach ($item as $k => $v)
  304. {
  305. $collection[$k] = $this->_lookupRecursive($v);
  306. }
  307. return $collection;
  308. }
  309. else
  310. {
  311. return $this->lookup($item);
  312. }
  313. }
  314. }