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