DependencyContainer.php 9.6KB

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