These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @file |
||
4 | * Contains \Drupal\service_container\DependencyInjection\Container |
||
5 | */ |
||
6 | |||
7 | namespace Drupal\service_container\DependencyInjection; |
||
8 | |||
9 | use ReflectionClass; |
||
10 | use RuntimeException; |
||
11 | use Symfony\Component\DependencyInjection\ScopeInterface; |
||
12 | |||
13 | /** |
||
14 | * Container is a DI container that provides services to users of the class. |
||
15 | * |
||
16 | * @ingroup dic |
||
17 | */ |
||
18 | class Container implements ContainerInterface { |
||
19 | |||
20 | /** |
||
21 | * The parameters of the container. |
||
22 | * |
||
23 | * @var array |
||
24 | */ |
||
25 | protected $parameters = array(); |
||
26 | |||
27 | /** |
||
28 | * The service definitions of the container. |
||
29 | * |
||
30 | * @var array |
||
31 | */ |
||
32 | protected $serviceDefinitions = array(); |
||
33 | |||
34 | /** |
||
35 | * The instantiated services. |
||
36 | * |
||
37 | * @var array |
||
38 | */ |
||
39 | protected $services = array(); |
||
40 | |||
41 | /** |
||
42 | * The currently loading services. |
||
43 | * |
||
44 | * @var array |
||
45 | */ |
||
46 | protected $loading = array(); |
||
47 | |||
48 | /** |
||
49 | * Can the container parameters still be changed. |
||
50 | * |
||
51 | * For testing purposes the container needs to be changed. |
||
52 | * |
||
53 | * @var bool |
||
54 | */ |
||
55 | protected $frozen = TRUE; |
||
56 | |||
57 | /** |
||
58 | * Constructs a new Container instance. |
||
59 | * |
||
60 | * @param array $container_definition |
||
61 | * An array containing the 'services' and 'parameters' |
||
62 | * @param bool $frozen |
||
63 | * (optional) Determines whether the container parameters can be changed, |
||
64 | * defaults to TRUE; |
||
65 | */ |
||
66 | public function __construct(array $container_definition, $frozen = TRUE) { |
||
67 | $this->parameters = $container_definition['parameters']; |
||
68 | $this->serviceDefinitions = $container_definition['services']; |
||
69 | $this->services['service_container'] = $this; |
||
70 | $this->frozen = $frozen; |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * {@inheritdoc} |
||
75 | */ |
||
76 | public function get($name, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { |
||
77 | View Code Duplication | if (isset($this->services[$name]) || ($invalidBehavior === ContainerInterface::NULL_ON_INVALID_REFERENCE && array_key_exists($name, $this->services))) { |
|
0 ignored issues
–
show
|
|||
78 | return $this->services[$name]; |
||
79 | } |
||
80 | |||
81 | if (isset($this->loading[$name])) { |
||
82 | throw new RuntimeException(sprintf('Circular reference detected for service "%s", path: "%s".', $name, implode(' -> ', array_keys($this->loading)))); |
||
83 | } |
||
84 | |||
85 | $definition = isset($this->serviceDefinitions[$name]) ? $this->serviceDefinitions[$name] : NULL; |
||
86 | |||
87 | if (!$definition && $invalidBehavior === ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { |
||
88 | throw new RuntimeException(sprintf('The "%s" service definition does not exist.', $name)); |
||
89 | } |
||
90 | |||
91 | if (!$definition) { |
||
92 | $this->services[$name] = NULL; |
||
93 | return $this->services[$name]; |
||
94 | } |
||
95 | |||
96 | if (isset($definition['alias'])) { |
||
97 | return $this->get($definition['alias'], $invalidBehavior); |
||
98 | } |
||
99 | |||
100 | $this->loading[$name] = TRUE; |
||
101 | |||
102 | $definition += array( |
||
103 | 'class' => '', |
||
104 | 'factory' => '', |
||
105 | 'factory_class' => '', |
||
106 | 'factory_method' => '', |
||
107 | 'factory_service' => '', |
||
108 | 'arguments' => array(), |
||
109 | 'properties' => array(), |
||
110 | 'calls' => array(), |
||
111 | 'tags' => array(), |
||
112 | ); // @codeCoverageIgnore |
||
113 | |||
114 | try { |
||
115 | if (!empty($definition['arguments'])) { |
||
116 | $arguments = $this->expandArguments($definition['arguments'], $invalidBehavior); |
||
117 | } else { |
||
118 | $arguments = array(); |
||
119 | } |
||
120 | if (!empty($definition['factory'])) { |
||
121 | $factory = $definition['factory']; |
||
122 | if (is_array($factory)) { |
||
123 | $factory = $this->expandArguments($factory, $invalidBehavior); |
||
124 | } |
||
125 | $service = call_user_func_array($factory, $arguments); |
||
126 | } |
||
127 | elseif (!empty($definition['factory_method'])) { |
||
128 | $method = $definition['factory_method']; |
||
129 | |||
130 | if (!empty($definition['factory_class'])) { |
||
131 | $factory = $definition['factory_class']; |
||
132 | } |
||
133 | elseif (!empty($definition['factory_service'])) { |
||
134 | $factory = $this->get($definition['factory_service'], $invalidBehavior); |
||
135 | } |
||
136 | else { |
||
137 | throw new RuntimeException(sprintf('Cannot create service "%s" from factory method without a factory service or factory class.', $name)); |
||
138 | } |
||
139 | $service = call_user_func_array(array($factory, $method), $arguments); |
||
140 | } |
||
141 | else { |
||
142 | // @todo Allow dynamic class definitions via parameters. |
||
143 | $class = $definition['class']; |
||
144 | $length = count($arguments); |
||
145 | |||
146 | switch ($length) { |
||
147 | case 0: |
||
148 | $service = new $class(); |
||
149 | break; |
||
150 | case 1: |
||
151 | $service = new $class($arguments[0]); |
||
152 | break; |
||
153 | case 2: |
||
154 | $service = new $class($arguments[0], $arguments[1]); |
||
155 | break; |
||
156 | case 3: |
||
157 | $service = new $class($arguments[0], $arguments[1], $arguments[2]); |
||
158 | break; |
||
159 | // @codeCoverageIgnoreStart |
||
160 | case 4: |
||
161 | $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3]); |
||
162 | break; |
||
163 | case 5: |
||
164 | $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); |
||
165 | break; |
||
166 | case 6: |
||
167 | $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]); |
||
168 | break; |
||
169 | case 7: |
||
170 | $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6]); |
||
171 | break; |
||
172 | case 8: |
||
173 | $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7]); |
||
174 | break; |
||
175 | case 9: |
||
176 | $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7], $arguments[8]); |
||
177 | break; |
||
178 | case 10: |
||
179 | $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7], $arguments[8], $arguments[9]); |
||
180 | break; |
||
181 | default: |
||
182 | $r = new ReflectionClass($class); |
||
183 | $service = $r->newInstanceArgs($arguments); |
||
184 | break; |
||
185 | } |
||
186 | // @codeCoverageIgnoreEnd |
||
187 | } |
||
188 | } |
||
189 | catch (\Exception $e) { |
||
190 | unset($this->loading[$name]); |
||
191 | throw $e; |
||
192 | } |
||
193 | $this->services[$name] = $service; |
||
194 | unset($this->loading[$name]); |
||
195 | |||
196 | foreach ($definition['calls'] as $call) { |
||
197 | $method = $call[0]; |
||
198 | $arguments = array(); |
||
199 | if (!empty($call[1])) { |
||
200 | $arguments = $this->expandArguments($call[1], $invalidBehavior); |
||
201 | } |
||
202 | call_user_func_array(array($service, $method), $arguments); |
||
203 | } |
||
204 | foreach ($definition['properties'] as $key => $value) { |
||
205 | $service->{$key} = $value; |
||
206 | } |
||
207 | |||
208 | return $this->services[$name]; |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * {@inheritdoc} |
||
213 | */ |
||
214 | public function set($id, $service, $scope = self::SCOPE_CONTAINER) { |
||
215 | if (isset($service)) { |
||
216 | $service->_serviceId = $id; |
||
217 | } |
||
218 | $this->services[$id] = $service; |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * {@inheritdoc} |
||
223 | */ |
||
224 | public function has($id) { |
||
225 | return isset($this->services[$id]) || isset($this->serviceDefinitions[$id]); |
||
226 | } |
||
227 | |||
228 | /** |
||
229 | * {@inheritdoc} |
||
230 | */ |
||
231 | public function createInstance($plugin_id, $service_definition) { |
||
232 | $temporary_name = 'plugin_' . $plugin_id; |
||
233 | $this->serviceDefinitions[$temporary_name] = $service_definition; |
||
234 | |||
235 | $plugin = $this->get($temporary_name); |
||
236 | unset($this->serviceDefinitions[$temporary_name]); |
||
237 | unset($this->services[$temporary_name]); |
||
238 | |||
239 | return $plugin; |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * {@inheritdoc} |
||
244 | */ |
||
245 | public function getDefinition($plugin_id, $exception_on_invalid = TRUE) { |
||
246 | $definition = isset($this->serviceDefinitions[$plugin_id]) ? $this->serviceDefinitions[$plugin_id] : NULL; |
||
247 | |||
248 | if (!$definition && $exception_on_invalid) { |
||
249 | throw new RuntimeException(sprintf('The "%s" service definition does not exist.', $plugin_id)); |
||
250 | } |
||
251 | |||
252 | return $definition; |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * {@inheritdoc} |
||
257 | */ |
||
258 | public function getDefinitions() { |
||
259 | return $this->serviceDefinitions; |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * {@inheritdoc} |
||
264 | */ |
||
265 | public function hasDefinition($plugin_id) { |
||
266 | return isset($this->serviceDefinitions[$plugin_id]); |
||
267 | } |
||
268 | |||
269 | /** |
||
270 | * {@inheritdoc} |
||
271 | */ |
||
272 | public function getParameter($name) { |
||
273 | return isset($this->parameters[$name]) ? $this->parameters[$name] : NULL; |
||
274 | } |
||
275 | |||
276 | /** |
||
277 | * {@inheritdoc} |
||
278 | */ |
||
279 | public function hasParameter($name) { |
||
280 | return isset($this->parameters[$name]); |
||
281 | } |
||
282 | |||
283 | /** |
||
284 | * {@inheritdoc} |
||
285 | */ |
||
286 | public function setParameter($name, $value) { |
||
287 | if ($this->frozen) { |
||
288 | throw new \BadMethodCallException("Container parameters can't be changed on runtime."); |
||
289 | } |
||
290 | $this->parameters[$name] = $value; |
||
291 | } |
||
292 | |||
293 | /** |
||
294 | * Expands arguments from %parameter and @service to the resolved values. |
||
295 | * |
||
296 | * @param array $arguments |
||
297 | * The arguments to expand. |
||
298 | * @param int $invalidBehavior |
||
299 | * The behavior when the service does not exist |
||
300 | * |
||
301 | * @return array |
||
302 | * The expanded arguments. |
||
303 | * |
||
304 | * @throws \RuntimeException if a parameter/service could not be resolved. |
||
305 | */ |
||
306 | protected function expandArguments($arguments, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { |
||
307 | foreach ($arguments as $key => $argument) { |
||
308 | if ($argument instanceof \stdClass) { |
||
309 | $name = $argument->id; |
||
310 | $this->serviceDefinitions[$name] = $argument->value; |
||
311 | $arguments[$key] = $this->get($name, $invalidBehavior); |
||
312 | unset($this->serviceDefinitions[$name]); |
||
313 | unset($this->services[$name]); |
||
314 | continue; |
||
315 | } |
||
316 | |||
317 | if (is_array($argument)) { |
||
318 | $arguments[$key] = $this->expandArguments($argument, $invalidBehavior); |
||
319 | continue; |
||
320 | } |
||
321 | |||
322 | if (!is_string($argument)) { |
||
323 | continue; |
||
324 | } |
||
325 | |||
326 | if (strpos($argument, '%') === 0) { |
||
327 | $name = substr($argument, 1, -1); |
||
328 | View Code Duplication | if (!isset($this->parameters[$name])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository.
Loading history...
|
|||
329 | if ($invalidBehavior === ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { |
||
330 | throw new RuntimeException("Could not find parameter: $name"); |
||
331 | } |
||
332 | $arguments[$key] = NULL; |
||
333 | continue; |
||
334 | } |
||
335 | $arguments[$key] = $this->parameters[$name]; |
||
336 | } |
||
337 | else if (strpos($argument, '@') === 0) { |
||
338 | $name = substr($argument, 1); |
||
339 | if (strpos($name, '?') === 0) { |
||
340 | $name = substr($name, 1); |
||
341 | $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; |
||
342 | } |
||
343 | View Code Duplication | if (!isset($this->serviceDefinitions[$name])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository.
Loading history...
|
|||
344 | if ($invalidBehavior === ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { |
||
345 | throw new RuntimeException("Could not find service: $name"); |
||
346 | } |
||
347 | $arguments[$key] = NULL; |
||
348 | continue; |
||
349 | } |
||
350 | $arguments[$key] = $this->get($name, $invalidBehavior); |
||
351 | } |
||
352 | } |
||
353 | |||
354 | return $arguments; |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * {@inheritdoc} |
||
359 | * |
||
360 | * @codeCoverageIgnore |
||
361 | */ |
||
362 | public function enterScope($name) { |
||
363 | throw new \BadMethodCallException(sprintf("'%s' is not implemented", __FUNCTION__)); |
||
364 | } |
||
365 | |||
366 | /** |
||
367 | * {@inheritdoc} |
||
368 | * |
||
369 | * @codeCoverageIgnore |
||
370 | */ |
||
371 | public function leaveScope($name) { |
||
372 | throw new \BadMethodCallException(sprintf("'%s' is not implemented", __FUNCTION__)); |
||
373 | } |
||
374 | |||
375 | /** |
||
376 | * {@inheritdoc} |
||
377 | * |
||
378 | * @codeCoverageIgnore |
||
379 | */ |
||
380 | public function addScope(ScopeInterface $scope) { |
||
381 | throw new \BadMethodCallException(sprintf("'%s' is not implemented", __FUNCTION__)); |
||
382 | } |
||
383 | |||
384 | /** |
||
385 | * {@inheritdoc} |
||
386 | * |
||
387 | * @codeCoverageIgnore |
||
388 | */ |
||
389 | public function hasScope($name) { |
||
390 | throw new \BadMethodCallException(sprintf("'%s' is not implemented", __FUNCTION__)); |
||
391 | } |
||
392 | |||
393 | /** |
||
394 | * {@inheritdoc} |
||
395 | * |
||
396 | * @codeCoverageIgnore |
||
397 | */ |
||
398 | public function isScopeActive($name) { |
||
399 | throw new \BadMethodCallException(sprintf("'%s' is not implemented", __FUNCTION__)); |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * {@inheritdoc} |
||
404 | */ |
||
405 | public function initialized($id) { |
||
406 | return isset($this->services[$id]); |
||
407 | } |
||
408 | |||
409 | /** |
||
410 | * Camelizes a string. |
||
411 | * |
||
412 | * @param $name |
||
413 | * The string to camelize. |
||
414 | * |
||
415 | * @return string |
||
416 | * The camelized string. |
||
417 | * |
||
418 | */ |
||
419 | public static function camelize($name) { |
||
420 | return strtr(ucwords(strtr($name, array('_' => ' ', '\\' => '_ '))), array(' ' => '')); |
||
421 | } |
||
422 | |||
423 | /** |
||
424 | * Un-camelizes a string. |
||
425 | * |
||
426 | * @param $name |
||
427 | * The string to underscore. |
||
428 | * |
||
429 | * @return string |
||
430 | * The underscored string. |
||
431 | * |
||
432 | */ |
||
433 | public static function underscore($name) { |
||
434 | return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name)); |
||
435 | } |
||
436 | } |
||
437 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.