Fix bug in style changes for the Use cases on the live site.
[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\InactiveScopeException;
15 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
16 use Symfony\Component\DependencyInjection\Exception\LogicException;
17 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
18 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
19 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
20 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
21 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
22 use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
23
24 /**
25  * Container is a dependency injection container.
26  *
27  * It gives access to object instances (services).
28  *
29  * Services and parameters are simple key/pair stores.
30  *
31  * Parameter and service keys are case insensitive.
32  *
33  * A service id can contain lowercased letters, digits, underscores, and dots.
34  * Underscores are used to separate words, and dots to group services
35  * under namespaces:
36  *
37  * <ul>
38  *   <li>request</li>
39  *   <li>mysql_session_storage</li>
40  *   <li>symfony.mysql_session_storage</li>
41  * </ul>
42  *
43  * A service can also be defined by creating a method named
44  * getXXXService(), where XXX is the camelized version of the id:
45  *
46  * <ul>
47  *   <li>request -> getRequestService()</li>
48  *   <li>mysql_session_storage -> getMysqlSessionStorageService()</li>
49  *   <li>symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()</li>
50  * </ul>
51  *
52  * The container can have three possible behaviors when a service does not exist:
53  *
54  *  * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default)
55  *  * NULL_ON_INVALID_REFERENCE:      Returns null
56  *  * IGNORE_ON_INVALID_REFERENCE:    Ignores the wrapping command asking for the reference
57  *                                    (for instance, ignore a setter if the service does not exist)
58  *
59  * @author Fabien Potencier <fabien@symfony.com>
60  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
61  */
62 class Container implements IntrospectableContainerInterface, ResettableContainerInterface
63 {
64     /**
65      * @var ParameterBagInterface
66      */
67     protected $parameterBag;
68
69     protected $services = array();
70     protected $methodMap = array();
71     protected $aliases = array();
72     protected $scopes = array();
73     protected $scopeChildren = array();
74     protected $scopedServices = array();
75     protected $scopeStacks = array();
76     protected $loading = array();
77
78     private $underscoreMap = array('_' => '', '.' => '_', '\\' => '_');
79
80     /**
81      * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance
82      */
83     public function __construct(ParameterBagInterface $parameterBag = null)
84     {
85         $this->parameterBag = $parameterBag ?: new ParameterBag();
86     }
87
88     /**
89      * Compiles the container.
90      *
91      * This method does two things:
92      *
93      *  * Parameter values are resolved;
94      *  * The parameter bag is frozen.
95      */
96     public function compile()
97     {
98         $this->parameterBag->resolve();
99
100         $this->parameterBag = new FrozenParameterBag($this->parameterBag->all());
101     }
102
103     /**
104      * Returns true if the container parameter bag are frozen.
105      *
106      * @return bool true if the container parameter bag are frozen, false otherwise
107      */
108     public function isFrozen()
109     {
110         return $this->parameterBag instanceof FrozenParameterBag;
111     }
112
113     /**
114      * Gets the service container parameter bag.
115      *
116      * @return ParameterBagInterface A ParameterBagInterface instance
117      */
118     public function getParameterBag()
119     {
120         return $this->parameterBag;
121     }
122
123     /**
124      * Gets a parameter.
125      *
126      * @param string $name The parameter name
127      *
128      * @return mixed The parameter value
129      *
130      * @throws InvalidArgumentException if the parameter is not defined
131      */
132     public function getParameter($name)
133     {
134         return $this->parameterBag->get($name);
135     }
136
137     /**
138      * Checks if a parameter exists.
139      *
140      * @param string $name The parameter name
141      *
142      * @return bool The presence of parameter in container
143      */
144     public function hasParameter($name)
145     {
146         return $this->parameterBag->has($name);
147     }
148
149     /**
150      * Sets a parameter.
151      *
152      * @param string $name  The parameter name
153      * @param mixed  $value The parameter value
154      */
155     public function setParameter($name, $value)
156     {
157         $this->parameterBag->set($name, $value);
158     }
159
160     /**
161      * Sets a service.
162      *
163      * Setting a service to null resets the service: has() returns false and get()
164      * behaves in the same way as if the service was never created.
165      *
166      * Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0.
167      *
168      * @param string $id      The service identifier
169      * @param object $service The service instance
170      * @param string $scope   The scope of the service
171      *
172      * @throws RuntimeException         When trying to set a service in an inactive scope
173      * @throws InvalidArgumentException When trying to set a service in the prototype scope
174      */
175     public function set($id, $service, $scope = self::SCOPE_CONTAINER)
176     {
177         if (!in_array($scope, array('container', 'request')) || ('request' === $scope && 'request' !== $id)) {
178             @trigger_error('The concept of container scopes is deprecated since version 2.8 and will be removed in 3.0. Omit the third parameter.', E_USER_DEPRECATED);
179         }
180
181         if (self::SCOPE_PROTOTYPE === $scope) {
182             throw new InvalidArgumentException(sprintf('You cannot set service "%s" of scope "prototype".', $id));
183         }
184
185         $id = strtolower($id);
186
187         if ('service_container' === $id) {
188             // BC: 'service_container' is no longer a self-reference but always
189             // $this, so ignore this call.
190             // @todo Throw InvalidArgumentException in next major release.
191             return;
192         }
193         if (self::SCOPE_CONTAINER !== $scope) {
194             if (!isset($this->scopedServices[$scope])) {
195                 throw new RuntimeException(sprintf('You cannot set service "%s" of inactive scope.', $id));
196             }
197
198             $this->scopedServices[$scope][$id] = $service;
199         }
200
201         if (isset($this->aliases[$id])) {
202             unset($this->aliases[$id]);
203         }
204
205         $this->services[$id] = $service;
206
207         if (method_exists($this, $method = 'synchronize'.strtr($id, $this->underscoreMap).'Service')) {
208             $this->$method();
209         }
210
211         if (null === $service) {
212             if (self::SCOPE_CONTAINER !== $scope) {
213                 unset($this->scopedServices[$scope][$id]);
214             }
215
216             unset($this->services[$id]);
217         }
218     }
219
220     /**
221      * Returns true if the given service is defined.
222      *
223      * @param string $id The service identifier
224      *
225      * @return bool true if the service is defined, false otherwise
226      */
227     public function has($id)
228     {
229         for ($i = 2;;) {
230             if ('service_container' === $id
231                 || isset($this->aliases[$id])
232                 || isset($this->services[$id])
233                 || array_key_exists($id, $this->services)
234             ) {
235                 return true;
236             }
237             if (--$i && $id !== $lcId = strtolower($id)) {
238                 $id = $lcId;
239             } else {
240                 return method_exists($this, 'get'.strtr($id, $this->underscoreMap).'Service');
241             }
242         }
243     }
244
245     /**
246      * Gets a service.
247      *
248      * If a service is defined both through a set() method and
249      * with a get{$id}Service() method, the former has always precedence.
250      *
251      * @param string $id              The service identifier
252      * @param int    $invalidBehavior The behavior when the service does not exist
253      *
254      * @return object The associated service
255      *
256      * @throws ServiceCircularReferenceException When a circular reference is detected
257      * @throws ServiceNotFoundException          When the service is not defined
258      * @throws \Exception                        if an exception has been thrown when the service has been resolved
259      *
260      * @see Reference
261      */
262     public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE)
263     {
264         // Attempt to retrieve the service by checking first aliases then
265         // available services. Service IDs are case insensitive, however since
266         // this method can be called thousands of times during a request, avoid
267         // calling strtolower() unless necessary.
268         for ($i = 2;;) {
269             if ('service_container' === $id) {
270                 return $this;
271             }
272             if (isset($this->aliases[$id])) {
273                 $id = $this->aliases[$id];
274             }
275             // Re-use shared service instance if it exists.
276             if (isset($this->services[$id]) || array_key_exists($id, $this->services)) {
277                 return $this->services[$id];
278             }
279
280             if (isset($this->loading[$id])) {
281                 throw new ServiceCircularReferenceException($id, array_keys($this->loading));
282             }
283
284             if (isset($this->methodMap[$id])) {
285                 $method = $this->methodMap[$id];
286             } elseif (--$i && $id !== $lcId = strtolower($id)) {
287                 $id = $lcId;
288                 continue;
289             } elseif (method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) {
290                 // $method is set to the right value, proceed
291             } else {
292                 if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
293                     if (!$id) {
294                         throw new ServiceNotFoundException($id);
295                     }
296
297                     $alternatives = array();
298                     foreach ($this->getServiceIds() as $knownId) {
299                         $lev = levenshtein($id, $knownId);
300                         if ($lev <= strlen($id) / 3 || false !== strpos($knownId, $id)) {
301                             $alternatives[] = $knownId;
302                         }
303                     }
304
305                     throw new ServiceNotFoundException($id, null, null, $alternatives);
306                 }
307
308                 return;
309             }
310
311             $this->loading[$id] = true;
312
313             try {
314                 $service = $this->$method();
315             } catch (\Exception $e) {
316                 unset($this->loading[$id]);
317                 unset($this->services[$id]);
318
319                 if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
320                     return;
321                 }
322
323                 throw $e;
324             } catch (\Throwable $e) {
325                 unset($this->loading[$id]);
326                 unset($this->services[$id]);
327
328                 throw $e;
329             }
330
331             unset($this->loading[$id]);
332
333             return $service;
334         }
335     }
336
337     /**
338      * Returns true if the given service has actually been initialized.
339      *
340      * @param string $id The service identifier
341      *
342      * @return bool true if service has already been initialized, false otherwise
343      */
344     public function initialized($id)
345     {
346         $id = strtolower($id);
347
348         if ('service_container' === $id) {
349             // BC: 'service_container' was a synthetic service previously.
350             // @todo Change to false in next major release.
351             return true;
352         }
353
354         if (isset($this->aliases[$id])) {
355             $id = $this->aliases[$id];
356         }
357
358         return isset($this->services[$id]) || array_key_exists($id, $this->services);
359     }
360
361     /**
362      * {@inheritdoc}
363      */
364     public function reset()
365     {
366         if (!empty($this->scopedServices)) {
367             throw new LogicException('Resetting the container is not allowed when a scope is active.');
368         }
369
370         $this->services = array();
371     }
372
373     /**
374      * Gets all service ids.
375      *
376      * @return array An array of all defined service ids
377      */
378     public function getServiceIds()
379     {
380         $ids = array();
381         foreach (get_class_methods($this) as $method) {
382             if (preg_match('/^get(.+)Service$/', $method, $match)) {
383                 $ids[] = self::underscore($match[1]);
384             }
385         }
386         $ids[] = 'service_container';
387
388         return array_unique(array_merge($ids, array_keys($this->services)));
389     }
390
391     /**
392      * This is called when you enter a scope.
393      *
394      * @param string $name
395      *
396      * @throws RuntimeException         When the parent scope is inactive
397      * @throws InvalidArgumentException When the scope does not exist
398      *
399      * @deprecated since version 2.8, to be removed in 3.0.
400      */
401     public function enterScope($name)
402     {
403         if ('request' !== $name) {
404             @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
405         }
406
407         if (!isset($this->scopes[$name])) {
408             throw new InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name));
409         }
410
411         if (self::SCOPE_CONTAINER !== $this->scopes[$name] && !isset($this->scopedServices[$this->scopes[$name]])) {
412             throw new RuntimeException(sprintf('The parent scope "%s" must be active when entering this scope.', $this->scopes[$name]));
413         }
414
415         // check if a scope of this name is already active, if so we need to
416         // remove all services of this scope, and those of any of its child
417         // scopes from the global services map
418         if (isset($this->scopedServices[$name])) {
419             $services = array($this->services, $name => $this->scopedServices[$name]);
420             unset($this->scopedServices[$name]);
421
422             foreach ($this->scopeChildren[$name] as $child) {
423                 if (isset($this->scopedServices[$child])) {
424                     $services[$child] = $this->scopedServices[$child];
425                     unset($this->scopedServices[$child]);
426                 }
427             }
428
429             // update global map
430             $this->services = call_user_func_array('array_diff_key', $services);
431             array_shift($services);
432
433             // add stack entry for this scope so we can restore the removed services later
434             if (!isset($this->scopeStacks[$name])) {
435                 $this->scopeStacks[$name] = new \SplStack();
436             }
437             $this->scopeStacks[$name]->push($services);
438         }
439
440         $this->scopedServices[$name] = array();
441     }
442
443     /**
444      * This is called to leave the current scope, and move back to the parent
445      * scope.
446      *
447      * @param string $name The name of the scope to leave
448      *
449      * @throws InvalidArgumentException if the scope is not active
450      *
451      * @deprecated since version 2.8, to be removed in 3.0.
452      */
453     public function leaveScope($name)
454     {
455         if ('request' !== $name) {
456             @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
457         }
458
459         if (!isset($this->scopedServices[$name])) {
460             throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $name));
461         }
462
463         // remove all services of this scope, or any of its child scopes from
464         // the global service map
465         $services = array($this->services, $this->scopedServices[$name]);
466         unset($this->scopedServices[$name]);
467
468         foreach ($this->scopeChildren[$name] as $child) {
469             if (isset($this->scopedServices[$child])) {
470                 $services[] = $this->scopedServices[$child];
471                 unset($this->scopedServices[$child]);
472             }
473         }
474
475         // update global map
476         $this->services = call_user_func_array('array_diff_key', $services);
477
478         // check if we need to restore services of a previous scope of this type
479         if (isset($this->scopeStacks[$name]) && count($this->scopeStacks[$name]) > 0) {
480             $services = $this->scopeStacks[$name]->pop();
481             $this->scopedServices += $services;
482
483             if ($this->scopeStacks[$name]->isEmpty()) {
484                 unset($this->scopeStacks[$name]);
485             }
486
487             foreach ($services as $array) {
488                 foreach ($array as $id => $service) {
489                     $this->set($id, $service, $name);
490                 }
491             }
492         }
493     }
494
495     /**
496      * Adds a scope to the container.
497      *
498      * @param ScopeInterface $scope
499      *
500      * @throws InvalidArgumentException
501      *
502      * @deprecated since version 2.8, to be removed in 3.0.
503      */
504     public function addScope(ScopeInterface $scope)
505     {
506         $name = $scope->getName();
507         $parentScope = $scope->getParentName();
508
509         if ('request' !== $name) {
510             @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
511         }
512         if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) {
513             throw new InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name));
514         }
515         if (isset($this->scopes[$name])) {
516             throw new InvalidArgumentException(sprintf('A scope with name "%s" already exists.', $name));
517         }
518         if (self::SCOPE_CONTAINER !== $parentScope && !isset($this->scopes[$parentScope])) {
519             throw new InvalidArgumentException(sprintf('The parent scope "%s" does not exist, or is invalid.', $parentScope));
520         }
521
522         $this->scopes[$name] = $parentScope;
523         $this->scopeChildren[$name] = array();
524
525         // normalize the child relations
526         while ($parentScope !== self::SCOPE_CONTAINER) {
527             $this->scopeChildren[$parentScope][] = $name;
528             $parentScope = $this->scopes[$parentScope];
529         }
530     }
531
532     /**
533      * Returns whether this container has a certain scope.
534      *
535      * @param string $name The name of the scope
536      *
537      * @return bool
538      *
539      * @deprecated since version 2.8, to be removed in 3.0.
540      */
541     public function hasScope($name)
542     {
543         if ('request' !== $name) {
544             @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
545         }
546
547         return isset($this->scopes[$name]);
548     }
549
550     /**
551      * Returns whether this scope is currently active.
552      *
553      * This does not actually check if the passed scope actually exists.
554      *
555      * @param string $name
556      *
557      * @return bool
558      *
559      * @deprecated since version 2.8, to be removed in 3.0.
560      */
561     public function isScopeActive($name)
562     {
563         @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
564
565         return isset($this->scopedServices[$name]);
566     }
567
568     /**
569      * Camelizes a string.
570      *
571      * @param string $id A string to camelize
572      *
573      * @return string The camelized string
574      */
575     public static function camelize($id)
576     {
577         return strtr(ucwords(strtr($id, array('_' => ' ', '.' => '_ ', '\\' => '_ '))), array(' ' => ''));
578     }
579
580     /**
581      * A string to underscore.
582      *
583      * @param string $id The string to underscore
584      *
585      * @return string The underscored string
586      */
587     public static function underscore($id)
588     {
589         return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), str_replace('_', '.', $id)));
590     }
591
592     private function __clone()
593     {
594     }
595 }