Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / vendor / symfony / dependency-injection / Container.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\Exception\EnvNotFoundException;
15 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
16 use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
17 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
18 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
19 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
20 use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
21 use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
22
23 /**
24  * Container is a dependency injection container.
25  *
26  * It gives access to object instances (services).
27  * Services and parameters are simple key/pair stores.
28  * The container can have four possible behaviors when a service
29  * does not exist (or is not initialized for the last case):
30  *
31  *  * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default)
32  *  * NULL_ON_INVALID_REFERENCE:      Returns null
33  *  * IGNORE_ON_INVALID_REFERENCE:    Ignores the wrapping command asking for the reference
34  *                                    (for instance, ignore a setter if the service does not exist)
35  *  * IGNORE_ON_UNINITIALIZED_REFERENCE: Ignores/returns null for uninitialized services or invalid references
36  *
37  * @author Fabien Potencier <fabien@symfony.com>
38  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
39  */
40 class Container implements ResettableContainerInterface
41 {
42     protected $parameterBag;
43     protected $services = array();
44     protected $fileMap = array();
45     protected $methodMap = array();
46     protected $aliases = array();
47     protected $loading = array();
48     protected $resolving = array();
49     protected $syntheticIds = array();
50
51     /**
52      * @internal
53      */
54     protected $privates = array();
55
56     /**
57      * @internal
58      */
59     protected $normalizedIds = array();
60
61     private $underscoreMap = array('_' => '', '.' => '_', '\\' => '_');
62     private $envCache = array();
63     private $compiled = false;
64     private $getEnv;
65
66     public function __construct(ParameterBagInterface $parameterBag = null)
67     {
68         $this->parameterBag = $parameterBag ?: new EnvPlaceholderParameterBag();
69     }
70
71     /**
72      * Compiles the container.
73      *
74      * This method does two things:
75      *
76      *  * Parameter values are resolved;
77      *  * The parameter bag is frozen.
78      */
79     public function compile()
80     {
81         $this->parameterBag->resolve();
82
83         $this->parameterBag = new FrozenParameterBag($this->parameterBag->all());
84
85         $this->compiled = true;
86     }
87
88     /**
89      * Returns true if the container is compiled.
90      *
91      * @return bool
92      */
93     public function isCompiled()
94     {
95         return $this->compiled;
96     }
97
98     /**
99      * Returns true if the container parameter bag are frozen.
100      *
101      * @deprecated since version 3.3, to be removed in 4.0.
102      *
103      * @return bool true if the container parameter bag are frozen, false otherwise
104      */
105     public function isFrozen()
106     {
107         @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED);
108
109         return $this->parameterBag instanceof FrozenParameterBag;
110     }
111
112     /**
113      * Gets the service container parameter bag.
114      *
115      * @return ParameterBagInterface A ParameterBagInterface instance
116      */
117     public function getParameterBag()
118     {
119         return $this->parameterBag;
120     }
121
122     /**
123      * Gets a parameter.
124      *
125      * @param string $name The parameter name
126      *
127      * @return mixed The parameter value
128      *
129      * @throws InvalidArgumentException if the parameter is not defined
130      */
131     public function getParameter($name)
132     {
133         return $this->parameterBag->get($name);
134     }
135
136     /**
137      * Checks if a parameter exists.
138      *
139      * @param string $name The parameter name
140      *
141      * @return bool The presence of parameter in container
142      */
143     public function hasParameter($name)
144     {
145         return $this->parameterBag->has($name);
146     }
147
148     /**
149      * Sets a parameter.
150      *
151      * @param string $name  The parameter name
152      * @param mixed  $value The parameter value
153      */
154     public function setParameter($name, $value)
155     {
156         $this->parameterBag->set($name, $value);
157     }
158
159     /**
160      * Sets a service.
161      *
162      * Setting a service to null resets the service: has() returns false and get()
163      * behaves in the same way as if the service was never created.
164      *
165      * @param string $id      The service identifier
166      * @param object $service The service instance
167      */
168     public function set($id, $service)
169     {
170         // Runs the internal initializer; used by the dumped container to include always-needed files
171         if (isset($this->privates['service_container']) && $this->privates['service_container'] instanceof \Closure) {
172             $initialize = $this->privates['service_container'];
173             unset($this->privates['service_container']);
174             $initialize();
175         }
176
177         $id = $this->normalizeId($id);
178
179         if ('service_container' === $id) {
180             throw new InvalidArgumentException('You cannot set service "service_container".');
181         }
182
183         if (isset($this->privates[$id]) || !(isset($this->fileMap[$id]) || isset($this->methodMap[$id]))) {
184             if (!isset($this->privates[$id]) && !isset($this->getRemovedIds()[$id])) {
185                 // no-op
186             } elseif (null === $service) {
187                 @trigger_error(sprintf('The "%s" service is private, unsetting it is deprecated since Symfony 3.2 and will fail in 4.0.', $id), E_USER_DEPRECATED);
188                 unset($this->privates[$id]);
189             } else {
190                 @trigger_error(sprintf('The "%s" service is private, replacing it is deprecated since Symfony 3.2 and will fail in 4.0.', $id), E_USER_DEPRECATED);
191             }
192         } elseif (isset($this->services[$id])) {
193             if (null === $service) {
194                 @trigger_error(sprintf('The "%s" service is already initialized, unsetting it is deprecated since Symfony 3.3 and will fail in 4.0.', $id), E_USER_DEPRECATED);
195             } else {
196                 @trigger_error(sprintf('The "%s" service is already initialized, replacing it is deprecated since Symfony 3.3 and will fail in 4.0.', $id), E_USER_DEPRECATED);
197             }
198         }
199
200         if (isset($this->aliases[$id])) {
201             unset($this->aliases[$id]);
202         }
203
204         if (null === $service) {
205             unset($this->services[$id]);
206
207             return;
208         }
209
210         $this->services[$id] = $service;
211     }
212
213     /**
214      * Returns true if the given service is defined.
215      *
216      * @param string $id The service identifier
217      *
218      * @return bool true if the service is defined, false otherwise
219      */
220     public function has($id)
221     {
222         for ($i = 2;;) {
223             if (isset($this->privates[$id])) {
224                 @trigger_error(sprintf('The "%s" service is private, checking for its existence is deprecated since Symfony 3.2 and will fail in 4.0.', $id), E_USER_DEPRECATED);
225             }
226             if (isset($this->aliases[$id])) {
227                 $id = $this->aliases[$id];
228             }
229             if (isset($this->services[$id])) {
230                 return true;
231             }
232             if ('service_container' === $id) {
233                 return true;
234             }
235
236             if (isset($this->fileMap[$id]) || isset($this->methodMap[$id])) {
237                 return true;
238             }
239
240             if (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
241                 $id = $normalizedId;
242                 continue;
243             }
244
245             // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
246             // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
247             if (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, 'get'.strtr($id, $this->underscoreMap).'Service')) {
248                 @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED);
249
250                 return true;
251             }
252
253             return false;
254         }
255     }
256
257     /**
258      * Gets a service.
259      *
260      * If a service is defined both through a set() method and
261      * with a get{$id}Service() method, the former has always precedence.
262      *
263      * @param string $id              The service identifier
264      * @param int    $invalidBehavior The behavior when the service does not exist
265      *
266      * @return object The associated service
267      *
268      * @throws ServiceCircularReferenceException When a circular reference is detected
269      * @throws ServiceNotFoundException          When the service is not defined
270      * @throws \Exception                        if an exception has been thrown when the service has been resolved
271      *
272      * @see Reference
273      */
274     public function get($id, $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERENCE */ 1)
275     {
276         // Attempt to retrieve the service by checking first aliases then
277         // available services. Service IDs are case insensitive, however since
278         // this method can be called thousands of times during a request, avoid
279         // calling $this->normalizeId($id) unless necessary.
280         for ($i = 2;;) {
281             if (isset($this->privates[$id])) {
282                 @trigger_error(sprintf('The "%s" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.', $id), E_USER_DEPRECATED);
283             }
284             if (isset($this->aliases[$id])) {
285                 $id = $this->aliases[$id];
286             }
287
288             // Re-use shared service instance if it exists.
289             if (isset($this->services[$id])) {
290                 return $this->services[$id];
291             }
292             if ('service_container' === $id) {
293                 return $this;
294             }
295
296             if (isset($this->loading[$id])) {
297                 throw new ServiceCircularReferenceException($id, array_keys($this->loading));
298             }
299
300             $this->loading[$id] = true;
301
302             try {
303                 if (isset($this->fileMap[$id])) {
304                     return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->load($this->fileMap[$id]);
305                 } elseif (isset($this->methodMap[$id])) {
306                     return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$this->methodMap[$id]}();
307                 } elseif (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
308                     unset($this->loading[$id]);
309                     $id = $normalizedId;
310                     continue;
311                 } elseif (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) {
312                     // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
313                     // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
314                     @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED);
315
316                     return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$method}();
317                 }
318
319                 break;
320             } catch (\Exception $e) {
321                 unset($this->services[$id]);
322
323                 throw $e;
324             } finally {
325                 unset($this->loading[$id]);
326             }
327         }
328
329         if (/* self::EXCEPTION_ON_INVALID_REFERENCE */ 1 === $invalidBehavior) {
330             if (!$id) {
331                 throw new ServiceNotFoundException($id);
332             }
333             if (isset($this->syntheticIds[$id])) {
334                 throw new ServiceNotFoundException($id, null, null, array(), sprintf('The "%s" service is synthetic, it needs to be set at boot time before it can be used.', $id));
335             }
336             if (isset($this->getRemovedIds()[$id])) {
337                 throw new ServiceNotFoundException($id, null, null, array(), sprintf('The "%s" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.', $id));
338             }
339
340             $alternatives = array();
341             foreach ($this->getServiceIds() as $knownId) {
342                 $lev = levenshtein($id, $knownId);
343                 if ($lev <= strlen($id) / 3 || false !== strpos($knownId, $id)) {
344                     $alternatives[] = $knownId;
345                 }
346             }
347
348             throw new ServiceNotFoundException($id, null, null, $alternatives);
349         }
350     }
351
352     /**
353      * Returns true if the given service has actually been initialized.
354      *
355      * @param string $id The service identifier
356      *
357      * @return bool true if service has already been initialized, false otherwise
358      */
359     public function initialized($id)
360     {
361         $id = $this->normalizeId($id);
362
363         if (isset($this->privates[$id])) {
364             @trigger_error(sprintf('Checking for the initialization of the "%s" private service is deprecated since Symfony 3.4 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED);
365         }
366
367         if (isset($this->aliases[$id])) {
368             $id = $this->aliases[$id];
369         }
370
371         if ('service_container' === $id) {
372             return false;
373         }
374
375         return isset($this->services[$id]);
376     }
377
378     /**
379      * {@inheritdoc}
380      */
381     public function reset()
382     {
383         $this->services = array();
384     }
385
386     /**
387      * Gets all service ids.
388      *
389      * @return array An array of all defined service ids
390      */
391     public function getServiceIds()
392     {
393         $ids = array();
394
395         if (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class) {
396             // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
397             // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
398             @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED);
399
400             foreach (get_class_methods($this) as $method) {
401                 if (preg_match('/^get(.+)Service$/', $method, $match)) {
402                     $ids[] = self::underscore($match[1]);
403                 }
404             }
405         }
406         $ids[] = 'service_container';
407
408         return array_unique(array_merge($ids, array_keys($this->methodMap), array_keys($this->fileMap), array_keys($this->services)));
409     }
410
411     /**
412      * Gets service ids that existed at compile time.
413      *
414      * @return array
415      */
416     public function getRemovedIds()
417     {
418         return array();
419     }
420
421     /**
422      * Camelizes a string.
423      *
424      * @param string $id A string to camelize
425      *
426      * @return string The camelized string
427      */
428     public static function camelize($id)
429     {
430         return strtr(ucwords(strtr($id, array('_' => ' ', '.' => '_ ', '\\' => '_ '))), array(' ' => ''));
431     }
432
433     /**
434      * A string to underscore.
435      *
436      * @param string $id The string to underscore
437      *
438      * @return string The underscored string
439      */
440     public static function underscore($id)
441     {
442         return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), str_replace('_', '.', $id)));
443     }
444
445     /**
446      * Creates a service by requiring its factory file.
447      *
448      * @return object The service created by the file
449      */
450     protected function load($file)
451     {
452         return require $file;
453     }
454
455     /**
456      * Fetches a variable from the environment.
457      *
458      * @param string $name The name of the environment variable
459      *
460      * @return mixed The value to use for the provided environment variable name
461      *
462      * @throws EnvNotFoundException When the environment variable is not found and has no default value
463      */
464     protected function getEnv($name)
465     {
466         if (isset($this->resolving[$envName = "env($name)"])) {
467             throw new ParameterCircularReferenceException(array_keys($this->resolving));
468         }
469         if (isset($this->envCache[$name]) || array_key_exists($name, $this->envCache)) {
470             return $this->envCache[$name];
471         }
472         if (!$this->has($id = 'container.env_var_processors_locator')) {
473             $this->set($id, new ServiceLocator(array()));
474         }
475         if (!$this->getEnv) {
476             $this->getEnv = new \ReflectionMethod($this, __FUNCTION__);
477             $this->getEnv->setAccessible(true);
478             $this->getEnv = $this->getEnv->getClosure($this);
479         }
480         $processors = $this->get($id);
481
482         if (false !== $i = strpos($name, ':')) {
483             $prefix = substr($name, 0, $i);
484             $localName = substr($name, 1 + $i);
485         } else {
486             $prefix = 'string';
487             $localName = $name;
488         }
489         $processor = $processors->has($prefix) ? $processors->get($prefix) : new EnvVarProcessor($this);
490
491         $this->resolving[$envName] = true;
492         try {
493             return $this->envCache[$name] = $processor->getEnv($prefix, $localName, $this->getEnv);
494         } finally {
495             unset($this->resolving[$envName]);
496         }
497     }
498
499     /**
500      * Returns the case sensitive id used at registration time.
501      *
502      * @param string $id
503      *
504      * @return string
505      *
506      * @internal
507      */
508     public function normalizeId($id)
509     {
510         if (!\is_string($id)) {
511             $id = (string) $id;
512         }
513         if (isset($this->normalizedIds[$normalizedId = strtolower($id)])) {
514             $normalizedId = $this->normalizedIds[$normalizedId];
515             if ($id !== $normalizedId) {
516                 @trigger_error(sprintf('Service identifiers will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.3.', $id, $normalizedId), E_USER_DEPRECATED);
517             }
518         } else {
519             $normalizedId = $this->normalizedIds[$normalizedId] = $id;
520         }
521
522         return $normalizedId;
523     }
524
525     private function __clone()
526     {
527     }
528 }