PageRenderTime 61ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/classes/Swift/DependencyContainer.php

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