PluginProbe
Cookiebot by Usercentrics – Automatic Cookie Banner for GDPR/CCPA & Google Consent Mode / 3.10.0
Cookiebot by Usercentrics – Automatic Cookie Banner for GDPR/CCPA & Google Consent Mode v3.10.0
4.7.3 4.7.2 4.7.1 trunk 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 3.0.0 3.0.1 3.1.0 3.10.0 3.10.1 3.11.1 3.11.2 3.11.3 3.2.0 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 All 93 releases
cookiebot / addons / inc / Dependencies / DI / Container.php

Container.php in Cookiebot by Usercentrics – Automatic Cookie Banner for GDPR/CCPA & Google Consent Mode 3.10.0, at addons/inc/Dependencies/DI/Container.php

341 lines 11.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * PHP-DI
4 *
5 * @link http://php-di.org/
6 * @copyright Matthieu Napoli (http://mnapoli.fr/)
7 * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
8 */
9
10 namespace Cybot\Dependencies\DI;
11
12 use Cybot\Dependencies\DI\Definition\ObjectDefinition;
13 use Cybot\Dependencies\DI\Definition\Definition;
14 use Cybot\Dependencies\DI\Definition\FactoryDefinition;
15 use Cybot\Dependencies\DI\Definition\InstanceDefinition;
16 use Cybot\Dependencies\DI\Definition\Resolver\ResolverDispatcher;
17 use Cybot\Dependencies\DI\Definition\Source\CachedDefinitionSource;
18 use Cybot\Dependencies\DI\Definition\Source\DefinitionSource;
19 use Cybot\Dependencies\DI\Definition\Source\MutableDefinitionSource;
20 use Cybot\Dependencies\DI\Definition\Helper\DefinitionHelper;
21 use Cybot\Dependencies\DI\Definition\Resolver\DefinitionResolver;
22 use Cybot\Dependencies\DI\Cybot\Dependencies\Invoker\DefinitionParameterResolver;
23 use Cybot\Dependencies\DI\Proxy\ProxyFactory;
24 use Exception;
25 use Cybot\Dependencies\Interop\Container\ContainerInterface;
26 use InvalidArgumentException;
27 use Cybot\Dependencies\Invoker\Invoker;
28 use Cybot\Dependencies\Invoker\ParameterResolver\AssociativeArrayResolver;
29 use Cybot\Dependencies\Invoker\ParameterResolver\Container\TypeHintContainerResolver;
30 use Cybot\Dependencies\Invoker\ParameterResolver\NumericArrayResolver;
31 use Cybot\Dependencies\Invoker\ParameterResolver\ResolverChain;
32
33 /**
34 * Dependency Injection Container.
35 *
36 * @author Matthieu Napoli <[email protected]>
37 */
38 class Container implements ContainerInterface, FactoryInterface, \Cybot\Dependencies\DI\InvokerInterface
39 {
40 /**
41 * Map of entries with Singleton scope that are already resolved.
42 * @var array
43 */
44 private $singletonEntries = [];
45
46 /**
47 * @var DefinitionSource
48 */
49 private $definitionSource;
50
51 /**
52 * @var DefinitionResolver
53 */
54 private $definitionResolver;
55
56 /**
57 * Array of entries being resolved. Used to avoid circular dependencies and infinite loops.
58 * @var array
59 */
60 private $entriesBeingResolved = [];
61
62 /**
63 * @var \Cybot\Dependencies\Invoker\InvokerInterface|null
64 */
65 private $invoker;
66
67 /**
68 * Container that wraps this container. If none, points to $this.
69 *
70 * @var ContainerInterface
71 */
72 private $wrapperContainer;
73
74 /**
75 * Use the ContainerBuilder to ease constructing the Container.
76 *
77 * @see ContainerBuilder
78 *
79 * @param DefinitionSource $definitionSource
80 * @param ProxyFactory $proxyFactory
81 * @param ContainerInterface $wrapperContainer If the container is wrapped by another container.
82 */
83 public function __construct(
84 DefinitionSource $definitionSource,
85 ProxyFactory $proxyFactory,
86 ContainerInterface $wrapperContainer = null
87 ) {
88 $this->wrapperContainer = $wrapperContainer ?: $this;
89
90 $this->definitionSource = $definitionSource;
91 $this->definitionResolver = new ResolverDispatcher($this->wrapperContainer, $proxyFactory);
92
93 // Auto-register the container
94 $this->singletonEntries['Cybot\Dependencies\DI\Container'] = $this;
95 $this->singletonEntries['Cybot\Dependencies\DI\ContainerInterface'] = $this;
96 $this->singletonEntries['Cybot\Dependencies\DI\FactoryInterface'] = $this;
97 $this->singletonEntries['Cybot\Dependencies\DI\InvokerInterface'] = $this;
98 }
99
100 /**
101 * Returns an entry of the container by its name.
102 *
103 * @param string $name Entry name or a class name.
104 *
105 * @throws InvalidArgumentException The name parameter must be of type string.
106 * @throws DependencyException Error while resolving the entry.
107 * @throws NotFoundException No entry found for the given name.
108 * @return mixed
109 */
110 public function get($name)
111 {
112 if (! is_string($name)) {
113 throw new InvalidArgumentException(sprintf(
114 'The name parameter must be of type string, %s given',
115 is_object($name) ? get_class($name) : gettype($name)
116 ));
117 }
118
119 // Try to find the entry in the singleton map
120 if (array_key_exists($name, $this->singletonEntries)) {
121 return $this->singletonEntries[$name];
122 }
123
124 $definition = $this->definitionSource->getDefinition($name);
125 if (! $definition) {
126 throw new NotFoundException("No entry or class found for '$name'");
127 }
128
129 $value = $this->resolveDefinition($definition);
130
131 // If the entry is singleton, we store it to always return it without recomputing it
132 if ($definition->getScope() === Scope::SINGLETON) {
133 $this->singletonEntries[$name] = $value;
134 }
135
136 return $value;
137 }
138
139 /**
140 * Build an entry of the container by its name.
141 *
142 * This method behave like get() except it forces the scope to "prototype",
143 * which means the definition of the entry will be re-evaluated each time.
144 * For example, if the entry is a class, then a new instance will be created each time.
145 *
146 * This method makes the container behave like a factory.
147 *
148 * @param string $name Entry name or a class name.
149 * @param array $parameters Optional parameters to use to build the entry. Use this to force specific parameters
150 * to specific values. Parameters not defined in this array will be resolved using
151 * the container.
152 *
153 * @throws InvalidArgumentException The name parameter must be of type string.
154 * @throws DependencyException Error while resolving the entry.
155 * @throws NotFoundException No entry found for the given name.
156 * @return mixed
157 */
158 public function make($name, array $parameters = [])
159 {
160 if (! is_string($name)) {
161 throw new InvalidArgumentException(sprintf(
162 'The name parameter must be of type string, %s given',
163 is_object($name) ? get_class($name) : gettype($name)
164 ));
165 }
166
167 $definition = $this->definitionSource->getDefinition($name);
168 if (! $definition) {
169 // Try to find the entry in the singleton map
170 if (array_key_exists($name, $this->singletonEntries)) {
171 return $this->singletonEntries[$name];
172 }
173
174 throw new NotFoundException("No entry or class found for '$name'");
175 }
176
177 return $this->resolveDefinition($definition, $parameters);
178 }
179
180 /**
181 * Test if the container can provide something for the given name.
182 *
183 * @param string $name Entry name or a class name.
184 *
185 * @throws InvalidArgumentException The name parameter must be of type string.
186 * @return bool
187 */
188 public function has($name)
189 {
190 if (! is_string($name)) {
191 throw new InvalidArgumentException(sprintf(
192 'The name parameter must be of type string, %s given',
193 is_object($name) ? get_class($name) : gettype($name)
194 ));
195 }
196
197 if (array_key_exists($name, $this->singletonEntries)) {
198 return true;
199 }
200
201 $definition = $this->definitionSource->getDefinition($name);
202 if ($definition === null) {
203 return false;
204 }
205
206 return $this->definitionResolver->isResolvable($definition);
207 }
208
209 /**
210 * Inject all dependencies on an existing instance
211 *
212 * @param object $instance Object to perform injection upon
213 * @throws InvalidArgumentException
214 * @throws DependencyException Error while injecting dependencies
215 * @return object $instance Returns the same instance
216 */
217 public function injectOn($instance)
218 {
219 $objectDefinition = $this->definitionSource->getDefinition(get_class($instance));
220 if (! $objectDefinition instanceof ObjectDefinition) {
221 return $instance;
222 }
223
224 $definition = new InstanceDefinition($instance, $objectDefinition);
225
226 $this->definitionResolver->resolve($definition);
227
228 return $instance;
229 }
230
231 /**
232 * Call the given function using the given parameters.
233 *
234 * Missing parameters will be resolved from the container.
235 *
236 * @param callable $callable Function to call.
237 * @param array $parameters Parameters to use. Can be indexed by the parameter names
238 * or not indexed (same order as the parameters).
239 * The array can also contain DI definitions, e.g. Cybot\Dependencies\DI\get().
240 *
241 * @return mixed Result of the function.
242 */
243 public function call($callable, array $parameters = [])
244 {
245 return $this->getInvoker()->call($callable, $parameters);
246 }
247
248 /**
249 * Define an object or a value in the container.
250 *
251 * @param string $name Entry name
252 * @param mixed|DefinitionHelper $value Value, use definition helpers to define objects
253 */
254 public function set($name, $value)
255 {
256 if ($value instanceof DefinitionHelper) {
257 $value = $value->getDefinition($name);
258 } elseif ($value instanceof \Closure) {
259 $value = new FactoryDefinition($name, $value);
260 }
261
262 if ($value instanceof Definition) {
263 $this->setDefinition($name, $value);
264 } else {
265 $this->singletonEntries[$name] = $value;
266 }
267 }
268
269 /**
270 * Resolves a definition.
271 *
272 * Checks for circular dependencies while resolving the definition.
273 *
274 * @param Definition $definition
275 * @param array $parameters
276 *
277 * @throws DependencyException Error while resolving the entry.
278 * @return mixed
279 */
280 private function resolveDefinition(Definition $definition, array $parameters = [])
281 {
282 $entryName = $definition->getName();
283
284 // Check if we are already getting this entry -> circular dependency
285 if (isset($this->entriesBeingResolved[$entryName])) {
286 throw new DependencyException("Circular dependency detected while trying to resolve entry '$entryName'");
287 }
288 $this->entriesBeingResolved[$entryName] = true;
289
290 // Resolve the definition
291 try {
292 $value = $this->definitionResolver->resolve($definition, $parameters);
293 } catch (Exception $exception) {
294 unset($this->entriesBeingResolved[$entryName]);
295 throw $exception;
296 }
297
298 unset($this->entriesBeingResolved[$entryName]);
299
300 return $value;
301 }
302
303 private function setDefinition($name, Definition $definition)
304 {
305 if ($this->definitionSource instanceof CachedDefinitionSource) {
306 throw new \LogicException('You cannot set a definition at runtime on a container that has a cache configured. Doing so would risk caching the definition for the next execution, where it might be different. You can either put your definitions in a file, remove the cache or ->set() a raw value directly (PHP object, string, int, ...) instead of a PHP-DI definition.');
307 }
308
309 if (! $this->definitionSource instanceof MutableDefinitionSource) {
310 // This can happen if you instantiate the container yourself
311 throw new \LogicException('The container has not been initialized correctly');
312 }
313
314 // Clear existing entry if it exists
315 if (array_key_exists($name, $this->singletonEntries)) {
316 unset($this->singletonEntries[$name]);
317 }
318
319 $this->definitionSource->addDefinition($definition);
320 }
321
322 /**
323 * @return \Cybot\Dependencies\Invoker\InvokerInterface
324 */
325 private function getInvoker()
326 {
327 if (! $this->invoker) {
328 $parameterResolver = new ResolverChain([
329 new NumericArrayResolver,
330 new AssociativeArrayResolver,
331 new DefinitionParameterResolver($this->definitionResolver),
332 new TypeHintContainerResolver($this->wrapperContainer),
333 ]);
334
335 $this->invoker = new Invoker($parameterResolver, $this);
336 }
337
338 return $this->invoker;
339 }
340 }
341