PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.3
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.3
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 / Foundation / Container.php

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

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