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