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 }