Passed
Branch main (7db956)
by Michael
03:49
created

Container::resolve()   B

Complexity

Conditions 11
Paths 132

Size

Total Lines 63
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 23
nc 132
nop 3
dl 0
loc 63
rs 7.05
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Illuminate\Container;
4
5
use ArrayAccess;
6
use Closure;
7
use Exception;
8
use Illuminate\Contracts\Container\BindingResolutionException;
9
use Illuminate\Contracts\Container\CircularDependencyException;
10
use Illuminate\Contracts\Container\Container as ContainerContract;
11
use LogicException;
12
use ReflectionClass;
13
use ReflectionException;
14
use ReflectionParameter;
15
use TypeError;
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 \Illuminate\Contracts\Container\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 type to the
254
        // abstract type. After that, the concrete type to be registered as shared
255
        // without being forced to state their classes in both of the parameters.
256
        if (is_null($concrete)) {
257
            $concrete = $abstract;
258
        }
259
260
        // If the factory is not a Closure, it means it is just a class name which is
261
        // bound into this container to the abstract type and we will just wrap it
262
        // up inside its own Closure to give us more convenience when extending.
263
        if (! $concrete instanceof Closure) {
264
            if (! is_string($concrete)) {
265
                throw new TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');
266
            }
267
268
            $concrete = $this->getClosure($abstract, $concrete);
269
        }
270
271
        $this->bindings[$abstract] = compact('concrete', 'shared');
272
273
        // If the abstract type was already resolved in this container we'll fire the
274
        // rebound listener so that any objects which have already gotten resolved
275
        // can have their copy of the object updated via the listener callbacks.
276
        if ($this->resolved($abstract)) {
277
            $this->rebound($abstract);
278
        }
279
    }
280
281
    /**
282
     * Get the Closure to be used when building a type.
283
     *
284
     * @param  string  $abstract
285
     * @param  string  $concrete
286
     * @return \Closure
287
     */
288
    protected function getClosure($abstract, $concrete)
289
    {
290
        return function ($container, $parameters = []) use ($abstract, $concrete) {
291
            if ($abstract == $concrete) {
292
                return $container->build($concrete);
293
            }
294
295
            return $container->resolve(
296
                $concrete, $parameters, $raiseEvents = false
297
            );
298
        };
299
    }
300
301
    /**
302
     * Determine if the container has a method binding.
303
     *
304
     * @param  string  $method
305
     * @return bool
306
     */
307
    public function hasMethodBinding($method)
308
    {
309
        return isset($this->methodBindings[$method]);
310
    }
311
312
    /**
313
     * Bind a callback to resolve with Container::call.
314
     *
315
     * @param  array|string  $method
316
     * @param  \Closure  $callback
317
     * @return void
318
     */
319
    public function bindMethod($method, $callback)
320
    {
321
        $this->methodBindings[$this->parseBindMethod($method)] = $callback;
322
    }
323
324
    /**
325
     * Get the method to be bound in class@method format.
326
     *
327
     * @param  array|string  $method
328
     * @return string
329
     */
330
    protected function parseBindMethod($method)
331
    {
332
        if (is_array($method)) {
333
            return $method[0].'@'.$method[1];
334
        }
335
336
        return $method;
337
    }
338
339
    /**
340
     * Get the method binding for the given method.
341
     *
342
     * @param  string  $method
343
     * @param  mixed  $instance
344
     * @param  array  $parameters
345
     *
346
     * @return mixed
347
     */
348
    public function callMethodBinding($method, $instance, $parameters)
349
    {
350
        return call_user_func($this->methodBindings[$method], $instance, $this, $parameters);
351
    }
352
353
    /**
354
     * Add a contextual binding to the container.
355
     *
356
     * @param  string  $concrete
357
     * @param  string  $abstract
358
     * @param  \Closure|string  $implementation
359
     * @return void
360
     */
361
    public function addContextualBinding($concrete, $abstract, $implementation)
362
    {
363
        $this->contextual[$concrete][$this->getAlias($abstract)] = $implementation;
364
    }
365
366
    /**
367
     * Register a binding if it hasn't already been registered.
368
     *
369
     * @param  string  $abstract
370
     * @param  \Closure|string|null  $concrete
371
     * @param  bool  $shared
372
     * @return void
373
     */
374
    public function bindIf($abstract, $concrete = null, $shared = false)
375
    {
376
        if (! $this->bound($abstract)) {
377
            $this->bind($abstract, $concrete, $shared);
378
        }
379
    }
380
381
    /**
382
     * Register a shared binding in the container.
383
     *
384
     * @param  string  $abstract
385
     * @param  \Closure|string|null  $concrete
386
     * @return void
387
     */
388
    public function singleton($abstract, $concrete = null)
389
    {
390
        $this->bind($abstract, $concrete, true);
391
    }
392
393
    /**
394
     * Register a shared binding if it hasn't already been registered.
395
     *
396
     * @param  string  $abstract
397
     * @param  \Closure|string|null  $concrete
398
     * @return void
399
     */
400
    public function singletonIf($abstract, $concrete = null)
401
    {
402
        if (! $this->bound($abstract)) {
403
            $this->singleton($abstract, $concrete);
404
        }
405
    }
406
407
    /**
408
     * Register a scoped binding in the container.
409
     *
410
     * @param  string  $abstract
411
     * @param  \Closure|string|null  $concrete
412
     * @return void
413
     */
414
    public function scoped($abstract, $concrete = null)
415
    {
416
        $this->scopedInstances[] = $abstract;
417
418
        $this->singleton($abstract, $concrete);
419
    }
420
421
    /**
422
     * Register a scoped binding if it hasn't already been registered.
423
     *
424
     * @param  string  $abstract
425
     * @param  \Closure|string|null  $concrete
426
     * @return void
427
     */
428
    public function scopedIf($abstract, $concrete = null)
429
    {
430
        if (! $this->bound($abstract)) {
431
            $this->scopedInstances[] = $abstract;
432
433
            $this->singleton($abstract, $concrete);
434
        }
435
    }
436
437
    /**
438
     * "Extend" an abstract type in the container.
439
     *
440
     * @param  string  $abstract
441
     * @param  \Closure  $closure
442
     * @return void
443
     *
444
     * @throws \InvalidArgumentException
445
     */
446
    public function extend($abstract, Closure $closure)
447
    {
448
        $abstract = $this->getAlias($abstract);
449
450
        if (isset($this->instances[$abstract])) {
451
            $this->instances[$abstract] = $closure($this->instances[$abstract], $this);
452
453
            $this->rebound($abstract);
454
        } else {
455
            $this->extenders[$abstract][] = $closure;
456
457
            if ($this->resolved($abstract)) {
458
                $this->rebound($abstract);
459
            }
460
        }
461
    }
462
463
    /**
464
     * Register an existing instance as shared in the container.
465
     *
466
     * @param  string  $abstract
467
     * @param  mixed  $instance
468
     * @return mixed
469
     */
470
    public function instance($abstract, $instance)
471
    {
472
        $this->removeAbstractAlias($abstract);
473
474
        $isBound = $this->bound($abstract);
475
476
        unset($this->aliases[$abstract]);
477
478
        // We'll check to determine if this type has been bound before, and if it has
479
        // we will fire the rebound callbacks registered with the container and it
480
        // can be updated with consuming classes that have gotten resolved here.
481
        $this->instances[$abstract] = $instance;
482
483
        if ($isBound) {
484
            $this->rebound($abstract);
485
        }
486
487
        return $instance;
488
    }
489
490
    /**
491
     * Remove an alias from the contextual binding alias cache.
492
     *
493
     * @param  string  $searched
494
     * @return void
495
     */
496
    protected function removeAbstractAlias($searched)
497
    {
498
        if (! isset($this->aliases[$searched])) {
499
            return;
500
        }
501
502
        foreach ($this->abstractAliases as $abstract => $aliases) {
503
            foreach ($aliases as $index => $alias) {
504
                if ($alias == $searched) {
505
                    unset($this->abstractAliases[$abstract][$index]);
506
                }
507
            }
508
        }
509
    }
510
511
    /**
512
     * Assign a set of tags to a given binding.
513
     *
514
     * @param  array|string  $abstracts
515
     * @param  array|mixed  ...$tags
516
     * @return void
517
     */
518
    public function tag($abstracts, $tags)
519
    {
520
        $tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);
521
522
        foreach ($tags as $tag) {
523
            if (! isset($this->tags[$tag])) {
524
                $this->tags[$tag] = [];
525
            }
526
527
            foreach ((array) $abstracts as $abstract) {
528
                $this->tags[$tag][] = $abstract;
529
            }
530
        }
531
    }
532
533
    /**
534
     * Resolve all of the bindings for a given tag.
535
     *
536
     * @param  string  $tag
537
     * @return iterable
538
     */
539
    public function tagged($tag)
540
    {
541
        if (! isset($this->tags[$tag])) {
542
            return [];
543
        }
544
545
        return new RewindableGenerator(function () use ($tag) {
546
            foreach ($this->tags[$tag] as $abstract) {
547
                yield $this->make($abstract);
548
            }
549
        }, count($this->tags[$tag]));
550
    }
551
552
    /**
553
     * Alias a type to a different name.
554
     *
555
     * @param  string  $abstract
556
     * @param  string  $alias
557
     * @return void
558
     *
559
     * @throws \LogicException
560
     */
561
    public function alias($abstract, $alias)
562
    {
563
        if ($alias === $abstract) {
564
            throw new LogicException("[{$abstract}] is aliased to itself.");
565
        }
566
567
        $this->aliases[$alias] = $abstract;
568
569
        $this->abstractAliases[$abstract][] = $alias;
570
    }
571
572
    /**
573
     * Bind a new callback to an abstract's rebind event.
574
     *
575
     * @param  string  $abstract
576
     * @param  \Closure  $callback
577
     * @return mixed
578
     */
579
    public function rebinding($abstract, Closure $callback)
580
    {
581
        $this->reboundCallbacks[$abstract = $this->getAlias($abstract)][] = $callback;
582
583
        if ($this->bound($abstract)) {
584
            return $this->make($abstract);
585
        }
586
    }
587
588
    /**
589
     * Refresh an instance on the given target and method.
590
     *
591
     * @param  string  $abstract
592
     * @param  mixed  $target
593
     * @param  string  $method
594
     * @return mixed
595
     */
596
    public function refresh($abstract, $target, $method)
597
    {
598
        return $this->rebinding($abstract, function ($app, $instance) use ($target, $method) {
599
            $target->{$method}($instance);
600
        });
601
    }
602
603
    /**
604
     * Fire the "rebound" callbacks for the given abstract type.
605
     *
606
     * @param  string  $abstract
607
     * @return void
608
     */
609
    protected function rebound($abstract)
610
    {
611
        $instance = $this->make($abstract);
612
613
        foreach ($this->getReboundCallbacks($abstract) as $callback) {
614
            call_user_func($callback, $this, $instance);
615
        }
616
    }
617
618
    /**
619
     * Get the rebound callbacks for a given type.
620
     *
621
     * @param  string  $abstract
622
     * @return array
623
     */
624
    protected function getReboundCallbacks($abstract)
625
    {
626
        return $this->reboundCallbacks[$abstract] ?? [];
627
    }
628
629
    /**
630
     * Wrap the given closure such that its dependencies will be injected when executed.
631
     *
632
     * @param  \Closure  $callback
633
     * @param  array  $parameters
634
     * @return \Closure
635
     */
636
    public function wrap(Closure $callback, array $parameters = [])
637
    {
638
        return function () use ($callback, $parameters) {
639
            return $this->call($callback, $parameters);
640
        };
641
    }
642
643
    /**
644
     * Call the given Closure / class@method and inject its dependencies.
645
     *
646
     * @param  callable|string  $callback
647
     * @param  array<string, mixed>  $parameters
648
     * @param  string|null  $defaultMethod
649
     * @return mixed
650
     *
651
     * @throws \InvalidArgumentException
652
     */
653
    public function call($callback, array $parameters = [], $defaultMethod = null)
654
    {
655
        return BoundMethod::call($this, $callback, $parameters, $defaultMethod);
656
    }
657
658
    /**
659
     * Get a closure to resolve the given type from the container.
660
     *
661
     * @param  string  $abstract
662
     * @return \Closure
663
     */
664
    public function factory($abstract)
665
    {
666
        return function () use ($abstract) {
667
            return $this->make($abstract);
668
        };
669
    }
670
671
    /**
672
     * An alias function name for make().
673
     *
674
     * @param  string|callable  $abstract
675
     * @param  array  $parameters
676
     * @return mixed
677
     *
678
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
679
     */
680
    public function makeWith($abstract, array $parameters = [])
681
    {
682
        return $this->make($abstract, $parameters);
683
    }
684
685
    /**
686
     * Resolve the given type from the container.
687
     *
688
     * @param  string|callable  $abstract
689
     * @param  array  $parameters
690
     * @return mixed
691
     *
692
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
693
     */
694
    public function make($abstract, array $parameters = [])
695
    {
696
        return $this->resolve($abstract, $parameters);
697
    }
698
699
    /**
700
     * {@inheritdoc}
701
     *
702
     * @return mixed
703
     */
704
    public function get($id)
705
    {
706
        try {
707
            return $this->resolve($id);
708
        } catch (Exception $e) {
709
            if ($this->has($id) || $e instanceof CircularDependencyException) {
710
                throw $e;
711
            }
712
713
            throw new EntryNotFoundException($id, $e->getCode(), $e);
714
        }
715
    }
716
717
    /**
718
     * Resolve the given type from the container.
719
     *
720
     * @param  string|callable  $abstract
721
     * @param  array  $parameters
722
     * @param  bool  $raiseEvents
723
     * @return mixed
724
     *
725
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
726
     * @throws \Illuminate\Contracts\Container\CircularDependencyException
727
     */
728
    protected function resolve($abstract, $parameters = [], $raiseEvents = true)
729
    {
730
        $abstract = $this->getAlias($abstract);
0 ignored issues
show
Bug introduced by
It seems like $abstract can also be of type callable; however, parameter $abstract of Illuminate\Container\Container::getAlias() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

730
        $abstract = $this->getAlias(/** @scrutinizer ignore-type */ $abstract);
Loading history...
731
732
        // First we'll fire any event handlers which handle the "before" resolving of
733
        // specific types. This gives some hooks the chance to add various extends
734
        // calls to change the resolution of objects that they're interested in.
735
        if ($raiseEvents) {
736
            $this->fireBeforeResolvingCallbacks($abstract, $parameters);
737
        }
738
739
        $concrete = $this->getContextualConcrete($abstract);
740
741
        $needsContextualBuild = ! empty($parameters) || ! is_null($concrete);
742
743
        // If an instance of the type is currently being managed as a singleton we'll
744
        // just return an existing instance instead of instantiating new instances
745
        // so the developer can keep using the same objects instance every time.
746
        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
747
            return $this->instances[$abstract];
748
        }
749
750
        $this->with[] = $parameters;
751
752
        if (is_null($concrete)) {
753
            $concrete = $this->getConcrete($abstract);
754
        }
755
756
        // We're ready to instantiate an instance of the concrete type registered for
757
        // the binding. This will instantiate the types, as well as resolve any of
758
        // its "nested" dependencies recursively until all have gotten resolved.
759
        if ($this->isBuildable($concrete, $abstract)) {
760
            $object = $this->build($concrete);
761
        } else {
762
            $object = $this->make($concrete);
763
        }
764
765
        // If we defined any extenders for this type, we'll need to spin through them
766
        // and apply them to the object being built. This allows for the extension
767
        // of services, such as changing configuration or decorating the object.
768
        foreach ($this->getExtenders($abstract) as $extender) {
769
            $object = $extender($object, $this);
770
        }
771
772
        // If the requested type is registered as a singleton we'll want to cache off
773
        // the instances in "memory" so we can return it later without creating an
774
        // entirely new instance of an object on each subsequent request for it.
775
        if ($this->isShared($abstract) && ! $needsContextualBuild) {
776
            $this->instances[$abstract] = $object;
777
        }
778
779
        if ($raiseEvents) {
780
            $this->fireResolvingCallbacks($abstract, $object);
781
        }
782
783
        // Before returning, we will also set the resolved flag to "true" and pop off
784
        // the parameter overrides for this build. After those two things are done
785
        // we will be ready to return back the fully constructed class instance.
786
        $this->resolved[$abstract] = true;
787
788
        array_pop($this->with);
789
790
        return $object;
791
    }
792
793
    /**
794
     * Get the concrete type for a given abstract.
795
     *
796
     * @param  string|callable  $abstract
797
     * @return mixed
798
     */
799
    protected function getConcrete($abstract)
800
    {
801
        // If we don't have a registered resolver or concrete for the type, we'll just
802
        // assume each type is a concrete name and will attempt to resolve it as is
803
        // since the container should be able to resolve concretes automatically.
804
        if (isset($this->bindings[$abstract])) {
805
            return $this->bindings[$abstract]['concrete'];
806
        }
807
808
        return $abstract;
809
    }
810
811
    /**
812
     * Get the contextual concrete binding for the given abstract.
813
     *
814
     * @param  string|callable  $abstract
815
     * @return \Closure|string|array|null
816
     */
817
    protected function getContextualConcrete($abstract)
818
    {
819
        if (! is_null($binding = $this->findInContextualBindings($abstract))) {
820
            return $binding;
821
        }
822
823
        // Next we need to see if a contextual binding might be bound under an alias of the
824
        // given abstract type. So, we will need to check if any aliases exist with this
825
        // type and then spin through them and check for contextual bindings on these.
826
        if (empty($this->abstractAliases[$abstract])) {
827
            return;
828
        }
829
830
        foreach ($this->abstractAliases[$abstract] as $alias) {
831
            if (! is_null($binding = $this->findInContextualBindings($alias))) {
832
                return $binding;
833
            }
834
        }
835
    }
836
837
    /**
838
     * Find the concrete binding for the given abstract in the contextual binding array.
839
     *
840
     * @param  string|callable  $abstract
841
     * @return \Closure|string|null
842
     */
843
    protected function findInContextualBindings($abstract)
844
    {
845
        return $this->contextual[end($this->buildStack)][$abstract] ?? null;
846
    }
847
848
    /**
849
     * Determine if the given concrete is buildable.
850
     *
851
     * @param  mixed  $concrete
852
     * @param  string  $abstract
853
     * @return bool
854
     */
855
    protected function isBuildable($concrete, $abstract)
856
    {
857
        return $concrete === $abstract || $concrete instanceof Closure;
858
    }
859
860
    /**
861
     * Instantiate a concrete instance of the given type.
862
     *
863
     * @param  \Closure|string  $concrete
864
     * @return mixed
865
     *
866
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
867
     * @throws \Illuminate\Contracts\Container\CircularDependencyException
868
     */
869
    public function build($concrete)
870
    {
871
        // If the concrete type is actually a Closure, we will just execute it and
872
        // hand back the results of the functions, which allows functions to be
873
        // used as resolvers for more fine-tuned resolution of these objects.
874
        if ($concrete instanceof Closure) {
875
            return $concrete($this, $this->getLastParameterOverride());
876
        }
877
878
        try {
879
            $reflector = new ReflectionClass($concrete);
880
        } catch (ReflectionException $e) {
881
            throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
882
        }
883
884
        // If the type is not instantiable, the developer is attempting to resolve
885
        // an abstract type such as an Interface or Abstract Class and there is
886
        // no binding registered for the abstractions so we need to bail out.
887
        if (! $reflector->isInstantiable()) {
888
            return $this->notInstantiable($concrete);
889
        }
890
891
        // if (in_array($concrete, $this->buildStack)) {
892
        //     throw new CircularDependencyException("Circular dependency detected while resolving [{$concrete}].");
893
        // }
894
895
        $this->buildStack[] = $concrete;
896
897
        $constructor = $reflector->getConstructor();
898
899
        // If there are no constructors, that means there are no dependencies then
900
        // we can just resolve the instances of the objects right away, without
901
        // resolving any other types or dependencies out of these containers.
902
        if (is_null($constructor)) {
903
            array_pop($this->buildStack);
904
905
            return new $concrete;
906
        }
907
908
        $dependencies = $constructor->getParameters();
909
910
        // Once we have all the constructor's parameters we can create each of the
911
        // dependency instances and then use the reflection instances to make a
912
        // new instance of this class, injecting the created dependencies in.
913
        try {
914
            $instances = $this->resolveDependencies($dependencies);
915
        } catch (BindingResolutionException $e) {
916
            array_pop($this->buildStack);
917
918
            throw $e;
919
        }
920
921
        array_pop($this->buildStack);
922
923
        return $reflector->newInstanceArgs($instances);
924
    }
925
926
    /**
927
     * Resolve all of the dependencies from the ReflectionParameters.
928
     *
929
     * @param  \ReflectionParameter[]  $dependencies
930
     * @return array
931
     *
932
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
933
     */
934
    protected function resolveDependencies(array $dependencies)
935
    {
936
        $results = [];
937
938
        foreach ($dependencies as $dependency) {
939
            // If the dependency has an override for this particular build we will use
940
            // that instead as the value. Otherwise, we will continue with this run
941
            // of resolutions and let reflection attempt to determine the result.
942
            if ($this->hasParameterOverride($dependency)) {
943
                $results[] = $this->getParameterOverride($dependency);
944
945
                continue;
946
            }
947
948
            // If the class is null, it means the dependency is a string or some other
949
            // primitive type which we can not resolve since it is not a class and
950
            // we will just bomb out with an error since we have no-where to go.
951
            $result = is_null(Util::getParameterClassName($dependency))
952
                            ? $this->resolvePrimitive($dependency)
953
                            : $this->resolveClass($dependency);
954
955
            if ($dependency->isVariadic()) {
956
                $results = array_merge($results, $result);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type string; however, parameter $arrays of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

956
                $results = array_merge($results, /** @scrutinizer ignore-type */ $result);
Loading history...
957
            } else {
958
                $results[] = $result;
959
            }
960
        }
961
962
        return $results;
963
    }
964
965
    /**
966
     * Determine if the given dependency has a parameter override.
967
     *
968
     * @param  \ReflectionParameter  $dependency
969
     * @return bool
970
     */
971
    protected function hasParameterOverride($dependency)
972
    {
973
        return array_key_exists(
974
            $dependency->name, $this->getLastParameterOverride()
975
        );
976
    }
977
978
    /**
979
     * Get a parameter override for a dependency.
980
     *
981
     * @param  \ReflectionParameter  $dependency
982
     * @return mixed
983
     */
984
    protected function getParameterOverride($dependency)
985
    {
986
        return $this->getLastParameterOverride()[$dependency->name];
987
    }
988
989
    /**
990
     * Get the last parameter override.
991
     *
992
     * @return array
993
     */
994
    protected function getLastParameterOverride()
995
    {
996
        return count($this->with) ? end($this->with) : [];
997
    }
998
999
    /**
1000
     * Resolve a non-class hinted primitive dependency.
1001
     *
1002
     * @param  \ReflectionParameter  $parameter
1003
     * @return mixed
1004
     *
1005
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
1006
     */
1007
    protected function resolvePrimitive(ReflectionParameter $parameter)
1008
    {
1009
        if (! is_null($concrete = $this->getContextualConcrete('$'.$parameter->getName()))) {
1010
            return $concrete instanceof Closure ? $concrete($this) : $concrete;
1011
        }
1012
1013
        if ($parameter->isDefaultValueAvailable()) {
1014
            return $parameter->getDefaultValue();
1015
        }
1016
1017
        $this->unresolvablePrimitive($parameter);
1018
    }
1019
1020
    /**
1021
     * Resolve a class based dependency from the container.
1022
     *
1023
     * @param  \ReflectionParameter  $parameter
1024
     * @return mixed
1025
     *
1026
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
1027
     */
1028
    protected function resolveClass(ReflectionParameter $parameter)
1029
    {
1030
        try {
1031
            return $parameter->isVariadic()
1032
                        ? $this->resolveVariadicClass($parameter)
1033
                        : $this->make(Util::getParameterClassName($parameter));
1034
        }
1035
1036
        // If we can not resolve the class instance, we will check to see if the value
1037
        // is optional, and if it is we will return the optional parameter value as
1038
        // the value of the dependency, similarly to how we do this with scalars.
1039
        catch (BindingResolutionException $e) {
1040
            if ($parameter->isDefaultValueAvailable()) {
1041
                array_pop($this->with);
1042
1043
                return $parameter->getDefaultValue();
1044
            }
1045
1046
            if ($parameter->isVariadic()) {
1047
                array_pop($this->with);
1048
1049
                return [];
1050
            }
1051
1052
            throw $e;
1053
        }
1054
    }
1055
1056
    /**
1057
     * Resolve a class based variadic dependency from the container.
1058
     *
1059
     * @param  \ReflectionParameter  $parameter
1060
     * @return mixed
1061
     */
1062
    protected function resolveVariadicClass(ReflectionParameter $parameter)
1063
    {
1064
        $className = Util::getParameterClassName($parameter);
1065
1066
        $abstract = $this->getAlias($className);
1067
1068
        if (! is_array($concrete = $this->getContextualConcrete($abstract))) {
0 ignored issues
show
introduced by
The condition is_array($concrete = $th...ualConcrete($abstract)) is always false.
Loading history...
1069
            return $this->make($className);
1070
        }
1071
1072
        return array_map(function ($abstract) {
1073
            return $this->resolve($abstract);
1074
        }, $concrete);
1075
    }
1076
1077
    /**
1078
     * Throw an exception that the concrete is not instantiable.
1079
     *
1080
     * @param  string  $concrete
1081
     * @return void
1082
     *
1083
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
1084
     */
1085
    protected function notInstantiable($concrete)
1086
    {
1087
        if (! empty($this->buildStack)) {
1088
            $previous = implode(', ', $this->buildStack);
1089
1090
            $message = "Target [$concrete] is not instantiable while building [$previous].";
1091
        } else {
1092
            $message = "Target [$concrete] is not instantiable.";
1093
        }
1094
1095
        throw new BindingResolutionException($message);
1096
    }
1097
1098
    /**
1099
     * Throw an exception for an unresolvable primitive.
1100
     *
1101
     * @param  \ReflectionParameter  $parameter
1102
     * @return void
1103
     *
1104
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
1105
     */
1106
    protected function unresolvablePrimitive(ReflectionParameter $parameter)
1107
    {
1108
        $message = "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->getName()}";
1109
1110
        throw new BindingResolutionException($message);
1111
    }
1112
1113
    /**
1114
     * Register a new before resolving callback for all types.
1115
     *
1116
     * @param  \Closure|string  $abstract
1117
     * @param  \Closure|null  $callback
1118
     * @return void
1119
     */
1120
    public function beforeResolving($abstract, Closure $callback = null)
1121
    {
1122
        if (is_string($abstract)) {
1123
            $abstract = $this->getAlias($abstract);
1124
        }
1125
1126
        if ($abstract instanceof Closure && is_null($callback)) {
1127
            $this->globalBeforeResolvingCallbacks[] = $abstract;
1128
        } else {
1129
            $this->beforeResolvingCallbacks[$abstract][] = $callback;
1130
        }
1131
    }
1132
1133
    /**
1134
     * Register a new resolving callback.
1135
     *
1136
     * @param  \Closure|string  $abstract
1137
     * @param  \Closure|null  $callback
1138
     * @return void
1139
     */
1140
    public function resolving($abstract, Closure $callback = null)
1141
    {
1142
        if (is_string($abstract)) {
1143
            $abstract = $this->getAlias($abstract);
1144
        }
1145
1146
        if (is_null($callback) && $abstract instanceof Closure) {
1147
            $this->globalResolvingCallbacks[] = $abstract;
1148
        } else {
1149
            $this->resolvingCallbacks[$abstract][] = $callback;
1150
        }
1151
    }
1152
1153
    /**
1154
     * Register a new after resolving callback for all types.
1155
     *
1156
     * @param  \Closure|string  $abstract
1157
     * @param  \Closure|null  $callback
1158
     * @return void
1159
     */
1160
    public function afterResolving($abstract, Closure $callback = null)
1161
    {
1162
        if (is_string($abstract)) {
1163
            $abstract = $this->getAlias($abstract);
1164
        }
1165
1166
        if ($abstract instanceof Closure && is_null($callback)) {
1167
            $this->globalAfterResolvingCallbacks[] = $abstract;
1168
        } else {
1169
            $this->afterResolvingCallbacks[$abstract][] = $callback;
1170
        }
1171
    }
1172
1173
    /**
1174
     * Fire all of the before resolving callbacks.
1175
     *
1176
     * @param  string  $abstract
1177
     * @param  array  $parameters
1178
     * @return void
1179
     */
1180
    protected function fireBeforeResolvingCallbacks($abstract, $parameters = [])
1181
    {
1182
        $this->fireBeforeCallbackArray($abstract, $parameters, $this->globalBeforeResolvingCallbacks);
1183
1184
        foreach ($this->beforeResolvingCallbacks as $type => $callbacks) {
1185
            if ($type === $abstract || is_subclass_of($abstract, $type)) {
1186
                $this->fireBeforeCallbackArray($abstract, $parameters, $callbacks);
1187
            }
1188
        }
1189
    }
1190
1191
    /**
1192
     * Fire an array of callbacks with an object.
1193
     *
1194
     * @param  string  $abstract
1195
     * @param  array  $parameters
1196
     * @param  array  $callbacks
1197
     * @return void
1198
     */
1199
    protected function fireBeforeCallbackArray($abstract, $parameters, array $callbacks)
1200
    {
1201
        foreach ($callbacks as $callback) {
1202
            $callback($abstract, $parameters, $this);
1203
        }
1204
    }
1205
1206
    /**
1207
     * Fire all of the resolving callbacks.
1208
     *
1209
     * @param  string  $abstract
1210
     * @param  mixed  $object
1211
     * @return void
1212
     */
1213
    protected function fireResolvingCallbacks($abstract, $object)
1214
    {
1215
        $this->fireCallbackArray($object, $this->globalResolvingCallbacks);
1216
1217
        $this->fireCallbackArray(
1218
            $object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
1219
        );
1220
1221
        $this->fireAfterResolvingCallbacks($abstract, $object);
1222
    }
1223
1224
    /**
1225
     * Fire all of the after resolving callbacks.
1226
     *
1227
     * @param  string  $abstract
1228
     * @param  mixed  $object
1229
     * @return void
1230
     */
1231
    protected function fireAfterResolvingCallbacks($abstract, $object)
1232
    {
1233
        $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks);
1234
1235
        $this->fireCallbackArray(
1236
            $object, $this->getCallbacksForType($abstract, $object, $this->afterResolvingCallbacks)
1237
        );
1238
    }
1239
1240
    /**
1241
     * Get all callbacks for a given type.
1242
     *
1243
     * @param  string  $abstract
1244
     * @param  object  $object
1245
     * @param  array  $callbacksPerType
1246
     * @return array
1247
     */
1248
    protected function getCallbacksForType($abstract, $object, array $callbacksPerType)
1249
    {
1250
        $results = [];
1251
1252
        foreach ($callbacksPerType as $type => $callbacks) {
1253
            if ($type === $abstract || $object instanceof $type) {
1254
                $results = array_merge($results, $callbacks);
1255
            }
1256
        }
1257
1258
        return $results;
1259
    }
1260
1261
    /**
1262
     * Fire an array of callbacks with an object.
1263
     *
1264
     * @param  mixed  $object
1265
     * @param  array  $callbacks
1266
     * @return void
1267
     */
1268
    protected function fireCallbackArray($object, array $callbacks)
1269
    {
1270
        foreach ($callbacks as $callback) {
1271
            $callback($object, $this);
1272
        }
1273
    }
1274
1275
    /**
1276
     * Get the container's bindings.
1277
     *
1278
     * @return array
1279
     */
1280
    public function getBindings()
1281
    {
1282
        return $this->bindings;
1283
    }
1284
1285
    /**
1286
     * Get the container's method bindings.
1287
     *
1288
     * @return array
1289
     */
1290
    public function getMethodBindings()
1291
    {
1292
        return $this->methodBindings;
1293
    }
1294
1295
    /**
1296
     * Get the alias for an abstract if available.
1297
     *
1298
     * @param  string  $abstract
1299
     * @return string
1300
     */
1301
    public function getAlias($abstract)
1302
    {
1303
        return isset($this->aliases[$abstract])
1304
                    ? $this->getAlias($this->aliases[$abstract])
1305
                    : $abstract;
1306
    }
1307
1308
    /**
1309
     * Get the extender callbacks for a given type.
1310
     *
1311
     * @param  string  $abstract
1312
     * @return array
1313
     */
1314
    protected function getExtenders($abstract)
1315
    {
1316
        return $this->extenders[$this->getAlias($abstract)] ?? [];
1317
    }
1318
1319
    /**
1320
     * Remove all of the extender callbacks for a given type.
1321
     *
1322
     * @param  string  $abstract
1323
     * @return void
1324
     */
1325
    public function forgetExtenders($abstract)
1326
    {
1327
        unset($this->extenders[$this->getAlias($abstract)]);
1328
    }
1329
1330
    /**
1331
     * Drop all of the stale instances and aliases.
1332
     *
1333
     * @param  string  $abstract
1334
     * @return void
1335
     */
1336
    protected function dropStaleInstances($abstract)
1337
    {
1338
        unset($this->instances[$abstract], $this->aliases[$abstract]);
1339
    }
1340
1341
    /**
1342
     * Remove a resolved instance from the instance cache.
1343
     *
1344
     * @param  string  $abstract
1345
     * @return void
1346
     */
1347
    public function forgetInstance($abstract)
1348
    {
1349
        unset($this->instances[$abstract]);
1350
    }
1351
1352
    /**
1353
     * Clear all of the instances from the container.
1354
     *
1355
     * @return void
1356
     */
1357
    public function forgetInstances()
1358
    {
1359
        $this->instances = [];
1360
    }
1361
1362
    /**
1363
     * Clear all of the scoped instances from the container.
1364
     *
1365
     * @return void
1366
     */
1367
    public function forgetScopedInstances()
1368
    {
1369
        foreach ($this->scopedInstances as $scoped) {
1370
            unset($this->instances[$scoped]);
1371
        }
1372
    }
1373
1374
    /**
1375
     * Flush the container of all bindings and resolved instances.
1376
     *
1377
     * @return void
1378
     */
1379
    public function flush()
1380
    {
1381
        $this->aliases = [];
1382
        $this->resolved = [];
1383
        $this->bindings = [];
1384
        $this->instances = [];
1385
        $this->methodBindings = [];
1386
        $this->abstractAliases = [];
1387
        $this->scopedInstances = [];
1388
    }
1389
1390
    /**
1391
     * Get the globally available instance of the container.
1392
     *
1393
     * @return static
1394
     */
1395
    public static function getInstance()
1396
    {
1397
        if (is_null(static::$instance)) {
1398
            static::$instance = new static;
1399
        }
1400
1401
        return static::$instance;
1402
    }
1403
1404
    /**
1405
     * Set the shared instance of the container.
1406
     *
1407
     * @param  \Illuminate\Contracts\Container\Container|null  $container
1408
     * @return \Illuminate\Contracts\Container\Container|static
1409
     */
1410
    public static function setInstance(ContainerContract $container = null)
1411
    {
1412
        return static::$instance = $container;
1413
    }
1414
1415
    /**
1416
     * Determine if a given offset exists.
1417
     *
1418
     * @param  string  $key
1419
     * @return bool
1420
     */
1421
    #[\ReturnTypeWillChange]
1422
    public function offsetExists($key)
1423
    {
1424
        return $this->bound($key);
1425
    }
1426
1427
    /**
1428
     * Get the value at a given offset.
1429
     *
1430
     * @param  string  $key
1431
     * @return mixed
1432
     */
1433
    #[\ReturnTypeWillChange]
1434
    public function offsetGet($key)
1435
    {
1436
        return $this->make($key);
1437
    }
1438
1439
    /**
1440
     * Set the value at a given offset.
1441
     *
1442
     * @param  string  $key
1443
     * @param  mixed  $value
1444
     * @return void
1445
     */
1446
    #[\ReturnTypeWillChange]
1447
    public function offsetSet($key, $value)
1448
    {
1449
        $this->bind($key, $value instanceof Closure ? $value : function () use ($value) {
1450
            return $value;
1451
        });
1452
    }
1453
1454
    /**
1455
     * Unset the value at a given offset.
1456
     *
1457
     * @param  string  $key
1458
     * @return void
1459
     */
1460
    #[\ReturnTypeWillChange]
1461
    public function offsetUnset($key)
1462
    {
1463
        unset($this->bindings[$key], $this->instances[$key], $this->resolved[$key]);
1464
    }
1465
1466
    /**
1467
     * Dynamically access container services.
1468
     *
1469
     * @param  string  $key
1470
     * @return mixed
1471
     */
1472
    public function __get($key)
1473
    {
1474
        return $this[$key];
1475
    }
1476
1477
    /**
1478
     * Dynamically set container services.
1479
     *
1480
     * @param  string  $key
1481
     * @param  mixed  $value
1482
     * @return void
1483
     */
1484
    public function __set($key, $value)
1485
    {
1486
        $this[$key] = $value;
1487
    }
1488
}
1489