PluginProbe
Elementor Website Builder – more than just a page builder / 3.26.4
Elementor Website Builder – more than just a page builder v3.26.4
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / vendor_prefixed / php-di / php-di / src / Container.php

Container.php in Elementor Website Builder – more than just a page builder 3.26.4, at vendor_prefixed/php-di/php-di/src/Container.php

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