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