PluginProbe
WPIDE – File Manager & Code Editor / 3.5.5
WPIDE – File Manager & Code Editor v3.5.5
3.5.8 3.5.7 2.0.14 2.0.15 2.0.16 2.0.2 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1 2.2 2.3 2.3.1 2.3.2 2.4.0 2.5 2.6 3.0 3.1 3.2 3.3 3.4 All 54 releases
wpide / vendor / php-di / php-di / src / Container.php

Container.php in WPIDE – File Manager & Code Editor 3.5.5, at vendor/php-di/php-di/src/Container.php

434 lines 13.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DI;
6
7 use DI\Definition\Definition;
8 use DI\Definition\Exception\InvalidDefinition;
9 use DI\Definition\FactoryDefinition;
10 use DI\Definition\Helper\DefinitionHelper;
11 use DI\Definition\InstanceDefinition;
12 use DI\Definition\ObjectDefinition;
13 use DI\Definition\Resolver\DefinitionResolver;
14 use DI\Definition\Resolver\ResolverDispatcher;
15 use DI\Definition\Source\DefinitionArray;
16 use DI\Definition\Source\MutableDefinitionSource;
17 use DI\Definition\Source\ReflectionBasedAutowiring;
18 use DI\Definition\Source\SourceChain;
19 use DI\Definition\ValueDefinition;
20 use DI\Invoker\DefinitionParameterResolver;
21 use DI\Proxy\ProxyFactory;
22 use InvalidArgumentException;
23 use Invoker\Invoker;
24 use Invoker\InvokerInterface;
25 use Invoker\ParameterResolver\AssociativeArrayResolver;
26 use Invoker\ParameterResolver\Container\TypeHintContainerResolver;
27 use Invoker\ParameterResolver\DefaultValueResolver;
28 use Invoker\ParameterResolver\NumericArrayResolver;
29 use Invoker\ParameterResolver\ResolverChain;
30 use Psr\Container\ContainerInterface;
31
32 /**
33 * Dependency Injection Container.
34 *
35 * @api
36 *
37 * @author Matthieu Napoli <matthieu@mnapoli.fr>
38 */
39 class Container implements ContainerInterface, FactoryInterface, InvokerInterface
40 {
41 /**
42 * Map of entries that are already resolved.
43 * @var array
44 */
45 protected $resolvedEntries = [];
46
47 /**
48 * @var MutableDefinitionSource
49 */
50 private $definitionSource;
51
52 /**
53 * @var DefinitionResolver
54 */
55 private $definitionResolver;
56
57 /**
58 * Map of definitions that are already fetched (local cache).
59 *
60 * @var (Definition|null)[]
61 */
62 private $fetchedDefinitions = [];
63
64 /**
65 * Array of entries being resolved. Used to avoid circular dependencies and infinite loops.
66 * @var array
67 */
68 protected $entriesBeingResolved = [];
69
70 /**
71 * @var InvokerInterface|null
72 */
73 private $invoker;
74
75 /**
76 * Container that wraps this container. If none, points to $this.
77 *
78 * @var ContainerInterface
79 */
80 protected $delegateContainer;
81
82 /**
83 * @var ProxyFactory
84 */
85 protected $proxyFactory;
86
87 /**
88 * Use `$container = new Container()` if you want a container with the default configuration.
89 *
90 * If you want to customize the container's behavior, you are discouraged to create and pass the
91 * dependencies yourself, the ContainerBuilder class is here to help you instead.
92 *
93 * @see ContainerBuilder
94 *
95 * @param ContainerInterface $wrapperContainer If the container is wrapped by another container.
96 */
97 public function __construct(
98 MutableDefinitionSource $definitionSource = null,
99 ProxyFactory $proxyFactory = null,
100 ContainerInterface $wrapperContainer = null
101 ) {
102 $this->delegateContainer = $wrapperContainer ?: $this;
103
104 $this->definitionSource = $definitionSource ?: $this->createDefaultDefinitionSource();
105 $this->proxyFactory = $proxyFactory ?: new ProxyFactory(false);
106 $this->definitionResolver = new ResolverDispatcher($this->delegateContainer, $this->proxyFactory);
107
108 // Auto-register the container
109 $this->resolvedEntries = [
110 self::class => $this,
111 ContainerInterface::class => $this->delegateContainer,
112 FactoryInterface::class => $this,
113 InvokerInterface::class => $this,
114 ];
115 }
116
117 /**
118 * Returns an entry of the container by its name.
119 *
120 * @template T
121 * @param string|class-string<T> $name Entry name or a class name.
122 *
123 * @throws DependencyException Error while resolving the entry.
124 * @throws NotFoundException No entry found for the given name.
125 * @return mixed|T
126 */
127 public function get($name)
128 {
129 // If the entry is already resolved we return it
130 if (isset($this->resolvedEntries[$name]) || array_key_exists($name, $this->resolvedEntries)) {
131 return $this->resolvedEntries[$name];
132 }
133
134 $definition = $this->getDefinition($name);
135 if (! $definition) {
136 throw new NotFoundException("No entry or class found for '$name'");
137 }
138
139 $value = $this->resolveDefinition($definition);
140
141 $this->resolvedEntries[$name] = $value;
142
143 return $value;
144 }
145
146 /**
147 * @param string $name
148 *
149 * @return Definition|null
150 */
151 private function getDefinition($name)
152 {
153 // Local cache that avoids fetching the same definition twice
154 if (!array_key_exists($name, $this->fetchedDefinitions)) {
155 $this->fetchedDefinitions[$name] = $this->definitionSource->getDefinition($name);
156 }
157
158 return $this->fetchedDefinitions[$name];
159 }
160
161 /**
162 * Build an entry of the container by its name.
163 *
164 * This method behave like get() except resolves the entry again every time.
165 * For example if the entry is a class then a new instance will be created each time.
166 *
167 * This method makes the container behave like a factory.
168 *
169 * @template T
170 * @param string|class-string<T> $name Entry name or a class name.
171 * @param array $parameters Optional parameters to use to build the entry. Use this to force
172 * specific parameters to specific values. Parameters not defined in this
173 * array will be resolved using the container.
174 *
175 * @throws InvalidArgumentException The name parameter must be of type string.
176 * @throws DependencyException Error while resolving the entry.
177 * @throws NotFoundException No entry found for the given name.
178 * @return mixed|T
179 */
180 public function make($name, array $parameters = [])
181 {
182 if (! is_string($name)) {
183 throw new InvalidArgumentException(sprintf(
184 'The name parameter must be of type string, %s given',
185 is_object($name) ? get_class($name) : gettype($name)
186 ));
187 }
188
189 $definition = $this->getDefinition($name);
190 if (! $definition) {
191 // If the entry is already resolved we return it
192 if (array_key_exists($name, $this->resolvedEntries)) {
193 return $this->resolvedEntries[$name];
194 }
195
196 throw new NotFoundException("No entry or class found for '$name'");
197 }
198
199 return $this->resolveDefinition($definition, $parameters);
200 }
201
202 /**
203 * Test if the container can provide something for the given name.
204 *
205 * @param string $name Entry name or a class name.
206 *
207 * @throws InvalidArgumentException The name parameter must be of type string.
208 * @return bool
209 */
210 public function has($name)
211 {
212 if (! is_string($name)) {
213 throw new InvalidArgumentException(sprintf(
214 'The name parameter must be of type string, %s given',
215 is_object($name) ? get_class($name) : gettype($name)
216 ));
217 }
218
219 if (array_key_exists($name, $this->resolvedEntries)) {
220 return true;
221 }
222
223 $definition = $this->getDefinition($name);
224 if ($definition === null) {
225 return false;
226 }
227
228 return $this->definitionResolver->isResolvable($definition);
229 }
230
231 /**
232 * Inject all dependencies on an existing instance.
233 *
234 * @template T
235 * @param object|T $instance Object to perform injection upon
236 * @throws InvalidArgumentException
237 * @throws DependencyException Error while injecting dependencies
238 * @return object|T $instance Returns the same instance
239 */
240 public function injectOn($instance)
241 {
242 if (!$instance) {
243 return $instance;
244 }
245
246 $className = get_class($instance);
247
248 // If the class is anonymous, don't cache its definition
249 // Checking for anonymous classes is cleaner via Reflection, but also slower
250 $objectDefinition = false !== strpos($className, '@anonymous')
251 ? $this->definitionSource->getDefinition($className)
252 : $this->getDefinition($className);
253
254 if (! $objectDefinition instanceof ObjectDefinition) {
255 return $instance;
256 }
257
258 $definition = new InstanceDefinition($instance, $objectDefinition);
259
260 $this->definitionResolver->resolve($definition);
261
262 return $instance;
263 }
264
265 /**
266 * Call the given function using the given parameters.
267 *
268 * Missing parameters will be resolved from the container.
269 *
270 * @param callable $callable Function to call.
271 * @param array $parameters Parameters to use. Can be indexed by the parameter names
272 * or not indexed (same order as the parameters).
273 * The array can also contain DI definitions, e.g. DI\get().
274 *
275 * @return mixed Result of the function.
276 */
277 public function call($callable, array $parameters = [])
278 {
279 return $this->getInvoker()->call($callable, $parameters);
280 }
281
282 /**
283 * Define an object or a value in the container.
284 *
285 * @param string $name Entry name
286 * @param mixed|DefinitionHelper $value Value, use definition helpers to define objects
287 */
288 public function set(string $name, $value)
289 {
290 if ($value instanceof DefinitionHelper) {
291 $value = $value->getDefinition($name);
292 } elseif ($value instanceof \Closure) {
293 $value = new FactoryDefinition($name, $value);
294 }
295
296 if ($value instanceof ValueDefinition) {
297 $this->resolvedEntries[$name] = $value->getValue();
298 } elseif ($value instanceof Definition) {
299 $value->setName($name);
300 $this->setDefinition($name, $value);
301 } else {
302 $this->resolvedEntries[$name] = $value;
303 }
304 }
305
306 /**
307 * Get defined container entries.
308 *
309 * @return string[]
310 */
311 public function getKnownEntryNames() : array
312 {
313 $entries = array_unique(array_merge(
314 array_keys($this->definitionSource->getDefinitions()),
315 array_keys($this->resolvedEntries)
316 ));
317 sort($entries);
318
319 return $entries;
320 }
321
322 /**
323 * Get entry debug information.
324 *
325 * @param string $name Entry name
326 *
327 * @throws InvalidDefinition
328 * @throws NotFoundException
329 */
330 public function debugEntry(string $name) : string
331 {
332 $definition = $this->definitionSource->getDefinition($name);
333 if ($definition instanceof Definition) {
334 return (string) $definition;
335 }
336
337 if (array_key_exists($name, $this->resolvedEntries)) {
338 return $this->getEntryType($this->resolvedEntries[$name]);
339 }
340
341 throw new NotFoundException("No entry or class found for '$name'");
342 }
343
344 /**
345 * Get formatted entry type.
346 *
347 * @param mixed $entry
348 */
349 private function getEntryType($entry) : string
350 {
351 if (is_object($entry)) {
352 return sprintf("Object (\n class = %s\n)", get_class($entry));
353 }
354
355 if (is_array($entry)) {
356 return preg_replace(['/^array \(/', '/\)$/'], ['[', ']'], var_export($entry, true));
357 }
358
359 if (is_string($entry)) {
360 return sprintf('Value (\'%s\')', $entry);
361 }
362
363 if (is_bool($entry)) {
364 return sprintf('Value (%s)', $entry === true ? 'true' : 'false');
365 }
366
367 return sprintf('Value (%s)', is_scalar($entry) ? $entry : ucfirst(gettype($entry)));
368 }
369
370 /**
371 * Resolves a definition.
372 *
373 * Checks for circular dependencies while resolving the definition.
374 *
375 * @throws DependencyException Error while resolving the entry.
376 * @return mixed
377 */
378 private function resolveDefinition(Definition $definition, array $parameters = [])
379 {
380 $entryName = $definition->getName();
381
382 // Check if we are already getting this entry -> circular dependency
383 if (isset($this->entriesBeingResolved[$entryName])) {
384 throw new DependencyException("Circular dependency detected while trying to resolve entry '$entryName'");
385 }
386 $this->entriesBeingResolved[$entryName] = true;
387
388 // Resolve the definition
389 try {
390 $value = $this->definitionResolver->resolve($definition, $parameters);
391 } finally {
392 unset($this->entriesBeingResolved[$entryName]);
393 }
394
395 return $value;
396 }
397
398 protected function setDefinition(string $name, Definition $definition)
399 {
400 // Clear existing entry if it exists
401 if (array_key_exists($name, $this->resolvedEntries)) {
402 unset($this->resolvedEntries[$name]);
403 }
404 $this->fetchedDefinitions = []; // Completely clear this local cache
405
406 $this->definitionSource->addDefinition($definition);
407 }
408
409 private function getInvoker() : InvokerInterface
410 {
411 if (! $this->invoker) {
412 $parameterResolver = new ResolverChain([
413 new DefinitionParameterResolver($this->definitionResolver),
414 new NumericArrayResolver,
415 new AssociativeArrayResolver,
416 new DefaultValueResolver,
417 new TypeHintContainerResolver($this->delegateContainer),
418 ]);
419
420 $this->invoker = new Invoker($parameterResolver, $this);
421 }
422
423 return $this->invoker;
424 }
425
426 private function createDefaultDefinitionSource() : SourceChain
427 {
428 $source = new SourceChain([new ReflectionBasedAutowiring]);
429 $source->setMutableDefinitionSource(new DefinitionArray([], new ReflectionBasedAutowiring));
430
431 return $source;
432 }
433 }
434