PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.18.0
GiveWP – Donation Plugin and Fundraising Platform v4.18.0
4.18.0 4.17.0 4.16.9 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 All 257 releases
give / src / Container / Container.php

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

1,198 lines 31.4 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 * @since 4.17.0 Declare the nullable parameter explicitly.
881 */
882 public function resolving($abstract, ?Closure $callback = null)
883 {
884 if (is_string($abstract)) {
885 $abstract = $this->getAlias($abstract);
886 }
887
888 if (is_null($callback) && $abstract instanceof Closure) {
889 $this->globalResolvingCallbacks[] = $abstract;
890 } else {
891 $this->resolvingCallbacks[$abstract][] = $callback;
892 }
893 }
894
895 /**
896 * Register a new after resolving callback for all types.
897 *
898 * @param Closure|string $abstract
899 * @param Closure|null $callback
900 *
901 * @return void
902 * @since 4.17.0 Declare the nullable parameter explicitly.
903 */
904 public function afterResolving($abstract, ?Closure $callback = null)
905 {
906 if (is_string($abstract)) {
907 $abstract = $this->getAlias($abstract);
908 }
909
910 if ($abstract instanceof Closure && is_null($callback)) {
911 $this->globalAfterResolvingCallbacks[] = $abstract;
912 } else {
913 $this->afterResolvingCallbacks[$abstract][] = $callback;
914 }
915 }
916
917 /**
918 * Fire all of the resolving callbacks.
919 *
920 * @param string $abstract
921 * @param mixed $object
922 *
923 * @return void
924 */
925 protected function fireResolvingCallbacks($abstract, $object)
926 {
927 $this->fireCallbackArray($object, $this->globalResolvingCallbacks);
928
929 $this->fireCallbackArray(
930 $object,
931 $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
932 );
933
934 $this->fireAfterResolvingCallbacks($abstract, $object);
935 }
936
937 /**
938 * Fire all of the after resolving callbacks.
939 *
940 * @param string $abstract
941 * @param mixed $object
942 *
943 * @return void
944 */
945 protected function fireAfterResolvingCallbacks($abstract, $object)
946 {
947 $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks);
948
949 $this->fireCallbackArray(
950 $object,
951 $this->getCallbacksForType($abstract, $object, $this->afterResolvingCallbacks)
952 );
953 }
954
955 /**
956 * Get all callbacks for a given type.
957 *
958 * @param string $abstract
959 * @param object $object
960 * @param array $callbacksPerType
961 *
962 * @return array
963 */
964 protected function getCallbacksForType(string $abstract, $object, array $callbacksPerType): array
965 {
966 $results = [];
967
968 foreach ($callbacksPerType as $type => $callbacks) {
969 if ($type === $abstract || $object instanceof $type) {
970 array_push($results, ...$callbacks);
971 }
972 }
973
974 return $results;
975 }
976
977 /**
978 * Fire an array of callbacks with an object.
979 *
980 * @param mixed $object
981 * @param array $callbacks
982 *
983 * @return void
984 */
985 protected function fireCallbackArray($object, array $callbacks)
986 {
987 foreach ($callbacks as $callback) {
988 $callback($object, $this);
989 }
990 }
991
992 /**
993 * Retrieves the class name of a given parameter with respect to the PHP version
994 *
995 * @param ReflectionParameter $parameter
996 *
997 * @return string
998 */
999 protected function getParameterClassName(ReflectionParameter $parameter)
1000 {
1001 // Use ReflectionParameter::getClass() prior to its replacement in PHP 7.1
1002 if (version_compare(PHP_VERSION, '7.1', '<')) {
1003 $class = $parameter->getClass();
1004
1005 return $class ? $class->name : null;
1006 }
1007
1008 $type = $parameter->getType();
1009
1010 if (! $type instanceof \ReflectionNamedType || $type->isBuiltin()) {
1011 return null;
1012 }
1013
1014 return $type->getName();
1015 }
1016
1017 /**
1018 * Get the container's bindings.
1019 */
1020 public function getBindings(): array
1021 {
1022 return $this->bindings;
1023 }
1024
1025 /**
1026 * Get the alias for an abstract if available.
1027 */
1028 public function getAlias(string $abstract): string
1029 {
1030 if ( ! isset($this->aliases[$abstract])) {
1031 return $abstract;
1032 }
1033
1034 return $this->getAlias($this->aliases[$abstract]);
1035 }
1036
1037 /**
1038 * Get the extender callbacks for a given type.
1039 */
1040 protected function getExtenders(string $abstract): array
1041 {
1042 $abstract = $this->getAlias($abstract);
1043
1044 return $this->extenders[$abstract] ?? [];
1045 }
1046
1047 /**
1048 * Remove all the extender callbacks for a given type.
1049 *
1050 * @return void
1051 */
1052 public function forgetExtenders(string $abstract)
1053 {
1054 unset($this->extenders[$this->getAlias($abstract)]);
1055 }
1056
1057 /**
1058 * Drop all the stale instances and aliases.
1059 *
1060 * @param string $abstract
1061 *
1062 * @return void
1063 */
1064 protected function dropStaleInstances(string $abstract)
1065 {
1066 unset($this->instances[$abstract], $this->aliases[$abstract]);
1067 }
1068
1069 /**
1070 * Remove a resolved instance from the instance cache.
1071 *
1072 * @return void
1073 */
1074 public function forgetInstance(string $abstract)
1075 {
1076 unset($this->instances[$abstract]);
1077 }
1078
1079 /**
1080 * Clear all the instances from the container.
1081 *
1082 * @return void
1083 */
1084 public function forgetInstances()
1085 {
1086 $this->instances = [];
1087 }
1088
1089 /**
1090 * Flush the container of all bindings and resolved instances.
1091 *
1092 * @return void
1093 */
1094 public function flush()
1095 {
1096 $this->aliases = [];
1097 $this->resolved = [];
1098 $this->bindings = [];
1099 $this->instances = [];
1100 $this->abstractAliases = [];
1101 }
1102
1103 /**
1104 * Determine if a given offset exists.
1105 *
1106 * @param string $key
1107 *
1108 * @return bool
1109 */
1110 #[\ReturnTypeWillChange]
1111 public function offsetExists($key)
1112 {
1113 return $this->bound($key);
1114 }
1115
1116 /**
1117 * Get the value at a given offset.
1118 *
1119 * @param string $key
1120 *
1121 * @return mixed
1122 */
1123 #[\ReturnTypeWillChange]
1124 public function offsetGet($key)
1125 {
1126 return $this->make($key);
1127 }
1128
1129 /**
1130 * Set the value at a given offset.
1131 *
1132 * @param string $key
1133 * @param mixed $value
1134 *
1135 * @return void
1136 */
1137 #[\ReturnTypeWillChange]
1138 public function offsetSet($key, $value)
1139 {
1140 $this->bind(
1141 $key,
1142 $value instanceof Closure ? $value : static function () use ($value) {
1143 return $value;
1144 }
1145 );
1146 }
1147
1148 /**
1149 * Unset the value at a given offset.
1150 *
1151 * @param string $key
1152 *
1153 * @return void
1154 */
1155 #[\ReturnTypeWillChange]
1156 public function offsetUnset($key)
1157 {
1158 unset($this->bindings[$key], $this->instances[$key], $this->resolved[$key]);
1159 }
1160
1161 /**
1162 * Dynamically access container services.
1163 *
1164 * @param string $key
1165 *
1166 * @return mixed
1167 */
1168 public function __get(string $key)
1169 {
1170 return $this[$key];
1171 }
1172
1173 /**
1174 * Dynamically set container services.
1175 *
1176 * @param string $key
1177 * @param mixed $value
1178 *
1179 * @return void
1180 */
1181 public function __set(string $key, $value)
1182 {
1183 $this[$key] = $value;
1184 }
1185
1186 /**
1187 * Checks to see if the key exists.
1188 *
1189 * @param $key
1190 *
1191 * @return bool
1192 */
1193 public function __isset($key): bool
1194 {
1195 return isset($this[$key]);
1196 }
1197 }
1198