PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.14
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.14
6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 All 196 releases
fluentform / vendor / wpfluent / framework / src / WPFluent / Container / Container.php

Container.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.14, at vendor/wpfluent/framework/src/WPFluent/Container/Container.php

1,516 lines 40.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentForm\Framework\Container;
4
5 use Closure;
6 use Exception;
7 use TypeError;
8 use ArrayAccess;
9 use LogicException;
10 use ReflectionClass;
11 use ReflectionException;
12 use ReflectionParameter;
13 use FluentForm\Framework\Container\Contracts\BindingResolutionException;
14 use FluentForm\Framework\Container\Contracts\CircularDependencyException;
15 use FluentForm\Framework\Container\Contracts\Container as ContainerContract;
16
17 class Container implements ArrayAccess, ContainerContract
18 {
19 /**
20 * The current globally available container (if any).
21 *
22 * @var static
23 */
24 protected static $instance;
25
26 /**
27 * An array of the types that have been resolved.
28 *
29 * @var bool[]
30 */
31 protected $resolved = [];
32
33 /**
34 * The container's bindings.
35 *
36 * @var array[]
37 */
38 protected $bindings = [];
39
40 /**
41 * The container's method bindings.
42 *
43 * @var \Closure[]
44 */
45 protected $methodBindings = [];
46
47 /**
48 * The container's shared instances.
49 *
50 * @var object[]
51 */
52 protected $instances = [];
53
54 /**
55 * The container's scoped instances.
56 *
57 * @var array
58 */
59 protected $scopedInstances = [];
60
61 /**
62 * The registered type aliases.
63 *
64 * @var string[]
65 */
66 protected $aliases = [];
67
68 /**
69 * The registered aliases keyed by the abstract name.
70 *
71 * @var array[]
72 */
73 protected $abstractAliases = [];
74
75 /**
76 * The extension closures for services.
77 *
78 * @var array[]
79 */
80 protected $extenders = [];
81
82 /**
83 * All of the registered tags.
84 *
85 * @var array[]
86 */
87 protected $tags = [];
88
89 /**
90 * The stack of concretions currently being built.
91 *
92 * @var array[]
93 */
94 protected $buildStack = [];
95
96 /**
97 * The parameter override stack.
98 *
99 * @var array[]
100 */
101 protected $with = [];
102
103 /**
104 * The contextual binding map.
105 *
106 * @var array[]
107 */
108 public $contextual = [];
109
110 /**
111 * All of the registered rebound callbacks.
112 *
113 * @var array[]
114 */
115 protected $reboundCallbacks = [];
116
117 /**
118 * All of the global before resolving callbacks.
119 *
120 * @var \Closure[]
121 */
122 protected $globalBeforeResolvingCallbacks = [];
123
124 /**
125 * All of the global resolving callbacks.
126 *
127 * @var \Closure[]
128 */
129 protected $globalResolvingCallbacks = [];
130
131 /**
132 * All of the global after resolving callbacks.
133 *
134 * @var \Closure[]
135 */
136 protected $globalAfterResolvingCallbacks = [];
137
138 /**
139 * All of the before resolving callbacks by class type.
140 *
141 * @var array[]
142 */
143 protected $beforeResolvingCallbacks = [];
144
145 /**
146 * All of the resolving callbacks by class type.
147 *
148 * @var array[]
149 */
150 protected $resolvingCallbacks = [];
151
152 /**
153 * All of the after resolving callbacks by class type.
154 *
155 * @var array[]
156 */
157 protected $afterResolvingCallbacks = [];
158
159 /**
160 * Define a contextual binding.
161 *
162 * @param array|string $concrete
163 * @return \FluentForm\Framework\Container\Contracts\ContextualBindingBuilder
164 */
165 public function when($concrete)
166 {
167 $aliases = [];
168
169 foreach (Util::arrayWrap($concrete) as $c) {
170 $aliases[] = $this->getAlias($c);
171 }
172
173 return new ContextualBindingBuilder($this, $aliases);
174 }
175
176 /**
177 * Determine if the given abstract type has been bound.
178 *
179 * @param string $abstract
180 * @return bool
181 */
182 public function bound($abstract)
183 {
184 return isset($this->bindings[$abstract]) ||
185 isset($this->instances[$abstract]) ||
186 $this->isAlias($abstract);
187 }
188
189 /**
190 * {@inheritdoc}
191 *
192 * @return bool
193 */
194 public function has($id)
195 {
196 return $this->bound($id);
197 }
198
199 /**
200 * Determine if the given abstract type has been resolved.
201 *
202 * @param string $abstract
203 * @return bool
204 */
205 public function resolved($abstract)
206 {
207 if ($this->isAlias($abstract)) {
208 $abstract = $this->getAlias($abstract);
209 }
210
211 return isset($this->resolved[$abstract]) ||
212 isset($this->instances[$abstract]);
213 }
214
215 /**
216 * Determine if a given type is shared.
217 *
218 * @param string $abstract
219 * @return bool
220 */
221 public function isShared($abstract)
222 {
223 return isset($this->instances[$abstract]) ||
224 (isset($this->bindings[$abstract]['shared']) &&
225 $this->bindings[$abstract]['shared'] === true);
226 }
227
228 /**
229 * Determine if a given string is an alias.
230 *
231 * @param string $name
232 * @return bool
233 */
234 public function isAlias($name)
235 {
236 return isset($this->aliases[$name]);
237 }
238
239 /**
240 * Register a binding with the container.
241 *
242 * @param string $abstract
243 * @param \Closure|string|null $concrete
244 * @param bool $shared
245 * @return void
246 *
247 * @throws \TypeError
248 */
249 public function bind($abstract, $concrete = null, $shared = false)
250 {
251 $this->dropStaleInstances($abstract);
252
253 // If no concrete type was given, we will simply set the concrete
254 // type to the abstract type. After that, the concrete type to
255 // be registered as shared without being forced to state
256 // their classes in both of the parameters.
257 if (is_null($concrete)) {
258 $concrete = $abstract;
259 }
260
261 // If the factory is not a Closure, it means it is just a class name
262 // which is bound into this container to the abstract type and we
263 // will just wrap it up inside its own Closure to give us more
264 // convenience when extending.
265 if (! $concrete instanceof Closure) {
266 if (! is_string($concrete)) {
267 throw new TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');
268 }
269
270 $concrete = $this->getClosure($abstract, $concrete);
271 }
272
273 $this->bindings[$abstract] = compact('concrete', 'shared');
274
275 // If the abstract type was already resolved in this container
276 // we'll fire the rebound listener so that any objects which
277 // have already gotten resolved can have their copy of the
278 // object updated via the listener callbacks.
279 if ($this->resolved($abstract)) {
280 $this->rebound($abstract);
281 }
282 }
283
284 /**
285 * Get the Closure to be used when building a type.
286 *
287 * @param string $abstract
288 * @param string $concrete
289 * @return \Closure
290 */
291 protected function getClosure($abstract, $concrete)
292 {
293 return function ($container, $parameters = []) use ($abstract, $concrete) {
294 if ($abstract == $concrete) {
295 return $container->build($concrete);
296 }
297
298 return $container->resolve(
299 $concrete, $parameters, $raiseEvents = false
300 );
301 };
302 }
303
304 /**
305 * Determine if the container has a method binding.
306 *
307 * @param string $method
308 * @return bool
309 */
310 public function hasMethodBinding($method)
311 {
312 return isset($this->methodBindings[$method]);
313 }
314
315 /**
316 * Bind a callback to resolve with Container::call.
317 *
318 * @param array|string $method
319 * @param \Closure $callback
320 * @return void
321 */
322 public function bindMethod($method, $callback)
323 {
324 $this->methodBindings[$this->parseBindMethod($method)] = $callback;
325 }
326
327 /**
328 * Get the method to be bound in class@method format.
329 *
330 * @param array|string $method
331 * @return string
332 */
333 protected function parseBindMethod($method)
334 {
335 if (is_array($method)) {
336 return $method[0].'@'.$method[1];
337 }
338
339 return $method;
340 }
341
342 /**
343 * Get the method binding for the given method.
344 *
345 * @param string $method
346 * @param mixed $instance
347 * @return mixed
348 */
349 public function callMethodBinding($method, $instance)
350 {
351 return call_user_func($this->methodBindings[$method], $instance, $this);
352 }
353
354 /**
355 * Add a contextual binding to the container.
356 *
357 * @param string $concrete
358 * @param string $abstract
359 * @param \Closure|string $implementation
360 * @return void
361 */
362 public function addContextualBinding($concrete, $abstract, $implementation)
363 {
364 $this->contextual[$concrete][$this->getAlias($abstract)] = $implementation;
365 }
366
367 /**
368 * Register a binding if it hasn't already been registered.
369 *
370 * @param string $abstract
371 * @param \Closure|string|null $concrete
372 * @param bool $shared
373 * @return void
374 */
375 public function bindIf($abstract, $concrete = null, $shared = false)
376 {
377 if (! $this->bound($abstract)) {
378 $this->bind($abstract, $concrete, $shared);
379 }
380 }
381
382 /**
383 * Register a shared binding in the container.
384 *
385 * @param string $abstract
386 * @param \Closure|string|null $concrete
387 * @return void
388 */
389 public function singleton($abstract, $concrete = null)
390 {
391 $this->bind($abstract, $concrete, true);
392 }
393
394 /**
395 * Register a shared binding if it hasn't already been registered.
396 *
397 * @param string $abstract
398 * @param \Closure|string|null $concrete
399 * @return void
400 */
401 public function singletonIf($abstract, $concrete = null)
402 {
403 if (! $this->bound($abstract)) {
404 $this->singleton($abstract, $concrete);
405 }
406 }
407
408 /**
409 * Register a scoped binding in the container.
410 *
411 * @param string $abstract
412 * @param \Closure|string|null $concrete
413 * @return void
414 */
415 public function scoped($abstract, $concrete = null)
416 {
417 $this->scopedInstances[] = $abstract;
418
419 $this->singleton($abstract, $concrete);
420 }
421
422 /**
423 * Register a scoped binding if it hasn't already been registered.
424 *
425 * @param string $abstract
426 * @param \Closure|string|null $concrete
427 * @return void
428 */
429 public function scopedIf($abstract, $concrete = null)
430 {
431 if (! $this->bound($abstract)) {
432 $this->scopedInstances[] = $abstract;
433
434 $this->singleton($abstract, $concrete);
435 }
436 }
437
438 /**
439 * "Extend" an abstract type in the container.
440 *
441 * @param string $abstract
442 * @param \Closure $closure
443 * @return void
444 *
445 * @throws \InvalidArgumentException
446 */
447 public function extend($abstract, Closure $closure)
448 {
449 $abstract = $this->getAlias($abstract);
450
451 if (isset($this->instances[$abstract])) {
452 $this->instances[$abstract] = $closure($this->instances[$abstract], $this);
453
454 $this->rebound($abstract);
455 } else {
456 $this->extenders[$abstract][] = $closure;
457
458 if ($this->resolved($abstract)) {
459 $this->rebound($abstract);
460 }
461 }
462 }
463
464 /**
465 * Register an existing instance as shared in the container.
466 *
467 * @param string $abstract
468 * @param mixed $instance
469 * @return mixed
470 */
471 public function instance($abstract, $instance)
472 {
473 $this->removeAbstractAlias($abstract);
474
475 $isBound = $this->bound($abstract);
476
477 unset($this->aliases[$abstract]);
478
479 // We'll check to determine if this type has been bound before, and
480 // if it has we will fire the rebound callbacks registered with
481 // the container and it can be updated with consuming classes
482 // that have gotten resolved here.
483 $this->instances[$abstract] = $instance;
484
485 if ($isBound) {
486 $this->rebound($abstract);
487 }
488
489 return $instance;
490 }
491
492 /**
493 * Remove an alias from the contextual binding alias cache.
494 *
495 * @param string $searched
496 * @return void
497 */
498 protected function removeAbstractAlias($searched)
499 {
500 if (! isset($this->aliases[$searched])) {
501 return;
502 }
503
504 foreach ($this->abstractAliases as $abstract => $aliases) {
505 foreach ($aliases as $index => $alias) {
506 if ($alias == $searched) {
507 unset($this->abstractAliases[$abstract][$index]);
508 }
509 }
510 }
511 }
512
513 /**
514 * Assign a set of tags to a given binding.
515 *
516 * @param array|string $abstracts
517 * @param array|mixed ...$tags
518 * @return void
519 */
520 public function tag($abstracts, $tags)
521 {
522 $tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);
523
524 foreach ($tags as $tag) {
525 if (! isset($this->tags[$tag])) {
526 $this->tags[$tag] = [];
527 }
528
529 foreach ((array) $abstracts as $abstract) {
530 $this->tags[$tag][] = $abstract;
531 }
532 }
533 }
534
535 /**
536 * Resolve all of the bindings for a given tag.
537 *
538 * @param string $tag
539 * @return iterable
540 */
541 public function tagged($tag)
542 {
543 if (! isset($this->tags[$tag])) {
544 return [];
545 }
546
547 return new RewindableGenerator(function () use ($tag) {
548 foreach ($this->tags[$tag] as $abstract) {
549 yield $this->make($abstract);
550 }
551 }, count($this->tags[$tag]));
552 }
553
554 /**
555 * Alias a type to a different name.
556 *
557 * @param string $abstract
558 * @param string $alias
559 * @return void
560 *
561 * @throws \LogicException
562 */
563 public function alias($abstract, $alias)
564 {
565 if ($alias === $abstract) {
566 throw new LogicException("[{$abstract}] is aliased to itself.");
567 }
568
569 $this->aliases[$alias] = $abstract;
570
571 $this->abstractAliases[$abstract][] = $alias;
572 }
573
574 /**
575 * Bind a new callback to an abstract's rebind event.
576 *
577 * @param string $abstract
578 * @param \Closure $callback
579 * @return mixed
580 */
581 public function rebinding($abstract, Closure $callback)
582 {
583 $this->reboundCallbacks[$abstract = $this->getAlias($abstract)][] = $callback;
584
585 if ($this->bound($abstract)) {
586 return $this->make($abstract);
587 }
588 }
589
590 /**
591 * Refresh an instance on the given target and method.
592 *
593 * @param string $abstract
594 * @param mixed $target
595 * @param string $method
596 * @return mixed
597 */
598 public function refresh($abstract, $target, $method)
599 {
600 return $this->rebinding($abstract, function ($app, $instance) use ($target, $method) {
601 $target->{$method}($instance);
602 });
603 }
604
605 /**
606 * Fire the "rebound" callbacks for the given abstract type.
607 *
608 * @param string $abstract
609 * @return void
610 */
611 protected function rebound($abstract)
612 {
613 $instance = $this->make($abstract);
614
615 foreach ($this->getReboundCallbacks($abstract) as $callback) {
616 call_user_func($callback, $this, $instance);
617 }
618 }
619
620 /**
621 * Get the rebound callbacks for a given type.
622 *
623 * @param string $abstract
624 * @return array
625 */
626 protected function getReboundCallbacks($abstract)
627 {
628 return $this->reboundCallbacks[$abstract] ?? [];
629 }
630
631 /**
632 * Wrap the given closure such that its dependencies will be injected when executed.
633 *
634 * @param \Closure $callback
635 * @param array $parameters
636 * @return \Closure
637 */
638 public function wrap(Closure $callback, array $parameters = [])
639 {
640 return function () use ($callback, $parameters) {
641 return $this->call($callback, $parameters);
642 };
643 }
644
645 /**
646 * Call the given Closure / class@method and inject its dependencies.
647 *
648 * @param callable|string $callback
649 * @param array<string, mixed> $parameters
650 * @param string|null $defaultMethod
651 * @return mixed
652 *
653 * @throws \InvalidArgumentException
654 */
655 public function call($callback, array $parameters = [], $defaultMethod = null)
656 {
657 return BoundMethod::call($this, $callback, $parameters, $defaultMethod);
658 }
659
660 /**
661 * Get a closure to resolve the given type from the container.
662 *
663 * @param string $abstract
664 * @return \Closure
665 */
666 public function factory($abstract)
667 {
668 return function () use ($abstract) {
669 return $this->make($abstract);
670 };
671 }
672
673 /**
674 * An alias function name for make().
675 *
676 * @param string|callable $abstract
677 * @param array $parameters
678 * @return mixed
679 *
680 * @throws \FluentForm\Framework\Container\Contracts\BindingResolutionException
681 */
682 public function makeWith($abstract, array $parameters = [])
683 {
684 return $this->make($abstract, $parameters);
685 }
686
687 /**
688 * Resolve the given type from the container.
689 *
690 * @param string|callable $abstract
691 * @param array $parameters
692 * @return mixed
693 *
694 * @throws \FluentForm\Framework\Container\Contracts\BindingResolutionException
695 */
696 public function make($abstract, array $parameters = [])
697 {
698 try {
699 return $this->resolve($abstract, $parameters);
700 } catch (BindingResolutionException $e) {
701 if (class_exists($class = $this->retry($abstract))) {
702 return $this->make($class, $parameters);
703 }
704
705 throw $e;
706 }
707 }
708
709 /**
710 * Try to resolve the given service from the Framework context.
711 *
712 * @param string|null $module [description]
713 * @return string
714 */
715 protected function retry($module)
716 {
717 $pieces = explode('\\', __NAMESPACE__);
718 array_pop($pieces);
719 $prefix = implode('\\', $pieces);
720
721 return $prefix . '\\' . str_replace('.', '\\', $module);
722 }
723
724 /**
725 * {@inheritdoc}
726 *
727 * @return mixed
728 */
729 public function get($id)
730 {
731 try {
732 return $this->resolve($id);
733 } catch (Exception $e) {
734 if ($this->has($id) || $e instanceof CircularDependencyException) {
735 throw $e;
736 }
737
738 throw new EntryNotFoundException($id, $e->getCode(), $e);
739 }
740 }
741
742 /**
743 * Resolve the given type from the container.
744 *
745 * @param string|callable $abstract
746 * @param array $parameters
747 * @param bool $raiseEvents
748 * @return mixed
749 *
750 * @throws \FluentForm\Framework\Container\Contracts\BindingResolutionException
751 * @throws \FluentForm\Framework\Container\Contracts\CircularDependencyException
752 */
753 protected function resolve($abstract, $parameters = [], $raiseEvents = true)
754 {
755 $abstract = $this->getAlias($abstract);
756
757 // First we'll fire any event handlers which handle the "before"
758 // resolving of specific types. This gives some hooks the
759 // chance to add various extends calls to change the
760 // resolution of objects that they're interested in.
761 if ($raiseEvents) {
762 $this->fireBeforeResolvingCallbacks($abstract, $parameters);
763 }
764
765 $concrete = $this->getContextualConcrete($abstract);
766
767 $needsContextualBuild = ! empty($parameters) || ! is_null($concrete);
768
769 // If an instance of the type is currently being managed as a
770 // singleton we'll just return an existing instance
771 // instead of instantiating new instances so the
772 // developer can keep using the same objects
773 // instance every time.
774 if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
775 return $this->instances[$abstract];
776 }
777
778 $this->with[] = $parameters;
779
780 if (is_null($concrete)) {
781 $concrete = $this->getConcrete($abstract);
782 }
783
784 // We're ready to instantiate an instance of the concrete type
785 // registered for the binding. This will instantiate the types,
786 // as well as resolve any of its "nested" dependencies
787 // recursively until all have gotten resolved.
788 if ($this->isBuildable($concrete, $abstract)) {
789 $object = $this->build($concrete);
790 } else {
791 $object = $this->make($concrete);
792 }
793
794 // If we defined any extenders for this type, we'll need to spin
795 // through them and apply them to the object being built.
796 // This allows for the extension of services, such as
797 // changing configuration or decorating the object.
798 foreach ($this->getExtenders($abstract) as $extender) {
799 $object = $extender($object, $this);
800 }
801
802 // If the requested type is registered as a singleton we'll want to
803 // cache off the instances in "memory" so we can return it later
804 // without creating an entirely new instance of an object
805 // on each subsequent request for it.
806 if ($this->isShared($abstract) && ! $needsContextualBuild) {
807 $this->instances[$abstract] = $object;
808 }
809
810 if ($raiseEvents) {
811 $this->fireResolvingCallbacks($abstract, $object);
812 }
813
814 // Before returning, we will also set the resolved flag to "true"
815 // and pop off the parameter overrides for this build. After
816 // those two things are done we will be ready to return
817 // back the fully constructed class instance.
818 $this->resolved[$abstract] = true;
819
820 array_pop($this->with);
821
822 return $object;
823 }
824
825 /**
826 * Get the concrete type for a given abstract.
827 *
828 * @param string|callable $abstract
829 * @return mixed
830 */
831 protected function getConcrete($abstract)
832 {
833 // If we don't have a registered resolver or concrete for the type,
834 // we'll just assume each type is a concrete name and will
835 // attempt to resolve it as is since the container should
836 // be able to resolve concretes automatically.
837 if (isset($this->bindings[$abstract])) {
838 return $this->bindings[$abstract]['concrete'];
839 }
840
841 return $abstract;
842 }
843
844 /**
845 * Get the contextual concrete binding for the given abstract.
846 *
847 * @param string|callable $abstract
848 * @return \Closure|string|array|null
849 */
850 protected function getContextualConcrete($abstract)
851 {
852 if (! is_null($binding = $this->findInContextualBindings($abstract))) {
853 return $binding;
854 }
855
856 // Next we need to see if a contextual binding might be bound under
857 // an alias of the given abstract type. So, we will need to check
858 // if any aliases exist with this type and then spin through
859 // them and check for contextual bindings on these.
860 if (empty($this->abstractAliases[$abstract])) {
861 return;
862 }
863
864 foreach ($this->abstractAliases[$abstract] as $alias) {
865 if (! is_null($binding = $this->findInContextualBindings($alias))) {
866 return $binding;
867 }
868 }
869 }
870
871 /**
872 * Find the concrete binding for the given abstract
873 * in the contextual binding array.
874 *
875 * @param string|callable $abstract
876 * @return \Closure|string|null
877 */
878 protected function findInContextualBindings($abstract)
879 {
880 return $this->contextual[end($this->buildStack)][$abstract] ?? null;
881 }
882
883 /**
884 * Determine if the given concrete is buildable.
885 *
886 * @param mixed $concrete
887 * @param string $abstract
888 * @return bool
889 */
890 protected function isBuildable($concrete, $abstract)
891 {
892 return $concrete === $abstract || $concrete instanceof Closure;
893 }
894
895 /**
896 * Instantiate a concrete instance of the given type.
897 *
898 * @param \Closure|string $concrete
899 * @return mixed
900 *
901 * @throws \FluentForm\Framework\Container\Contracts\BindingResolutionException
902 * @throws \FluentForm\Framework\Container\Contracts\CircularDependencyException
903 */
904 public function build($concrete)
905 {
906 // If the concrete type is actually a Closure, we will just execute it and
907 // hand back the results of the functions, which allows functions to be
908 // used as resolvers for more fine-tuned resolution of these objects.
909 if ($concrete instanceof Closure) {
910 return $concrete($this, $this->getLastParameterOverride());
911 }
912
913 try {
914 $reflector = new ReflectionClass($concrete);
915 } catch (ReflectionException $e) {
916 throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
917 }
918
919 // If the type is not instantiable, the developer is attempting to resolve
920 // an abstract type such as an Interface or Abstract Class and there is
921 // no binding registered for the abstractions so we need to bail out.
922 if (! $reflector->isInstantiable()) {
923 return $this->notInstantiable($concrete);
924 }
925
926 $this->buildStack[] = $concrete;
927
928 $constructor = $reflector->getConstructor();
929
930 // If there are no constructors, that means there are no dependencies then
931 // we can just resolve the instances of the objects right away, without
932 // resolving any other types or dependencies out of these containers.
933 if (is_null($constructor)) {
934 array_pop($this->buildStack);
935
936 return new $concrete;
937 }
938
939 $dependencies = $constructor->getParameters();
940
941 // Once we have all the constructor's parameters we can create each of the
942 // dependency instances and then use the reflection instances to make a
943 // new instance of this class, injecting the created dependencies in.
944 try {
945 $instances = $this->resolveDependencies($dependencies);
946 } catch (BindingResolutionException $e) {
947 array_pop($this->buildStack);
948
949 throw $e;
950 }
951
952 array_pop($this->buildStack);
953
954 return $reflector->newInstanceArgs($instances);
955 }
956
957 /**
958 * Resolve all of the dependencies from the ReflectionParameters.
959 *
960 * @param \ReflectionParameter[] $dependencies
961 * @return array
962 *
963 * @throws \FluentForm\Framework\Container\Contracts\BindingResolutionException
964 */
965 protected function resolveDependencies(array $dependencies)
966 {
967 $results = [];
968
969 foreach ($dependencies as $dependency) {
970 // If the dependency has an override for this particular build
971 // we will use that instead as the value. Otherwise, we will
972 // continue with this run of resolutions and let reflection
973 // attempt to determine the result.
974 if ($this->hasParameterOverride($dependency)) {
975 $results[] = $this->getParameterOverride($dependency);
976
977 continue;
978 }
979
980 // If the class is null, it means the dependency is a string or
981 // some other primitive type which we can not resolve since
982 // it is not a class and we will just bomb out with an
983 // error since we have no-where to go.
984 $result = is_null(Util::getParameterClassName($dependency))
985 ? $this->resolvePrimitive($dependency)
986 : $this->resolveClass($dependency);
987
988 if ($dependency->isVariadic()) {
989 $results = array_merge($results, $result);
990 } else {
991 $results[] = $result;
992 }
993 }
994
995 return $results;
996 }
997
998 /**
999 * Determine if the given dependency has a parameter override.
1000 *
1001 * @param \ReflectionParameter $dependency
1002 * @return bool
1003 */
1004 protected function hasParameterOverride($dependency)
1005 {
1006 return array_key_exists(
1007 $dependency->name, $this->getLastParameterOverride()
1008 );
1009 }
1010
1011 /**
1012 * Get a parameter override for a dependency.
1013 *
1014 * @param \ReflectionParameter $dependency
1015 * @return mixed
1016 */
1017 protected function getParameterOverride($dependency)
1018 {
1019 return $this->getLastParameterOverride()[$dependency->name];
1020 }
1021
1022 /**
1023 * Get the last parameter override.
1024 *
1025 * @return array
1026 */
1027 protected function getLastParameterOverride()
1028 {
1029 return count($this->with) ? end($this->with) : [];
1030 }
1031
1032 /**
1033 * Resolve a non-class hinted primitive dependency.
1034 *
1035 * @param \ReflectionParameter $parameter
1036 * @return mixed
1037 *
1038 * @throws \FluentForm\Framework\Container\Contracts\BindingResolutionException
1039 */
1040 protected function resolvePrimitive(ReflectionParameter $parameter)
1041 {
1042 if (! is_null($concrete = $this->getContextualConcrete('$'.$parameter->getName()))) {
1043 return $concrete instanceof Closure ? $concrete($this) : $concrete;
1044 }
1045
1046 if ($parameter->isDefaultValueAvailable()) {
1047 return $parameter->getDefaultValue();
1048 }
1049
1050 $this->unresolvablePrimitive($parameter);
1051 }
1052
1053 /**
1054 * Resolve a class based dependency from the container.
1055 *
1056 * @param \ReflectionParameter $parameter
1057 * @return mixed
1058 *
1059 * @throws \FluentForm\Framework\Container\Contracts\BindingResolutionException
1060 */
1061 protected function resolveClass(ReflectionParameter $parameter)
1062 {
1063 try {
1064 return $parameter->isVariadic()
1065 ? $this->resolveVariadicClass($parameter)
1066 : $this->make(Util::getParameterClassName($parameter));
1067 }
1068
1069 // If we can not resolve the class instance, we will check to see
1070 // if the value is optional, and if it is we will return the
1071 // optional parameter value as the value of the dependency,
1072 // similarly to how we do this with scalars.
1073 catch (BindingResolutionException $e) {
1074 if ($parameter->isDefaultValueAvailable()) {
1075 array_pop($this->with);
1076
1077 return $parameter->getDefaultValue();
1078 }
1079
1080 if ($parameter->isVariadic()) {
1081 array_pop($this->with);
1082
1083 return [];
1084 }
1085
1086 throw $e;
1087 }
1088 }
1089
1090 /**
1091 * Resolve a class based variadic dependency from the container.
1092 *
1093 * @param \ReflectionParameter $parameter
1094 * @return mixed
1095 */
1096 protected function resolveVariadicClass(ReflectionParameter $parameter)
1097 {
1098 $className = Util::getParameterClassName($parameter);
1099
1100 $abstract = $this->getAlias($className);
1101
1102 if (! is_array($concrete = $this->getContextualConcrete($abstract))) {
1103 return $this->make($className);
1104 }
1105
1106 return array_map(function ($abstract) {
1107 return $this->resolve($abstract);
1108 }, $concrete);
1109 }
1110
1111 /**
1112 * Throw an exception that the concrete is not instantiable.
1113 *
1114 * @param string $concrete
1115 * @return never
1116 *
1117 * @throws \FluentForm\Framework\Container\Contracts\BindingResolutionException
1118 */
1119 protected function notInstantiable($concrete)
1120 {
1121 if (! empty($this->buildStack)) {
1122 $previous = implode(', ', $this->buildStack);
1123
1124 $message = "Target [$concrete] is not instantiable while building [$previous].";
1125 } else {
1126 $message = "Target [$concrete] is not instantiable.";
1127 }
1128
1129 throw new BindingResolutionException($message);
1130 }
1131
1132 /**
1133 * Throw an exception for an unresolvable primitive.
1134 *
1135 * @param \ReflectionParameter $parameter
1136 * @return void
1137 *
1138 * @throws \FluentForm\Framework\Container\Contracts\BindingResolutionException
1139 */
1140 protected function unresolvablePrimitive(ReflectionParameter $parameter)
1141 {
1142 $message = "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->getName()}";
1143
1144 throw new BindingResolutionException($message);
1145 }
1146
1147 /**
1148 * Register a new before resolving callback for all types.
1149 *
1150 * @param \Closure|string $abstract
1151 * @param \Closure|null $callback
1152 * @return void
1153 */
1154 public function beforeResolving($abstract, ?Closure $callback = null)
1155 {
1156 if (is_string($abstract)) {
1157 $abstract = $this->getAlias($abstract);
1158 }
1159
1160 if ($abstract instanceof Closure && is_null($callback)) {
1161 $this->globalBeforeResolvingCallbacks[] = $abstract;
1162 } else {
1163 $this->beforeResolvingCallbacks[$abstract][] = $callback;
1164 }
1165 }
1166
1167 /**
1168 * Register a new resolving callback.
1169 *
1170 * @param \Closure|string $abstract
1171 * @param \Closure|null $callback
1172 * @return void
1173 */
1174 public function resolving($abstract, ?Closure $callback = null)
1175 {
1176 if (is_string($abstract)) {
1177 $abstract = $this->getAlias($abstract);
1178 }
1179
1180 if (is_null($callback) && $abstract instanceof Closure) {
1181 $this->globalResolvingCallbacks[] = $abstract;
1182 } else {
1183 $this->resolvingCallbacks[$abstract][] = $callback;
1184 }
1185 }
1186
1187 /**
1188 * Register a new after resolving callback for all types.
1189 *
1190 * @param \Closure|string $abstract
1191 * @param \Closure|null $callback
1192 * @return void
1193 */
1194 public function afterResolving($abstract, ?Closure $callback = null)
1195 {
1196 if (is_string($abstract)) {
1197 $abstract = $this->getAlias($abstract);
1198 }
1199
1200 if ($abstract instanceof Closure && is_null($callback)) {
1201 $this->globalAfterResolvingCallbacks[] = $abstract;
1202 } else {
1203 $this->afterResolvingCallbacks[$abstract][] = $callback;
1204 }
1205 }
1206
1207 /**
1208 * Fire all of the before resolving callbacks.
1209 *
1210 * @param string $abstract
1211 * @param array $parameters
1212 * @return void
1213 */
1214 protected function fireBeforeResolvingCallbacks($abstract, $parameters = [])
1215 {
1216 $this->fireBeforeCallbackArray($abstract, $parameters, $this->globalBeforeResolvingCallbacks);
1217
1218 foreach ($this->beforeResolvingCallbacks as $type => $callbacks) {
1219 if ($type === $abstract || is_subclass_of($abstract, $type)) {
1220 $this->fireBeforeCallbackArray($abstract, $parameters, $callbacks);
1221 }
1222 }
1223 }
1224
1225 /**
1226 * Fire an array of callbacks with an object.
1227 *
1228 * @param string $abstract
1229 * @param array $parameters
1230 * @param array $callbacks
1231 * @return void
1232 */
1233 protected function fireBeforeCallbackArray($abstract, $parameters, array $callbacks)
1234 {
1235 foreach ($callbacks as $callback) {
1236 $callback($abstract, $parameters, $this);
1237 }
1238 }
1239
1240 /**
1241 * Fire all of the resolving callbacks.
1242 *
1243 * @param string $abstract
1244 * @param mixed $object
1245 * @return void
1246 */
1247 protected function fireResolvingCallbacks($abstract, $object)
1248 {
1249 $this->fireCallbackArray($object, $this->globalResolvingCallbacks);
1250
1251 $this->fireCallbackArray(
1252 $object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
1253 );
1254
1255 $this->fireAfterResolvingCallbacks($abstract, $object);
1256 }
1257
1258 /**
1259 * Fire all of the after resolving callbacks.
1260 *
1261 * @param string $abstract
1262 * @param mixed $object
1263 * @return void
1264 */
1265 protected function fireAfterResolvingCallbacks($abstract, $object)
1266 {
1267 $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks);
1268
1269 $this->fireCallbackArray(
1270 $object, $this->getCallbacksForType($abstract, $object, $this->afterResolvingCallbacks)
1271 );
1272 }
1273
1274 /**
1275 * Get all callbacks for a given type.
1276 *
1277 * @param string $abstract
1278 * @param object $object
1279 * @param array $callbacksPerType
1280 * @return array
1281 */
1282 protected function getCallbacksForType($abstract, $object, array $callbacksPerType)
1283 {
1284 $results = [];
1285
1286 foreach ($callbacksPerType as $type => $callbacks) {
1287 if ($type === $abstract || $object instanceof $type) {
1288 $results = array_merge($results, $callbacks);
1289 }
1290 }
1291
1292 return $results;
1293 }
1294
1295 /**
1296 * Fire an array of callbacks with an object.
1297 *
1298 * @param mixed $object
1299 * @param array $callbacks
1300 * @return void
1301 */
1302 protected function fireCallbackArray($object, array $callbacks)
1303 {
1304 foreach ($callbacks as $callback) {
1305 $callback($object, $this);
1306 }
1307 }
1308
1309 /**
1310 * Get the container's bindings.
1311 *
1312 * @return array
1313 */
1314 public function getBindings()
1315 {
1316 return $this->bindings;
1317 }
1318
1319 /**
1320 * Get the alias for an abstract if available.
1321 *
1322 * @param string $abstract
1323 * @return string
1324 */
1325 public function getAlias($abstract)
1326 {
1327 return isset($this->aliases[$abstract])
1328 ? $this->getAlias($this->aliases[$abstract])
1329 : $abstract;
1330 }
1331
1332 /**
1333 * Get the extender callbacks for a given type.
1334 *
1335 * @param string $abstract
1336 * @return array
1337 */
1338 protected function getExtenders($abstract)
1339 {
1340 return $this->extenders[$this->getAlias($abstract)] ?? [];
1341 }
1342
1343 /**
1344 * Remove all of the extender callbacks for a given type.
1345 *
1346 * @param string $abstract
1347 * @return void
1348 */
1349 public function forgetExtenders($abstract)
1350 {
1351 unset($this->extenders[$this->getAlias($abstract)]);
1352 }
1353
1354 /**
1355 * Drop all of the stale instances and aliases.
1356 *
1357 * @param string $abstract
1358 * @return void
1359 */
1360 protected function dropStaleInstances($abstract)
1361 {
1362 unset($this->instances[$abstract], $this->aliases[$abstract]);
1363 }
1364
1365 /**
1366 * Remove a resolved instance from the instance cache.
1367 *
1368 * @param string $abstract
1369 * @return void
1370 */
1371 public function forgetInstance($abstract)
1372 {
1373 unset($this->instances[$abstract]);
1374 }
1375
1376 /**
1377 * Clear all of the instances from the container.
1378 *
1379 * @return void
1380 */
1381 public function forgetInstances()
1382 {
1383 $this->instances = [];
1384 }
1385
1386 /**
1387 * Clear all of the scoped instances from the container.
1388 *
1389 * @return void
1390 */
1391 public function forgetScopedInstances()
1392 {
1393 foreach ($this->scopedInstances as $scoped) {
1394 unset($this->instances[$scoped]);
1395 }
1396 }
1397
1398 /**
1399 * Flush the container of all bindings and resolved instances.
1400 *
1401 * @return void
1402 */
1403 public function flush()
1404 {
1405 $this->aliases = [];
1406 $this->resolved = [];
1407 $this->bindings = [];
1408 $this->instances = [];
1409 $this->abstractAliases = [];
1410 $this->scopedInstances = [];
1411 }
1412
1413 /**
1414 * Get the globally available instance of the container.
1415 *
1416 * @return static
1417 */
1418 public static function getInstance()
1419 {
1420 if (is_null(static::$instance)) {
1421 static::$instance = new static;
1422 }
1423
1424 return static::$instance;
1425 }
1426
1427 /**
1428 * Set the shared instance of the container.
1429 *
1430 * @param \FluentForm\Framework\Container\Contracts\Container|null $container
1431 * @return \FluentForm\Framework\Container\Contracts\Container|static
1432 */
1433 public static function setInstance(?ContainerContract $container = null)
1434 {
1435 return static::$instance = $container;
1436 }
1437
1438 /**
1439 * Determine if a given offset exists.
1440 *
1441 * @param string $key
1442 * @return bool
1443 */
1444 #[\ReturnTypeWillChange]
1445 public function offsetExists($key)
1446 {
1447 return $this->bound($key);
1448 }
1449
1450 /**
1451 * Get the value at a given offset.
1452 *
1453 * @param string $key
1454 * @return mixed
1455 */
1456 #[\ReturnTypeWillChange]
1457 public function offsetGet($key)
1458 {
1459 return $this->make($key);
1460 }
1461
1462 /**
1463 * Set the value at a given offset.
1464 *
1465 * @param string $key
1466 * @param mixed $value
1467 * @return void
1468 */
1469 #[\ReturnTypeWillChange]
1470 public function offsetSet($key, $value)
1471 {
1472 $this->bind($key, $value instanceof Closure ? $value : function () use ($value) {
1473 return $value;
1474 });
1475 }
1476
1477 /**
1478 * Unset the value at a given offset.
1479 *
1480 * @param string $key
1481 * @return void
1482 */
1483 #[\ReturnTypeWillChange]
1484 public function offsetUnset($key)
1485 {
1486 unset(
1487 $this->bindings[$key],
1488 $this->instances[$key],
1489 $this->resolved[$key]
1490 );
1491 }
1492
1493 /**
1494 * Dynamically access container services.
1495 *
1496 * @param string $key
1497 * @return mixed
1498 */
1499 public function __get($key)
1500 {
1501 return $this[$key];
1502 }
1503
1504 /**
1505 * Dynamically set container services.
1506 *
1507 * @param string $key
1508 * @param mixed $value
1509 * @return void
1510 */
1511 public function __set($key, $value)
1512 {
1513 $this[$key] = $value;
1514 }
1515 }
1516