Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/dependency-injection/ContainerBuilder.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 /* | |
4 * This file is part of the Symfony package. | |
5 * | |
6 * (c) Fabien Potencier <fabien@symfony.com> | |
7 * | |
8 * For the full copyright and license information, please view the LICENSE | |
9 * file that was distributed with this source code. | |
10 */ | |
11 | |
12 namespace Symfony\Component\DependencyInjection; | |
13 | |
14 use Symfony\Component\DependencyInjection\Compiler\Compiler; | |
15 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | |
16 use Symfony\Component\DependencyInjection\Compiler\PassConfig; | |
17 use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; | |
18 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; | |
19 use Symfony\Component\DependencyInjection\Exception\LogicException; | |
20 use Symfony\Component\DependencyInjection\Exception\RuntimeException; | |
21 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; | |
22 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; | |
23 use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; | |
24 use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; | |
25 use Symfony\Component\Config\Resource\FileResource; | |
26 use Symfony\Component\Config\Resource\ResourceInterface; | |
27 use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface; | |
28 use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; | |
29 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; | |
30 use Symfony\Component\ExpressionLanguage\Expression; | |
31 use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; | |
32 | |
33 /** | |
34 * ContainerBuilder is a DI container that provides an API to easily describe services. | |
35 * | |
36 * @author Fabien Potencier <fabien@symfony.com> | |
37 */ | |
38 class ContainerBuilder extends Container implements TaggedContainerInterface | |
39 { | |
40 /** | |
41 * @var ExtensionInterface[] | |
42 */ | |
43 private $extensions = array(); | |
44 | |
45 /** | |
46 * @var ExtensionInterface[] | |
47 */ | |
48 private $extensionsByNs = array(); | |
49 | |
50 /** | |
51 * @var Definition[] | |
52 */ | |
53 private $definitions = array(); | |
54 | |
55 /** | |
56 * @var Alias[] | |
57 */ | |
58 private $aliasDefinitions = array(); | |
59 | |
60 /** | |
61 * @var ResourceInterface[] | |
62 */ | |
63 private $resources = array(); | |
64 | |
65 private $extensionConfigs = array(); | |
66 | |
67 /** | |
68 * @var Compiler | |
69 */ | |
70 private $compiler; | |
71 | |
72 private $trackResources; | |
73 | |
74 /** | |
75 * @var InstantiatorInterface|null | |
76 */ | |
77 private $proxyInstantiator; | |
78 | |
79 /** | |
80 * @var ExpressionLanguage|null | |
81 */ | |
82 private $expressionLanguage; | |
83 | |
84 /** | |
85 * @var ExpressionFunctionProviderInterface[] | |
86 */ | |
87 private $expressionLanguageProviders = array(); | |
88 | |
89 public function __construct(ParameterBagInterface $parameterBag = null) | |
90 { | |
91 parent::__construct($parameterBag); | |
92 | |
93 $this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface'); | |
94 } | |
95 | |
96 /** | |
97 * @var string[] with tag names used by findTaggedServiceIds | |
98 */ | |
99 private $usedTags = array(); | |
100 | |
101 /** | |
102 * @var string[][] a map of env var names to their placeholders | |
103 */ | |
104 private $envPlaceholders = array(); | |
105 | |
106 /** | |
107 * @var int[] a map of env vars to their resolution counter | |
108 */ | |
109 private $envCounters = array(); | |
110 | |
111 /** | |
112 * Sets the track resources flag. | |
113 * | |
114 * If you are not using the loaders and therefore don't want | |
115 * to depend on the Config component, set this flag to false. | |
116 * | |
117 * @param bool $track true if you want to track resources, false otherwise | |
118 */ | |
119 public function setResourceTracking($track) | |
120 { | |
121 $this->trackResources = (bool) $track; | |
122 } | |
123 | |
124 /** | |
125 * Checks if resources are tracked. | |
126 * | |
127 * @return bool true if resources are tracked, false otherwise | |
128 */ | |
129 public function isTrackingResources() | |
130 { | |
131 return $this->trackResources; | |
132 } | |
133 | |
134 /** | |
135 * Sets the instantiator to be used when fetching proxies. | |
136 * | |
137 * @param InstantiatorInterface $proxyInstantiator | |
138 */ | |
139 public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator) | |
140 { | |
141 $this->proxyInstantiator = $proxyInstantiator; | |
142 } | |
143 | |
144 /** | |
145 * Registers an extension. | |
146 * | |
147 * @param ExtensionInterface $extension An extension instance | |
148 */ | |
149 public function registerExtension(ExtensionInterface $extension) | |
150 { | |
151 $this->extensions[$extension->getAlias()] = $extension; | |
152 | |
153 if (false !== $extension->getNamespace()) { | |
154 $this->extensionsByNs[$extension->getNamespace()] = $extension; | |
155 } | |
156 } | |
157 | |
158 /** | |
159 * Returns an extension by alias or namespace. | |
160 * | |
161 * @param string $name An alias or a namespace | |
162 * | |
163 * @return ExtensionInterface An extension instance | |
164 * | |
165 * @throws LogicException if the extension is not registered | |
166 */ | |
167 public function getExtension($name) | |
168 { | |
169 if (isset($this->extensions[$name])) { | |
170 return $this->extensions[$name]; | |
171 } | |
172 | |
173 if (isset($this->extensionsByNs[$name])) { | |
174 return $this->extensionsByNs[$name]; | |
175 } | |
176 | |
177 throw new LogicException(sprintf('Container extension "%s" is not registered', $name)); | |
178 } | |
179 | |
180 /** | |
181 * Returns all registered extensions. | |
182 * | |
183 * @return ExtensionInterface[] An array of ExtensionInterface | |
184 */ | |
185 public function getExtensions() | |
186 { | |
187 return $this->extensions; | |
188 } | |
189 | |
190 /** | |
191 * Checks if we have an extension. | |
192 * | |
193 * @param string $name The name of the extension | |
194 * | |
195 * @return bool If the extension exists | |
196 */ | |
197 public function hasExtension($name) | |
198 { | |
199 return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]); | |
200 } | |
201 | |
202 /** | |
203 * Returns an array of resources loaded to build this configuration. | |
204 * | |
205 * @return ResourceInterface[] An array of resources | |
206 */ | |
207 public function getResources() | |
208 { | |
209 return array_unique($this->resources); | |
210 } | |
211 | |
212 /** | |
213 * Adds a resource for this configuration. | |
214 * | |
215 * @param ResourceInterface $resource A resource instance | |
216 * | |
217 * @return $this | |
218 */ | |
219 public function addResource(ResourceInterface $resource) | |
220 { | |
221 if (!$this->trackResources) { | |
222 return $this; | |
223 } | |
224 | |
225 $this->resources[] = $resource; | |
226 | |
227 return $this; | |
228 } | |
229 | |
230 /** | |
231 * Sets the resources for this configuration. | |
232 * | |
233 * @param ResourceInterface[] $resources An array of resources | |
234 * | |
235 * @return $this | |
236 */ | |
237 public function setResources(array $resources) | |
238 { | |
239 if (!$this->trackResources) { | |
240 return $this; | |
241 } | |
242 | |
243 $this->resources = $resources; | |
244 | |
245 return $this; | |
246 } | |
247 | |
248 /** | |
249 * Adds the object class hierarchy as resources. | |
250 * | |
251 * @param object $object An object instance | |
252 * | |
253 * @return $this | |
254 */ | |
255 public function addObjectResource($object) | |
256 { | |
257 if ($this->trackResources) { | |
258 $this->addClassResource(new \ReflectionClass($object)); | |
259 } | |
260 | |
261 return $this; | |
262 } | |
263 | |
264 /** | |
265 * Adds the given class hierarchy as resources. | |
266 * | |
267 * @param \ReflectionClass $class | |
268 * | |
269 * @return $this | |
270 */ | |
271 public function addClassResource(\ReflectionClass $class) | |
272 { | |
273 if (!$this->trackResources) { | |
274 return $this; | |
275 } | |
276 | |
277 do { | |
278 if (is_file($class->getFileName())) { | |
279 $this->addResource(new FileResource($class->getFileName())); | |
280 } | |
281 } while ($class = $class->getParentClass()); | |
282 | |
283 return $this; | |
284 } | |
285 | |
286 /** | |
287 * Loads the configuration for an extension. | |
288 * | |
289 * @param string $extension The extension alias or namespace | |
290 * @param array $values An array of values that customizes the extension | |
291 * | |
292 * @return $this | |
293 * | |
294 * @throws BadMethodCallException When this ContainerBuilder is frozen | |
295 * @throws \LogicException if the container is frozen | |
296 */ | |
297 public function loadFromExtension($extension, array $values = array()) | |
298 { | |
299 if ($this->isFrozen()) { | |
300 throw new BadMethodCallException('Cannot load from an extension on a frozen container.'); | |
301 } | |
302 | |
303 $namespace = $this->getExtension($extension)->getAlias(); | |
304 | |
305 $this->extensionConfigs[$namespace][] = $values; | |
306 | |
307 return $this; | |
308 } | |
309 | |
310 /** | |
311 * Adds a compiler pass. | |
312 * | |
313 * @param CompilerPassInterface $pass A compiler pass | |
314 * @param string $type The type of compiler pass | |
315 * @param int $priority Used to sort the passes | |
316 * | |
317 * @return $this | |
318 */ | |
319 public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, $priority = 0*/) | |
320 { | |
321 if (func_num_args() >= 3) { | |
322 $priority = func_get_arg(2); | |
323 } else { | |
324 if (__CLASS__ !== get_class($this)) { | |
325 $r = new \ReflectionMethod($this, __FUNCTION__); | |
326 if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | |
327 @trigger_error(sprintf('Method %s() will have a third `$priority = 0` argument in version 4.0. Not defining it is deprecated since 3.2.', __METHOD__), E_USER_DEPRECATED); | |
328 } | |
329 } | |
330 | |
331 $priority = 0; | |
332 } | |
333 | |
334 $this->getCompiler()->addPass($pass, $type, $priority); | |
335 | |
336 $this->addObjectResource($pass); | |
337 | |
338 return $this; | |
339 } | |
340 | |
341 /** | |
342 * Returns the compiler pass config which can then be modified. | |
343 * | |
344 * @return PassConfig The compiler pass config | |
345 */ | |
346 public function getCompilerPassConfig() | |
347 { | |
348 return $this->getCompiler()->getPassConfig(); | |
349 } | |
350 | |
351 /** | |
352 * Returns the compiler. | |
353 * | |
354 * @return Compiler The compiler | |
355 */ | |
356 public function getCompiler() | |
357 { | |
358 if (null === $this->compiler) { | |
359 $this->compiler = new Compiler(); | |
360 } | |
361 | |
362 return $this->compiler; | |
363 } | |
364 | |
365 /** | |
366 * Sets a service. | |
367 * | |
368 * @param string $id The service identifier | |
369 * @param object $service The service instance | |
370 * | |
371 * @throws BadMethodCallException When this ContainerBuilder is frozen | |
372 */ | |
373 public function set($id, $service) | |
374 { | |
375 $id = strtolower($id); | |
376 | |
377 if ($this->isFrozen() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) { | |
378 // setting a synthetic service on a frozen container is alright | |
379 throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a frozen container is not allowed.', $id)); | |
380 } | |
381 | |
382 unset($this->definitions[$id], $this->aliasDefinitions[$id]); | |
383 | |
384 parent::set($id, $service); | |
385 } | |
386 | |
387 /** | |
388 * Removes a service definition. | |
389 * | |
390 * @param string $id The service identifier | |
391 */ | |
392 public function removeDefinition($id) | |
393 { | |
394 unset($this->definitions[strtolower($id)]); | |
395 } | |
396 | |
397 /** | |
398 * Returns true if the given service is defined. | |
399 * | |
400 * @param string $id The service identifier | |
401 * | |
402 * @return bool true if the service is defined, false otherwise | |
403 */ | |
404 public function has($id) | |
405 { | |
406 $id = strtolower($id); | |
407 | |
408 return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id); | |
409 } | |
410 | |
411 /** | |
412 * Gets a service. | |
413 * | |
414 * @param string $id The service identifier | |
415 * @param int $invalidBehavior The behavior when the service does not exist | |
416 * | |
417 * @return object The associated service | |
418 * | |
419 * @throws InvalidArgumentException when no definitions are available | |
420 * @throws ServiceCircularReferenceException When a circular reference is detected | |
421 * @throws ServiceNotFoundException When the service is not defined | |
422 * @throws \Exception | |
423 * | |
424 * @see Reference | |
425 */ | |
426 public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) | |
427 { | |
428 $id = strtolower($id); | |
429 | |
430 if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { | |
431 return $service; | |
432 } | |
433 | |
434 if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) { | |
435 return $this->get((string) $this->aliasDefinitions[$id], $invalidBehavior); | |
436 } | |
437 | |
438 try { | |
439 $definition = $this->getDefinition($id); | |
440 } catch (ServiceNotFoundException $e) { | |
441 if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { | |
442 return; | |
443 } | |
444 | |
445 throw $e; | |
446 } | |
447 | |
448 $this->loading[$id] = true; | |
449 | |
450 try { | |
451 $service = $this->createService($definition, $id); | |
452 } finally { | |
453 unset($this->loading[$id]); | |
454 } | |
455 | |
456 return $service; | |
457 } | |
458 | |
459 /** | |
460 * Merges a ContainerBuilder with the current ContainerBuilder configuration. | |
461 * | |
462 * Service definitions overrides the current defined ones. | |
463 * | |
464 * But for parameters, they are overridden by the current ones. It allows | |
465 * the parameters passed to the container constructor to have precedence | |
466 * over the loaded ones. | |
467 * | |
468 * $container = new ContainerBuilder(array('foo' => 'bar')); | |
469 * $loader = new LoaderXXX($container); | |
470 * $loader->load('resource_name'); | |
471 * $container->register('foo', new stdClass()); | |
472 * | |
473 * In the above example, even if the loaded resource defines a foo | |
474 * parameter, the value will still be 'bar' as defined in the ContainerBuilder | |
475 * constructor. | |
476 * | |
477 * @param ContainerBuilder $container The ContainerBuilder instance to merge | |
478 * | |
479 * @throws BadMethodCallException When this ContainerBuilder is frozen | |
480 */ | |
481 public function merge(ContainerBuilder $container) | |
482 { | |
483 if ($this->isFrozen()) { | |
484 throw new BadMethodCallException('Cannot merge on a frozen container.'); | |
485 } | |
486 | |
487 $this->addDefinitions($container->getDefinitions()); | |
488 $this->addAliases($container->getAliases()); | |
489 $this->getParameterBag()->add($container->getParameterBag()->all()); | |
490 | |
491 if ($this->trackResources) { | |
492 foreach ($container->getResources() as $resource) { | |
493 $this->addResource($resource); | |
494 } | |
495 } | |
496 | |
497 foreach ($this->extensions as $name => $extension) { | |
498 if (!isset($this->extensionConfigs[$name])) { | |
499 $this->extensionConfigs[$name] = array(); | |
500 } | |
501 | |
502 $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name)); | |
503 } | |
504 | |
505 if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) { | |
506 $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag()); | |
507 } | |
508 | |
509 foreach ($container->envCounters as $env => $count) { | |
510 if (!isset($this->envCounters[$env])) { | |
511 $this->envCounters[$env] = $count; | |
512 } else { | |
513 $this->envCounters[$env] += $count; | |
514 } | |
515 } | |
516 } | |
517 | |
518 /** | |
519 * Returns the configuration array for the given extension. | |
520 * | |
521 * @param string $name The name of the extension | |
522 * | |
523 * @return array An array of configuration | |
524 */ | |
525 public function getExtensionConfig($name) | |
526 { | |
527 if (!isset($this->extensionConfigs[$name])) { | |
528 $this->extensionConfigs[$name] = array(); | |
529 } | |
530 | |
531 return $this->extensionConfigs[$name]; | |
532 } | |
533 | |
534 /** | |
535 * Prepends a config array to the configs of the given extension. | |
536 * | |
537 * @param string $name The name of the extension | |
538 * @param array $config The config to set | |
539 */ | |
540 public function prependExtensionConfig($name, array $config) | |
541 { | |
542 if (!isset($this->extensionConfigs[$name])) { | |
543 $this->extensionConfigs[$name] = array(); | |
544 } | |
545 | |
546 array_unshift($this->extensionConfigs[$name], $config); | |
547 } | |
548 | |
549 /** | |
550 * Compiles the container. | |
551 * | |
552 * This method passes the container to compiler | |
553 * passes whose job is to manipulate and optimize | |
554 * the container. | |
555 * | |
556 * The main compiler passes roughly do four things: | |
557 * | |
558 * * The extension configurations are merged; | |
559 * * Parameter values are resolved; | |
560 * * The parameter bag is frozen; | |
561 * * Extension loading is disabled. | |
562 */ | |
563 public function compile() | |
564 { | |
565 $compiler = $this->getCompiler(); | |
566 | |
567 if ($this->trackResources) { | |
568 foreach ($compiler->getPassConfig()->getPasses() as $pass) { | |
569 $this->addObjectResource($pass); | |
570 } | |
571 } | |
572 | |
573 $compiler->compile($this); | |
574 | |
575 foreach ($this->definitions as $id => $definition) { | |
576 if (!$definition->isPublic()) { | |
577 $this->privates[$id] = true; | |
578 } | |
579 if ($this->trackResources && $definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) { | |
580 $this->addClassResource(new \ReflectionClass($class)); | |
581 } | |
582 } | |
583 | |
584 $this->extensionConfigs = array(); | |
585 $bag = $this->getParameterBag(); | |
586 | |
587 parent::compile(); | |
588 | |
589 $this->envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : array(); | |
590 } | |
591 | |
592 /** | |
593 * Gets all service ids. | |
594 * | |
595 * @return array An array of all defined service ids | |
596 */ | |
597 public function getServiceIds() | |
598 { | |
599 return array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds())); | |
600 } | |
601 | |
602 /** | |
603 * Adds the service aliases. | |
604 * | |
605 * @param array $aliases An array of aliases | |
606 */ | |
607 public function addAliases(array $aliases) | |
608 { | |
609 foreach ($aliases as $alias => $id) { | |
610 $this->setAlias($alias, $id); | |
611 } | |
612 } | |
613 | |
614 /** | |
615 * Sets the service aliases. | |
616 * | |
617 * @param array $aliases An array of aliases | |
618 */ | |
619 public function setAliases(array $aliases) | |
620 { | |
621 $this->aliasDefinitions = array(); | |
622 $this->addAliases($aliases); | |
623 } | |
624 | |
625 /** | |
626 * Sets an alias for an existing service. | |
627 * | |
628 * @param string $alias The alias to create | |
629 * @param string|Alias $id The service to alias | |
630 * | |
631 * @throws InvalidArgumentException if the id is not a string or an Alias | |
632 * @throws InvalidArgumentException if the alias is for itself | |
633 */ | |
634 public function setAlias($alias, $id) | |
635 { | |
636 $alias = strtolower($alias); | |
637 | |
638 if (is_string($id)) { | |
639 $id = new Alias($id); | |
640 } elseif (!$id instanceof Alias) { | |
641 throw new InvalidArgumentException('$id must be a string, or an Alias object.'); | |
642 } | |
643 | |
644 if ($alias === (string) $id) { | |
645 throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias)); | |
646 } | |
647 | |
648 unset($this->definitions[$alias]); | |
649 | |
650 $this->aliasDefinitions[$alias] = $id; | |
651 } | |
652 | |
653 /** | |
654 * Removes an alias. | |
655 * | |
656 * @param string $alias The alias to remove | |
657 */ | |
658 public function removeAlias($alias) | |
659 { | |
660 unset($this->aliasDefinitions[strtolower($alias)]); | |
661 } | |
662 | |
663 /** | |
664 * Returns true if an alias exists under the given identifier. | |
665 * | |
666 * @param string $id The service identifier | |
667 * | |
668 * @return bool true if the alias exists, false otherwise | |
669 */ | |
670 public function hasAlias($id) | |
671 { | |
672 return isset($this->aliasDefinitions[strtolower($id)]); | |
673 } | |
674 | |
675 /** | |
676 * Gets all defined aliases. | |
677 * | |
678 * @return Alias[] An array of aliases | |
679 */ | |
680 public function getAliases() | |
681 { | |
682 return $this->aliasDefinitions; | |
683 } | |
684 | |
685 /** | |
686 * Gets an alias. | |
687 * | |
688 * @param string $id The service identifier | |
689 * | |
690 * @return Alias An Alias instance | |
691 * | |
692 * @throws InvalidArgumentException if the alias does not exist | |
693 */ | |
694 public function getAlias($id) | |
695 { | |
696 $id = strtolower($id); | |
697 | |
698 if (!isset($this->aliasDefinitions[$id])) { | |
699 throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id)); | |
700 } | |
701 | |
702 return $this->aliasDefinitions[$id]; | |
703 } | |
704 | |
705 /** | |
706 * Registers a service definition. | |
707 * | |
708 * This methods allows for simple registration of service definition | |
709 * with a fluid interface. | |
710 * | |
711 * @param string $id The service identifier | |
712 * @param string $class The service class | |
713 * | |
714 * @return Definition A Definition instance | |
715 */ | |
716 public function register($id, $class = null) | |
717 { | |
718 return $this->setDefinition($id, new Definition($class)); | |
719 } | |
720 | |
721 /** | |
722 * Adds the service definitions. | |
723 * | |
724 * @param Definition[] $definitions An array of service definitions | |
725 */ | |
726 public function addDefinitions(array $definitions) | |
727 { | |
728 foreach ($definitions as $id => $definition) { | |
729 $this->setDefinition($id, $definition); | |
730 } | |
731 } | |
732 | |
733 /** | |
734 * Sets the service definitions. | |
735 * | |
736 * @param Definition[] $definitions An array of service definitions | |
737 */ | |
738 public function setDefinitions(array $definitions) | |
739 { | |
740 $this->definitions = array(); | |
741 $this->addDefinitions($definitions); | |
742 } | |
743 | |
744 /** | |
745 * Gets all service definitions. | |
746 * | |
747 * @return Definition[] An array of Definition instances | |
748 */ | |
749 public function getDefinitions() | |
750 { | |
751 return $this->definitions; | |
752 } | |
753 | |
754 /** | |
755 * Sets a service definition. | |
756 * | |
757 * @param string $id The service identifier | |
758 * @param Definition $definition A Definition instance | |
759 * | |
760 * @return Definition the service definition | |
761 * | |
762 * @throws BadMethodCallException When this ContainerBuilder is frozen | |
763 */ | |
764 public function setDefinition($id, Definition $definition) | |
765 { | |
766 if ($this->isFrozen()) { | |
767 throw new BadMethodCallException('Adding definition to a frozen container is not allowed'); | |
768 } | |
769 | |
770 $id = strtolower($id); | |
771 | |
772 unset($this->aliasDefinitions[$id]); | |
773 | |
774 return $this->definitions[$id] = $definition; | |
775 } | |
776 | |
777 /** | |
778 * Returns true if a service definition exists under the given identifier. | |
779 * | |
780 * @param string $id The service identifier | |
781 * | |
782 * @return bool true if the service definition exists, false otherwise | |
783 */ | |
784 public function hasDefinition($id) | |
785 { | |
786 return isset($this->definitions[strtolower($id)]); | |
787 } | |
788 | |
789 /** | |
790 * Gets a service definition. | |
791 * | |
792 * @param string $id The service identifier | |
793 * | |
794 * @return Definition A Definition instance | |
795 * | |
796 * @throws ServiceNotFoundException if the service definition does not exist | |
797 */ | |
798 public function getDefinition($id) | |
799 { | |
800 $id = strtolower($id); | |
801 | |
802 if (!isset($this->definitions[$id])) { | |
803 throw new ServiceNotFoundException($id); | |
804 } | |
805 | |
806 return $this->definitions[$id]; | |
807 } | |
808 | |
809 /** | |
810 * Gets a service definition by id or alias. | |
811 * | |
812 * The method "unaliases" recursively to return a Definition instance. | |
813 * | |
814 * @param string $id The service identifier or alias | |
815 * | |
816 * @return Definition A Definition instance | |
817 * | |
818 * @throws ServiceNotFoundException if the service definition does not exist | |
819 */ | |
820 public function findDefinition($id) | |
821 { | |
822 $id = strtolower($id); | |
823 | |
824 while (isset($this->aliasDefinitions[$id])) { | |
825 $id = (string) $this->aliasDefinitions[$id]; | |
826 } | |
827 | |
828 return $this->getDefinition($id); | |
829 } | |
830 | |
831 /** | |
832 * Creates a service for a service definition. | |
833 * | |
834 * @param Definition $definition A service definition instance | |
835 * @param string $id The service identifier | |
836 * @param bool $tryProxy Whether to try proxying the service with a lazy proxy | |
837 * | |
838 * @return object The service described by the service definition | |
839 * | |
840 * @throws RuntimeException When the factory definition is incomplete | |
841 * @throws RuntimeException When the service is a synthetic service | |
842 * @throws InvalidArgumentException When configure callable is not callable | |
843 */ | |
844 private function createService(Definition $definition, $id, $tryProxy = true) | |
845 { | |
846 if ($definition instanceof DefinitionDecorator) { | |
847 throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id)); | |
848 } | |
849 | |
850 if ($definition->isSynthetic()) { | |
851 throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); | |
852 } | |
853 | |
854 if ($definition->isDeprecated()) { | |
855 @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED); | |
856 } | |
857 | |
858 if ($tryProxy && $definition->isLazy()) { | |
859 $proxy = $this | |
860 ->getProxyInstantiator() | |
861 ->instantiateProxy( | |
862 $this, | |
863 $definition, | |
864 $id, function () use ($definition, $id) { | |
865 return $this->createService($definition, $id, false); | |
866 } | |
867 ); | |
868 $this->shareService($definition, $proxy, $id); | |
869 | |
870 return $proxy; | |
871 } | |
872 | |
873 $parameterBag = $this->getParameterBag(); | |
874 | |
875 if (null !== $definition->getFile()) { | |
876 require_once $parameterBag->resolveValue($definition->getFile()); | |
877 } | |
878 | |
879 $arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments()))); | |
880 | |
881 if (null !== $factory = $definition->getFactory()) { | |
882 if (is_array($factory)) { | |
883 $factory = array($this->resolveServices($parameterBag->resolveValue($factory[0])), $factory[1]); | |
884 } elseif (!is_string($factory)) { | |
885 throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); | |
886 } | |
887 | |
888 $service = call_user_func_array($factory, $arguments); | |
889 | |
890 if (!$definition->isDeprecated() && is_array($factory) && is_string($factory[0])) { | |
891 $r = new \ReflectionClass($factory[0]); | |
892 | |
893 if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) { | |
894 @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name), E_USER_DEPRECATED); | |
895 } | |
896 } | |
897 } else { | |
898 $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass())); | |
899 | |
900 $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); | |
901 | |
902 if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) { | |
903 @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED); | |
904 } | |
905 } | |
906 | |
907 if ($tryProxy || !$definition->isLazy()) { | |
908 // share only if proxying failed, or if not a proxy | |
909 $this->shareService($definition, $service, $id); | |
910 } | |
911 | |
912 $properties = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties()))); | |
913 foreach ($properties as $name => $value) { | |
914 $service->$name = $value; | |
915 } | |
916 | |
917 foreach ($definition->getMethodCalls() as $call) { | |
918 $this->callMethod($service, $call); | |
919 } | |
920 | |
921 if ($callable = $definition->getConfigurator()) { | |
922 if (is_array($callable)) { | |
923 $callable[0] = $parameterBag->resolveValue($callable[0]); | |
924 | |
925 if ($callable[0] instanceof Reference) { | |
926 $callable[0] = $this->get((string) $callable[0], $callable[0]->getInvalidBehavior()); | |
927 } elseif ($callable[0] instanceof Definition) { | |
928 $callable[0] = $this->createService($callable[0], null); | |
929 } | |
930 } | |
931 | |
932 if (!is_callable($callable)) { | |
933 throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_class($service))); | |
934 } | |
935 | |
936 call_user_func($callable, $service); | |
937 } | |
938 | |
939 return $service; | |
940 } | |
941 | |
942 /** | |
943 * Replaces service references by the real service instance and evaluates expressions. | |
944 * | |
945 * @param mixed $value A value | |
946 * | |
947 * @return mixed The same value with all service references replaced by | |
948 * the real service instances and all expressions evaluated | |
949 */ | |
950 public function resolveServices($value) | |
951 { | |
952 if (is_array($value)) { | |
953 foreach ($value as $k => $v) { | |
954 $value[$k] = $this->resolveServices($v); | |
955 } | |
956 } elseif ($value instanceof Reference) { | |
957 $value = $this->get((string) $value, $value->getInvalidBehavior()); | |
958 } elseif ($value instanceof Definition) { | |
959 $value = $this->createService($value, null); | |
960 } elseif ($value instanceof Expression) { | |
961 $value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this)); | |
962 } | |
963 | |
964 return $value; | |
965 } | |
966 | |
967 /** | |
968 * Returns service ids for a given tag. | |
969 * | |
970 * Example: | |
971 * | |
972 * $container->register('foo')->addTag('my.tag', array('hello' => 'world')); | |
973 * | |
974 * $serviceIds = $container->findTaggedServiceIds('my.tag'); | |
975 * foreach ($serviceIds as $serviceId => $tags) { | |
976 * foreach ($tags as $tag) { | |
977 * echo $tag['hello']; | |
978 * } | |
979 * } | |
980 * | |
981 * @param string $name The tag name | |
982 * | |
983 * @return array An array of tags with the tagged service as key, holding a list of attribute arrays | |
984 */ | |
985 public function findTaggedServiceIds($name) | |
986 { | |
987 $this->usedTags[] = $name; | |
988 $tags = array(); | |
989 foreach ($this->getDefinitions() as $id => $definition) { | |
990 if ($definition->hasTag($name)) { | |
991 $tags[$id] = $definition->getTag($name); | |
992 } | |
993 } | |
994 | |
995 return $tags; | |
996 } | |
997 | |
998 /** | |
999 * Returns all tags the defined services use. | |
1000 * | |
1001 * @return array An array of tags | |
1002 */ | |
1003 public function findTags() | |
1004 { | |
1005 $tags = array(); | |
1006 foreach ($this->getDefinitions() as $id => $definition) { | |
1007 $tags = array_merge(array_keys($definition->getTags()), $tags); | |
1008 } | |
1009 | |
1010 return array_unique($tags); | |
1011 } | |
1012 | |
1013 /** | |
1014 * Returns all tags not queried by findTaggedServiceIds. | |
1015 * | |
1016 * @return string[] An array of tags | |
1017 */ | |
1018 public function findUnusedTags() | |
1019 { | |
1020 return array_values(array_diff($this->findTags(), $this->usedTags)); | |
1021 } | |
1022 | |
1023 public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) | |
1024 { | |
1025 $this->expressionLanguageProviders[] = $provider; | |
1026 } | |
1027 | |
1028 /** | |
1029 * @return ExpressionFunctionProviderInterface[] | |
1030 */ | |
1031 public function getExpressionLanguageProviders() | |
1032 { | |
1033 return $this->expressionLanguageProviders; | |
1034 } | |
1035 | |
1036 /** | |
1037 * Resolves env parameter placeholders in a string or an array. | |
1038 * | |
1039 * @param mixed $value The value to resolve | |
1040 * @param string|null $format A sprintf() format to use as replacement for env placeholders or null to use the default parameter format | |
1041 * @param array &$usedEnvs Env vars found while resolving are added to this array | |
1042 * | |
1043 * @return string The string with env parameters resolved | |
1044 */ | |
1045 public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null) | |
1046 { | |
1047 if (null === $format) { | |
1048 $format = '%%env(%s)%%'; | |
1049 } | |
1050 | |
1051 if (is_array($value)) { | |
1052 $result = array(); | |
1053 foreach ($value as $k => $v) { | |
1054 $result[$this->resolveEnvPlaceholders($k, $format, $usedEnvs)] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs); | |
1055 } | |
1056 | |
1057 return $result; | |
1058 } | |
1059 | |
1060 if (!is_string($value)) { | |
1061 return $value; | |
1062 } | |
1063 | |
1064 $bag = $this->getParameterBag(); | |
1065 $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders; | |
1066 | |
1067 foreach ($envPlaceholders as $env => $placeholders) { | |
1068 foreach ($placeholders as $placeholder) { | |
1069 if (false !== stripos($value, $placeholder)) { | |
1070 $value = str_ireplace($placeholder, sprintf($format, $env), $value); | |
1071 $usedEnvs[$env] = $env; | |
1072 $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1; | |
1073 } | |
1074 } | |
1075 } | |
1076 | |
1077 return $value; | |
1078 } | |
1079 | |
1080 /** | |
1081 * Get statistics about env usage. | |
1082 * | |
1083 * @return int[] The number of time each env vars has been resolved | |
1084 */ | |
1085 public function getEnvCounters() | |
1086 { | |
1087 $bag = $this->getParameterBag(); | |
1088 $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders; | |
1089 | |
1090 foreach ($envPlaceholders as $env => $placeholders) { | |
1091 if (!isset($this->envCounters[$env])) { | |
1092 $this->envCounters[$env] = 0; | |
1093 } | |
1094 } | |
1095 | |
1096 return $this->envCounters; | |
1097 } | |
1098 | |
1099 /** | |
1100 * Returns the Service Conditionals. | |
1101 * | |
1102 * @param mixed $value An array of conditionals to return | |
1103 * | |
1104 * @return array An array of Service conditionals | |
1105 */ | |
1106 public static function getServiceConditionals($value) | |
1107 { | |
1108 $services = array(); | |
1109 | |
1110 if (is_array($value)) { | |
1111 foreach ($value as $v) { | |
1112 $services = array_unique(array_merge($services, self::getServiceConditionals($v))); | |
1113 } | |
1114 } elseif ($value instanceof Reference && $value->getInvalidBehavior() === ContainerInterface::IGNORE_ON_INVALID_REFERENCE) { | |
1115 $services[] = (string) $value; | |
1116 } | |
1117 | |
1118 return $services; | |
1119 } | |
1120 | |
1121 /** | |
1122 * Retrieves the currently set proxy instantiator or instantiates one. | |
1123 * | |
1124 * @return InstantiatorInterface | |
1125 */ | |
1126 private function getProxyInstantiator() | |
1127 { | |
1128 if (!$this->proxyInstantiator) { | |
1129 $this->proxyInstantiator = new RealServiceInstantiator(); | |
1130 } | |
1131 | |
1132 return $this->proxyInstantiator; | |
1133 } | |
1134 | |
1135 private function callMethod($service, $call) | |
1136 { | |
1137 $services = self::getServiceConditionals($call[1]); | |
1138 | |
1139 foreach ($services as $s) { | |
1140 if (!$this->has($s)) { | |
1141 return; | |
1142 } | |
1143 } | |
1144 | |
1145 call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])))); | |
1146 } | |
1147 | |
1148 /** | |
1149 * Shares a given service in the container. | |
1150 * | |
1151 * @param Definition $definition | |
1152 * @param mixed $service | |
1153 * @param string|null $id | |
1154 */ | |
1155 private function shareService(Definition $definition, $service, $id) | |
1156 { | |
1157 if (null !== $id && $definition->isShared()) { | |
1158 $this->services[strtolower($id)] = $service; | |
1159 } | |
1160 } | |
1161 | |
1162 private function getExpressionLanguage() | |
1163 { | |
1164 if (null === $this->expressionLanguage) { | |
1165 if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { | |
1166 throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); | |
1167 } | |
1168 $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders); | |
1169 } | |
1170 | |
1171 return $this->expressionLanguage; | |
1172 } | |
1173 } |