324cccab98913a4c7d1c8de24c566aa11d473b31
[yaffs-website] / vendor / symfony / dependency-injection / ServiceLocator.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 Psr\Container\ContainerInterface as PsrContainerInterface;
15 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
16 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
17
18 /**
19  * @author Robin Chalas <robin.chalas@gmail.com>
20  * @author Nicolas Grekas <p@tchwork.com>
21  */
22 class ServiceLocator implements PsrContainerInterface
23 {
24     private $factories;
25     private $loading = array();
26     private $externalId;
27     private $container;
28
29     /**
30      * @param callable[] $factories
31      */
32     public function __construct(array $factories)
33     {
34         $this->factories = $factories;
35     }
36
37     /**
38      * {@inheritdoc}
39      */
40     public function has($id)
41     {
42         return isset($this->factories[$id]);
43     }
44
45     /**
46      * {@inheritdoc}
47      */
48     public function get($id)
49     {
50         if (!isset($this->factories[$id])) {
51             throw new ServiceNotFoundException($id, end($this->loading) ?: null, null, array(), $this->createServiceNotFoundMessage($id));
52         }
53
54         if (isset($this->loading[$id])) {
55             $ids = array_values($this->loading);
56             $ids = array_slice($this->loading, array_search($id, $ids));
57             $ids[] = $id;
58
59             throw new ServiceCircularReferenceException($id, $ids);
60         }
61
62         $this->loading[$id] = $id;
63         try {
64             return $this->factories[$id]();
65         } finally {
66             unset($this->loading[$id]);
67         }
68     }
69
70     public function __invoke($id)
71     {
72         return isset($this->factories[$id]) ? $this->get($id) : null;
73     }
74
75     /**
76      * @internal
77      */
78     public function withContext($externalId, Container $container)
79     {
80         $locator = clone $this;
81         $locator->externalId = $externalId;
82         $locator->container = $container;
83
84         return $locator;
85     }
86
87     private function createServiceNotFoundMessage($id)
88     {
89         if ($this->loading) {
90             return sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $this->formatAlternatives());
91         }
92
93         $class = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 3);
94         $class = isset($class[2]['object']) ? get_class($class[2]['object']) : null;
95         $externalId = $this->externalId ?: $class;
96
97         $msg = sprintf('Service "%s" not found: ', $id);
98
99         if (!$this->container) {
100             $class = null;
101         } elseif ($this->container->has($id) || isset($this->container->getRemovedIds()[$id])) {
102             $msg .= 'even though it exists in the app\'s container, ';
103         } else {
104             try {
105                 $this->container->get($id);
106                 $class = null;
107             } catch (ServiceNotFoundException $e) {
108                 if ($e->getAlternatives()) {
109                     $msg .= sprintf(' did you mean %s? Anyway, ', $this->formatAlternatives($e->getAlternatives(), 'or'));
110                 } else {
111                     $class = null;
112                 }
113             }
114         }
115         if ($externalId) {
116             $msg .= sprintf('the container inside "%s" is a smaller service locator that %s', $externalId, $this->formatAlternatives());
117         } else {
118             $msg .= sprintf('the current service locator %s', $this->formatAlternatives());
119         }
120
121         if (!$class) {
122             // no-op
123         } elseif (is_subclass_of($class, ServiceSubscriberInterface::class)) {
124             $msg .= sprintf(' Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "%s::getSubscribedServices()".', preg_replace('/([^\\\\]++\\\\)++/', '', $class));
125         } else {
126             $msg .= 'Try using dependency injection instead.';
127         }
128
129         return $msg;
130     }
131
132     private function formatAlternatives(array $alternatives = null, $separator = 'and')
133     {
134         $format = '"%s"%s';
135         if (null === $alternatives) {
136             if (!$alternatives = array_keys($this->factories)) {
137                 return 'is empty...';
138             }
139             $format = sprintf('only knows about the %s service%s.', $format, 1 < count($alternatives) ? 's' : '');
140         }
141         $last = array_pop($alternatives);
142
143         return sprintf($format, $alternatives ? implode('", "', $alternatives) : $last, $alternatives ? sprintf(' %s "%s"', $separator, $last) : '');
144     }
145 }