PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.16.8.1
GiveWP – Donation Plugin and Fundraising Platform v4.16.8.1
4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 All 254 releases
give / src / Container / Container.php

Container.php in GiveWP – Donation Plugin and Fundraising Platform 4.16.8.1, at src/Container/Container.php

1,196 lines 31.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Give\Container;
4
5 use ArrayAccess;
6 use Closure;
7 use Exception;
8 use Give\Container\Exceptions\BindingResolutionException;
9 use Give\Framework\Exceptions\Primitives\InvalidArgumentException;
10 use Give\Framework\Exceptions\Primitives\LogicException;
11 use Give\Vendors\StellarWP\ContainerContract\ContainerInterface;
12 use ReflectionClass;
13 use ReflectionException;
14 use ReflectionParameter;
15
16 class Container implements ArrayAccess, ContainerInterface
17 {
18 /**
19 * An array of the types that have been resolved.
20 *
21 * @var bool[]
22 */
23 protected $resolved = [];
24
25 /**
26 * The container's bindings.
27 *
28 * @var array[]
29 */
30 protected $bindings = [];
31
32 /**
33 * The container's method bindings.
34 *
35 * @var Closure[]
36 */
37 protected $methodBindings = [];
38
39 /**
40 * The container's shared instances.
41 *
42 * @var object[]
43 */
44 protected $instances = [];
45
46 /**
47 * The registered type aliases.
48 *
49 * @var string[]
50 */
51 protected $aliases = [];
52
53 /**
54 * The registered aliases keyed by the abstract name.
55 *
56 * @var array[]
57 */
58 protected $abstractAliases = [];
59
60 /**
61 * The extension closures for services.
62 *
63 * @var array[]
64 */
65 protected $extenders = [];
66
67 /**
68 * All of the registered tags.
69 *
70 * @var array[]
71 */
72 protected $tags = [];
73
74 /**
75 * The stack of concretions currently being built.
76 *
77 * @var array[]
78 */
79 protected $buildStack = [];
80
81 /**
82 * The parameter override stack.
83 *
84 * @var array[]
85 */
86 protected $with = [];
87
88 /**
89 * All of the registered rebound callbacks.
90 *
91 * @var array[]
92 */
93 protected $reboundCallbacks = [];
94
95 /**
96 * All of the global resolving callbacks.
97 *
98 * @var Closure[]
99 */
100 protected $globalResolvingCallbacks = [];
101
102 /**
103 * All of the global after resolving callbacks.
104 *
105 * @var Closure[]
106 */
107 protected $globalAfterResolvingCallbacks = [];
108
109 /**
110 * All of the resolving callbacks by class type.
111 *
112 * @var array[]
113 */
114 protected $resolvingCallbacks = [];
115
116 /**
117 * All of the after resolving callbacks by class type.
118 *
119 * @var array[]
120 */
121 protected $afterResolvingCallbacks = [];
122
123 /**
124 * Determine if the given abstract type has been bound.
125 */
126 public function bound(string $abstract): bool
127 {
128 return isset($this->bindings[$abstract]) ||
129 isset($this->instances[$abstract]) ||
130 $this->isAlias($abstract);
131 }
132
133 /**
134 * Returns true if the container can return an entry for the given identifier.
135 * Returns false otherwise.
136 *
137 * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
138 * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
139 */
140 public function has(string $id): bool
141 {
142 return $this->bound($id);
143 }
144
145 /**
146 * Determine if the given abstract type has been resolved.
147 */
148 public function resolved(string $abstract): bool
149 {
150 if ($this->isAlias($abstract)) {
151 $abstract = $this->getAlias($abstract);
152 }
153
154 return isset($this->resolved[$abstract]) ||
155 isset($this->instances[$abstract]);
156 }
157
158 /**
159 * Determine if a given type is shared.
160 */
161 public function isShared(string $abstract): bool
162 {
163 return isset($this->instances[$abstract]) ||
164 (isset($this->bindings[$abstract]['shared']) &&
165 $this->bindings[$abstract]['shared'] === true);
166 }
167
168 /**
169 * Determine if a given string is an alias.
170 */
171 public function isAlias(string $name): bool
172 {
173 return isset($this->aliases[$name]);
174 }
175
176 /**
177 * Register a binding with the container.
178 *
179 * @param string $abstract
180 * @param Closure|string|null $concrete
181 * @param bool $shared
182 *
183 * @return void
184 */
185 public function bind(string $abstract, $concrete = null, bool $shared = false)
186 {
187 $this->dropStaleInstances($abstract);
188
189 // If no concrete type was given, we will simply set the concrete type to the
190 // abstract type. After that, the concrete type to be registered as shared
191 // without being forced to state their classes in both of the parameters.
192 if (is_null($concrete)) {
193 $concrete = $abstract;
194 }
195
196 // If the factory is not a Closure, it means it is just a class name which is
197 // bound into this container to the abstract type and we will just wrap it
198 // up inside its own Closure to give us more convenience when extending.
199 if ( ! $concrete instanceof Closure) {
200 if (is_string($concrete) && ($abstract === $concrete || class_exists($concrete) || interface_exists($concrete))) {
201 $concrete = $this->getClosure($abstract, $concrete);
202 } else {
203 $concrete = function () use ($concrete) {
204 return $concrete;
205 };
206 }
207 }
208
209 $this->bindings[$abstract] = compact('concrete', 'shared');
210
211 // If the abstract type was already resolved in this container we'll fire the
212 // rebound listener so that any objects which have already gotten resolved
213 // can have their copy of the object updated via the listener callbacks.
214 if ($this->resolved($abstract)) {
215 $this->rebound($abstract);
216 }
217 }
218
219 /**
220 * Get the Closure to be used when building a type.
221 */
222 protected function getClosure(string $abstract, string $concrete): Closure
223 {
224 return static function ($container, $parameters = []) use ($abstract, $concrete) {
225 if ($abstract == $concrete) {
226 return $container->build($concrete);
227 }
228
229 return $container->resolve(
230 $concrete,
231 $parameters,
232 $raiseEvents = false
233 );
234 };
235 }
236
237 /**
238 * Determine if the container has a method binding.
239 */
240 public function hasMethodBinding(string $method): bool
241 {
242 return isset($this->methodBindings[$method]);
243 }
244
245 /**
246 * Bind a callback to resolve with Container::call.
247 *
248 * @param array|string $method
249 * @param Closure $callback
250 *
251 * @return void
252 */
253 public function bindMethod($method, Closure $callback)
254 {
255 $this->methodBindings[$this->parseBindMethod($method)] = $callback;
256 }
257
258 /**
259 * Get the method to be bound in class@method format.
260 *
261 * @param array|string $method
262 *
263 * @return string
264 */
265 protected function parseBindMethod($method): string
266 {
267 if (is_array($method)) {
268 return $method[0] . '@' . $method[1];
269 }
270
271 return $method;
272 }
273
274 /**
275 * Get the method binding for the given method.
276 *
277 * @param string $method
278 * @param mixed $instance
279 *
280 * @return mixed
281 */
282 public function callMethodBinding(string $method, $instance)
283 {
284 return call_user_func($this->methodBindings[$method], $instance, $this);
285 }
286
287 /**
288 * Register a binding if it hasn't already been registered.
289 *
290 * @param string $abstract
291 * @param Closure|string|null $concrete
292 * @param bool $shared
293 *
294 * @return void
295 */
296 public function bindIf(string $abstract, $concrete = null, bool $shared = false)
297 {
298 if ( ! $this->bound($abstract)) {
299 $this->bind($abstract, $concrete, $shared);
300 }
301 }
302
303 /**
304 * Register a shared binding in the container.
305 *
306 * @param string $abstract
307 * @param Closure|string|null $concrete
308 *
309 * @return void
310 */
311 public function singleton(string $abstract, $concrete = null)
312 {
313 $this->bind($abstract, $concrete, true);
314 }
315
316 /**
317 * Register a shared binding if it hasn't already been registered.
318 *
319 * @param string $abstract
320 * @param Closure|string|null $concrete
321 *
322 * @return void
323 */
324 public function singletonIf(string $abstract, $concrete = null)
325 {
326 if ( ! $this->bound($abstract)) {
327 $this->singleton($abstract, $concrete);
328 }
329 }
330
331 /**
332 * "Extend" an abstract type in the container.
333 *
334 * @param string $abstract
335 * @param Closure $closure
336 *
337 * @return void
338 *
339 * @throws \InvalidArgumentException
340 */
341 public function extend(string $abstract, Closure $closure)
342 {
343 $abstract = $this->getAlias($abstract);
344
345 if (isset($this->instances[$abstract])) {
346 $this->instances[$abstract] = $closure($this->instances[$abstract], $this);
347
348 $this->rebound($abstract);
349 } else {
350 $this->extenders[$abstract][] = $closure;
351
352 if ($this->resolved($abstract)) {
353 $this->rebound($abstract);
354 }
355 }
356 }
357
358 /**
359 * Register an existing instance as shared in the container.
360 *
361 * @param string $abstract
362 * @param mixed $instance
363 *
364 * @return mixed
365 */
366 public function instance(string $abstract, $instance)
367 {
368 $this->removeAbstractAlias($abstract);
369
370 $isBound = $this->bound($abstract);
371
372 unset($this->aliases[$abstract]);
373
374 // We'll check to determine if this type has been bound before, and if it has
375 // we will fire the rebound callbacks registered with the container and it
376 // can be updated with consuming classes that have gotten resolved here.
377 $this->instances[$abstract] = $instance;
378
379 if ($isBound) {
380 $this->rebound($abstract);
381 }
382
383 return $instance;
384 }
385
386 /**
387 * Remove an alias from the contextual binding alias cache.
388 *
389 * @return void
390 */
391 protected function removeAbstractAlias(string $searched)
392 {
393 if ( ! isset($this->aliases[$searched])) {
394 return;
395 }
396
397 foreach ($this->abstractAliases as $abstract => $aliases) {
398 foreach ($aliases as $index => $alias) {
399 if ($alias == $searched) {
400 unset($this->abstractAliases[$abstract][$index]);
401 }
402 }
403 }
404 }
405
406 /**
407 * Assign a set of tags to a given binding.
408 *
409 * @param array|string $abstracts
410 * @param array|mixed ...$tags
411 *
412 * @return void
413 */
414 public function tag($abstracts, $tags)
415 {
416 $tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);
417
418 foreach ($tags as $tag) {
419 if ( ! isset($this->tags[$tag])) {
420 $this->tags[$tag] = [];
421 }
422
423 foreach ((array)$abstracts as $abstract) {
424 $this->tags[$tag][] = $abstract;
425 }
426 }
427 }
428
429 /**
430 * Alias a type to a different name.
431 *
432 * @return void
433 *
434 * @throws LogicException
435 */
436 public function alias(string $abstract, string $alias)
437 {
438 if ($alias === $abstract) {
439 throw new LogicException("[{$abstract}] is aliased to itself.");
440 }
441
442 $this->aliases[$alias] = $abstract;
443
444 $this->abstractAliases[$abstract][] = $alias;
445 }
446
447 /**
448 * Bind a new callback to an abstract's rebind event.
449 *
450 * @param string $abstract
451 * @param Closure $callback
452 *
453 * @return mixed
454 */
455 public function rebinding(string $abstract, Closure $callback)
456 {
457 $this->reboundCallbacks[$abstract = $this->getAlias($abstract)][] = $callback;
458
459 if ($this->bound($abstract)) {
460 return $this->make($abstract);
461 }
462
463 return null;
464 }
465
466 /**
467 * Refresh an instance on the given target and method.
468 *
469 * @param string $abstract
470 * @param mixed $target
471 * @param string $method
472 *
473 * @return mixed
474 */
475 public function refresh(string $abstract, $target, string $method)
476 {
477 return $this->rebinding(
478 $abstract,
479 function ($app, $instance) use ($target, $method) {
480 $target->{$method}($instance);
481 }
482 );
483 }
484
485 /**
486 * Fire the "rebound" callbacks for the given abstract type.
487 *
488 * @return void
489 */
490 protected function rebound(string $abstract)
491 {
492 $instance = $this->make($abstract);
493
494 foreach ($this->getReboundCallbacks($abstract) as $callback) {
495 $callback($this, $instance);
496 }
497 }
498
499 /**
500 * Get the rebound callbacks for a given type.
501 */
502 protected function getReboundCallbacks(string $abstract): array
503 {
504 return $this->reboundCallbacks[$abstract] ?? [];
505 }
506
507 /**
508 * Get a closure to resolve the given type from the container.
509 */
510 public function factory(string $abstract): Closure
511 {
512 return function () use ($abstract) {
513 return $this->make($abstract);
514 };
515 }
516
517 /**
518 * Resolve the given type from the container.
519 *
520 * @template T
521 *
522 * @param class-string<T> $abstract
523 * @param array $parameters
524 *
525 * @return T|mixed
526 */
527 public function make(string $abstract, array $parameters = [])
528 {
529 return $this->resolve($abstract, $parameters);
530 }
531
532 /**
533 * Finds an entry of the container by its identifier and returns it.
534 *
535 * @template T
536 *
537 * @param class-string<T> $id Identifier of the entry to look for.
538 *
539 * @return T|mixed Entry.
540 * @throws InvalidArgumentException|BindingResolutionException
541 */
542 public function get(string $id)
543 {
544 try {
545 return $this->resolve($id);
546 } catch (Exception $e) {
547 if ($this->has($id)) {
548 throw $e;
549 }
550
551 throw new InvalidArgumentException($id, $e->getCode(), $e);
552 }
553 }
554
555 /**
556 * Resolve the given type from the container.
557 *
558 * @template T
559 *
560 * @param class-string<T> $abstract
561 *
562 * @return T|mixed
563 * @throws BindingResolutionException
564 */
565 protected function resolve(string $abstract, array $parameters = [], bool $raiseEvents = true)
566 {
567 $abstract = $this->getAlias($abstract);
568
569 $concrete = null;
570
571 $needsContextualBuild = false;
572
573 // If an instance of the type is currently being managed as a singleton we'll
574 // just return an existing instance instead of instantiating new instances
575 // so the developer can keep using the same objects instance every time.
576 if (isset($this->instances[$abstract])) {
577 return $this->instances[$abstract];
578 }
579
580 $this->with[] = $parameters;
581
582 if (is_null($concrete)) {
583 $concrete = $this->getConcrete($abstract);
584 }
585
586 // We're ready to instantiate an instance of the concrete type registered for
587 // the binding. This will instantiate the types, as well as resolve any of
588 // its "nested" dependencies recursively until all have gotten resolved.
589 if ($this->isBuildable($concrete, $abstract)) {
590 $object = $this->build($concrete);
591 } else {
592 $object = $this->make($concrete);
593 }
594
595 // If we defined any extenders for this type, we'll need to spin through them
596 // and apply them to the object being built. This allows for the extension
597 // of services, such as changing configuration or decorating the object.
598 foreach ($this->getExtenders($abstract) as $extender) {
599 $object = $extender($object, $this);
600 }
601
602 // If the requested type is registered as a singleton we'll want to cache off
603 // the instances in "memory" so we can return it later without creating an
604 // entirely new instance of an object on each subsequent request for it.
605 if ($this->isShared($abstract)) {
606 $this->instances[$abstract] = $object;
607 }
608
609 if ($raiseEvents) {
610 $this->fireResolvingCallbacks($abstract, $object);
611 }
612
613 // Before returning, we will also set the resolved flag to "true" and pop off
614 // the parameter overrides for this build. After those two things are done
615 // we will be ready to return back the fully constructed class instance.
616 $this->resolved[$abstract] = true;
617
618 array_pop($this->with);
619
620 return $object;
621 }
622
623 /**
624 * Get the concrete type for a given abstract.
625 *
626 * @return mixed
627 */
628 protected function getConcrete(string $abstract)
629 {
630 // If we don't have a registered resolver or concrete for the type, we'll just
631 // assume each type is a concrete name and will attempt to resolve it as is
632 // since the container should be able to resolve concretes automatically.
633 if (isset($this->bindings[$abstract])) {
634 return $this->bindings[$abstract]['concrete'];
635 }
636
637 return $abstract;
638 }
639
640 /**
641 * Determine if the given concrete is buildable.
642 *
643 * @param mixed $concrete
644 * @param string $abstract
645 *
646 * @return bool
647 */
648 protected function isBuildable($concrete, string $abstract): bool
649 {
650 return $concrete === $abstract || $concrete instanceof Closure;
651 }
652
653 /**
654 * Instantiate a concrete instance of the given type.
655 *
656 * @param Closure|string $concrete
657 *
658 * @return mixed
659 * @throws BindingResolutionException
660 * @throws ReflectionException
661 */
662 public function build($concrete)
663 {
664 // If the concrete type is actually a Closure, we will just execute it and
665 // hand back the results of the functions, which allows functions to be
666 // used as resolvers for more fine-tuned resolution of these objects.
667 if ($concrete instanceof Closure) {
668 return $concrete($this, $this->getLastParameterOverride());
669 }
670
671 try {
672 $reflector = new ReflectionClass($concrete);
673 } catch (ReflectionException $e) {
674 throw new InvalidArgumentException("Target class [$concrete] does not exist.", 0, $e);
675 }
676
677 // If the type is not instantiable, the developer is attempting to resolve
678 // an abstract type such as an Interface or Abstract Class and there is
679 // no binding registered for the abstractions so we need to bail out.
680 if ( ! $reflector->isInstantiable()) {
681 $this->notInstantiable($concrete);
682 }
683
684 $this->buildStack[] = $concrete;
685
686 $constructor = $reflector->getConstructor();
687
688 // If there are no constructors, that means there are no dependencies then
689 // we can just resolve the instances of the objects right away, without
690 // resolving any other types or dependencies out of these containers.
691 if (is_null($constructor)) {
692 array_pop($this->buildStack);
693
694 return new $concrete();
695 }
696
697 $dependencies = $constructor->getParameters();
698
699 // Once we have all the constructor's parameters we can create each of the
700 // dependency instances and then use the reflection instances to make a
701 // new instance of this class, injecting the created dependencies in.
702 try {
703 $instances = $this->resolveDependencies($dependencies);
704 } catch (BindingResolutionException $e) {
705 array_pop($this->buildStack);
706
707 throw $e;
708 }
709
710 array_pop($this->buildStack);
711
712 return $reflector->newInstanceArgs($instances);
713 }
714
715 /**
716 * Resolve all the dependencies from the ReflectionParameters.
717 *
718 * @throws BindingResolutionException
719 * @throws ReflectionException
720 */
721 protected function resolveDependencies(array $dependencies): array
722 {
723 $results = [];
724
725 foreach ($dependencies as $dependency) {
726 // If this dependency has a override for this particular build we will use
727 // that instead as the value. Otherwise, we will continue with this run
728 // of resolutions and let reflection attempt to determine the result.
729 if ($this->hasParameterOverride($dependency)) {
730 $results[] = $this->getParameterOverride($dependency);
731
732 continue;
733 }
734
735 $name = $this->getParameterClassName($dependency);
736
737 // If the class is null, it means the dependency is a string or some other
738 // primitive type which we can not resolve since it is not a class and
739 // we will just bomb out with an error since we have no-where to go.
740 $result = is_null($name)
741 ? $this->resolvePrimitive($dependency)
742 : $this->resolveClass($dependency);
743
744 if ($dependency->isVariadic()) {
745 array_push($results, ...$result);
746 } else {
747 $results[] = $result;
748 }
749 }
750
751 return $results;
752 }
753
754 /**
755 * Determine if the given dependency has a parameter override.
756 */
757 protected function hasParameterOverride(ReflectionParameter $dependency): bool
758 {
759 return array_key_exists(
760 $dependency->name,
761 $this->getLastParameterOverride()
762 );
763 }
764
765 /**
766 * Get a parameter override for a dependency.
767 *
768 * @return mixed
769 */
770 protected function getParameterOverride(ReflectionParameter $dependency)
771 {
772 return $this->getLastParameterOverride()[$dependency->name];
773 }
774
775 /**
776 * Get the last parameter override.
777 */
778 protected function getLastParameterOverride(): array
779 {
780 return count($this->with) ? end($this->with) : [];
781 }
782
783 /**
784 * Resolve a non-class hinted primitive dependency.
785 *
786 * @param ReflectionParameter $parameter
787 *
788 * @return mixed
789 * @throws BindingResolutionException
790 */
791 protected function resolvePrimitive(ReflectionParameter $parameter)
792 {
793 if ($parameter->isDefaultValueAvailable()) {
794 return $parameter->getDefaultValue();
795 }
796
797 $this->unresolvablePrimitive($parameter);
798 }
799
800 /**
801 * Resolve a class based dependency from the container.
802 *
803 * @param ReflectionParameter $parameter
804 *
805 * @return mixed
806 * @throws BindingResolutionException
807 * @throws ReflectionException
808 */
809 protected function resolveClass(ReflectionParameter $parameter)
810 {
811 try {
812 $class = $this->getParameterClassName($parameter);
813
814 if (is_null($class)) {
815 throw new BindingResolutionException();
816 }
817
818 return $this->make($class);
819 }
820
821 // If we can not resolve the class instance, we will check to see if the value
822 // is optional, and if it is we will return the optional parameter value as
823 // the value of the dependency, similarly to how we do this with scalars.
824 catch (BindingResolutionException $e) {
825 if ($parameter->isDefaultValueAvailable()) {
826 return $parameter->getDefaultValue();
827 }
828
829 if ($parameter->isVariadic()) {
830 return [];
831 }
832
833 throw $e;
834 }
835 }
836
837 /**
838 * Throw an exception that the concrete is not instantiable.
839 *
840 * @param string $concrete
841 *
842 * @return void
843 * @throws BindingResolutionException
844 */
845 protected function notInstantiable(string $concrete)
846 {
847 if ( ! empty($this->buildStack)) {
848 $previous = implode(', ', $this->buildStack);
849
850 $message = "Target [$concrete] is not instantiable while building [$previous].";
851 } else {
852 $message = "Target [$concrete] is not instantiable.";
853 }
854
855 throw new BindingResolutionException($message);
856 }
857
858 /**
859 * Throw an exception for an unresolvable primitive.
860 *
861 * @param ReflectionParameter $parameter
862 *
863 * @return void
864 * @throws BindingResolutionException
865 */
866 protected function unresolvablePrimitive(ReflectionParameter $parameter)
867 {
868 $message = "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->getName()}";
869
870 throw new BindingResolutionException($message);
871 }
872
873 /**
874 * Register a new resolving callback.
875 *
876 * @param Closure|string $abstract
877 * @param Closure|null $callback
878 *
879 * @return void
880 */
881 public function resolving($abstract, Closure $callback = null)
882 {
883 if (is_string($abstract)) {
884 $abstract = $this->getAlias($abstract);
885 }
886
887 if (is_null($callback) && $abstract instanceof Closure) {
888 $this->globalResolvingCallbacks[] = $abstract;
889 } else {
890 $this->resolvingCallbacks[$abstract][] = $callback;
891 }
892 }
893
894 /**
895 * Register a new after resolving callback for all types.
896 *
897 * @param Closure|string $abstract
898 * @param Closure|null $callback
899 *
900 * @return void
901 */
902 public function afterResolving($abstract, Closure $callback = null)
903 {
904 if (is_string($abstract)) {
905 $abstract = $this->getAlias($abstract);
906 }
907
908 if ($abstract instanceof Closure && is_null($callback)) {
909 $this->globalAfterResolvingCallbacks[] = $abstract;
910 } else {
911 $this->afterResolvingCallbacks[$abstract][] = $callback;
912 }
913 }
914
915 /**
916 * Fire all of the resolving callbacks.
917 *
918 * @param string $abstract
919 * @param mixed $object
920 *
921 * @return void
922 */
923 protected function fireResolvingCallbacks($abstract, $object)
924 {
925 $this->fireCallbackArray($object, $this->globalResolvingCallbacks);
926
927 $this->fireCallbackArray(
928 $object,
929 $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
930 );
931
932 $this->fireAfterResolvingCallbacks($abstract, $object);
933 }
934
935 /**
936 * Fire all of the after resolving callbacks.
937 *
938 * @param string $abstract
939 * @param mixed $object
940 *
941 * @return void
942 */
943 protected function fireAfterResolvingCallbacks($abstract, $object)
944 {
945 $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks);
946
947 $this->fireCallbackArray(
948 $object,
949 $this->getCallbacksForType($abstract, $object, $this->afterResolvingCallbacks)
950 );
951 }
952
953 /**
954 * Get all callbacks for a given type.
955 *
956 * @param string $abstract
957 * @param object $object
958 * @param array $callbacksPerType
959 *
960 * @return array
961 */
962 protected function getCallbacksForType(string $abstract, $object, array $callbacksPerType): array
963 {
964 $results = [];
965
966 foreach ($callbacksPerType as $type => $callbacks) {
967 if ($type === $abstract || $object instanceof $type) {
968 array_push($results, ...$callbacks);
969 }
970 }
971
972 return $results;
973 }
974
975 /**
976 * Fire an array of callbacks with an object.
977 *
978 * @param mixed $object
979 * @param array $callbacks
980 *
981 * @return void
982 */
983 protected function fireCallbackArray($object, array $callbacks)
984 {
985 foreach ($callbacks as $callback) {
986 $callback($object, $this);
987 }
988 }
989
990 /**
991 * Retrieves the class name of a given parameter with respect to the PHP version
992 *
993 * @param ReflectionParameter $parameter
994 *
995 * @return string
996 */
997 protected function getParameterClassName(ReflectionParameter $parameter)
998 {
999 // Use ReflectionParameter::getClass() prior to its replacement in PHP 7.1
1000 if (version_compare(PHP_VERSION, '7.1', '<')) {
1001 $class = $parameter->getClass();
1002
1003 return $class ? $class->name : null;
1004 }
1005
1006 $type = $parameter->getType();
1007
1008 if (! $type instanceof \ReflectionNamedType || $type->isBuiltin()) {
1009 return null;
1010 }
1011
1012 return $type->getName();
1013 }
1014
1015 /**
1016 * Get the container's bindings.
1017 */
1018 public function getBindings(): array
1019 {
1020 return $this->bindings;
1021 }
1022
1023 /**
1024 * Get the alias for an abstract if available.
1025 */
1026 public function getAlias(string $abstract): string
1027 {
1028 if ( ! isset($this->aliases[$abstract])) {
1029 return $abstract;
1030 }
1031
1032 return $this->getAlias($this->aliases[$abstract]);
1033 }
1034
1035 /**
1036 * Get the extender callbacks for a given type.
1037 */
1038 protected function getExtenders(string $abstract): array
1039 {
1040 $abstract = $this->getAlias($abstract);
1041
1042 return $this->extenders[$abstract] ?? [];
1043 }
1044
1045 /**
1046 * Remove all the extender callbacks for a given type.
1047 *
1048 * @return void
1049 */
1050 public function forgetExtenders(string $abstract)
1051 {
1052 unset($this->extenders[$this->getAlias($abstract)]);
1053 }
1054
1055 /**
1056 * Drop all the stale instances and aliases.
1057 *
1058 * @param string $abstract
1059 *
1060 * @return void
1061 */
1062 protected function dropStaleInstances(string $abstract)
1063 {
1064 unset($this->instances[$abstract], $this->aliases[$abstract]);
1065 }
1066
1067 /**
1068 * Remove a resolved instance from the instance cache.
1069 *
1070 * @return void
1071 */
1072 public function forgetInstance(string $abstract)
1073 {
1074 unset($this->instances[$abstract]);
1075 }
1076
1077 /**
1078 * Clear all the instances from the container.
1079 *
1080 * @return void
1081 */
1082 public function forgetInstances()
1083 {
1084 $this->instances = [];
1085 }
1086
1087 /**
1088 * Flush the container of all bindings and resolved instances.
1089 *
1090 * @return void
1091 */
1092 public function flush()
1093 {
1094 $this->aliases = [];
1095 $this->resolved = [];
1096 $this->bindings = [];
1097 $this->instances = [];
1098 $this->abstractAliases = [];
1099 }
1100
1101 /**
1102 * Determine if a given offset exists.
1103 *
1104 * @param string $key
1105 *
1106 * @return bool
1107 */
1108 #[\ReturnTypeWillChange]
1109 public function offsetExists($key)
1110 {
1111 return $this->bound($key);
1112 }
1113
1114 /**
1115 * Get the value at a given offset.
1116 *
1117 * @param string $key
1118 *
1119 * @return mixed
1120 */
1121 #[\ReturnTypeWillChange]
1122 public function offsetGet($key)
1123 {
1124 return $this->make($key);
1125 }
1126
1127 /**
1128 * Set the value at a given offset.
1129 *
1130 * @param string $key
1131 * @param mixed $value
1132 *
1133 * @return void
1134 */
1135 #[\ReturnTypeWillChange]
1136 public function offsetSet($key, $value)
1137 {
1138 $this->bind(
1139 $key,
1140 $value instanceof Closure ? $value : static function () use ($value) {
1141 return $value;
1142 }
1143 );
1144 }
1145
1146 /**
1147 * Unset the value at a given offset.
1148 *
1149 * @param string $key
1150 *
1151 * @return void
1152 */
1153 #[\ReturnTypeWillChange]
1154 public function offsetUnset($key)
1155 {
1156 unset($this->bindings[$key], $this->instances[$key], $this->resolved[$key]);
1157 }
1158
1159 /**
1160 * Dynamically access container services.
1161 *
1162 * @param string $key
1163 *
1164 * @return mixed
1165 */
1166 public function __get(string $key)
1167 {
1168 return $this[$key];
1169 }
1170
1171 /**
1172 * Dynamically set container services.
1173 *
1174 * @param string $key
1175 * @param mixed $value
1176 *
1177 * @return void
1178 */
1179 public function __set(string $key, $value)
1180 {
1181 $this[$key] = $value;
1182 }
1183
1184 /**
1185 * Checks to see if the key exists.
1186 *
1187 * @param $key
1188 *
1189 * @return bool
1190 */
1191 public function __isset($key): bool
1192 {
1193 return isset($this[$key]);
1194 }
1195 }
1196