Projekt

Obecné

Profil

Stáhnout (33.7 KB) Statistiky
| Větev: | Revize:
1
<?php
2

    
3
namespace Illuminate\Container;
4

    
5
use Closure;
6
use ArrayAccess;
7
use ReflectionClass;
8
use ReflectionMethod;
9
use ReflectionFunction;
10
use ReflectionParameter;
11
use InvalidArgumentException;
12
use Illuminate\Contracts\Container\BindingResolutionException;
13
use Illuminate\Contracts\Container\Container as ContainerContract;
14

    
15
class Container implements ArrayAccess, ContainerContract
16
{
17
    /**
18
     * The current globally available container (if any).
19
     *
20
     * @var static
21
     */
22
    protected static $instance;
23

    
24
    /**
25
     * An array of the types that have been resolved.
26
     *
27
     * @var array
28
     */
29
    protected $resolved = [];
30

    
31
    /**
32
     * The container's bindings.
33
     *
34
     * @var array
35
     */
36
    protected $bindings = [];
37

    
38
    /**
39
     * The container's shared instances.
40
     *
41
     * @var array
42
     */
43
    protected $instances = [];
44

    
45
    /**
46
     * The registered type aliases.
47
     *
48
     * @var array
49
     */
50
    protected $aliases = [];
51

    
52
    /**
53
     * The extension closures for services.
54
     *
55
     * @var array
56
     */
57
    protected $extenders = [];
58

    
59
    /**
60
     * All of the registered tags.
61
     *
62
     * @var array
63
     */
64
    protected $tags = [];
65

    
66
    /**
67
     * The stack of concretions currently being built.
68
     *
69
     * @var array
70
     */
71
    protected $buildStack = [];
72

    
73
    /**
74
     * The contextual binding map.
75
     *
76
     * @var array
77
     */
78
    public $contextual = [];
79

    
80
    /**
81
     * All of the registered rebound callbacks.
82
     *
83
     * @var array
84
     */
85
    protected $reboundCallbacks = [];
86

    
87
    /**
88
     * All of the global resolving callbacks.
89
     *
90
     * @var array
91
     */
92
    protected $globalResolvingCallbacks = [];
93

    
94
    /**
95
     * All of the global after resolving callbacks.
96
     *
97
     * @var array
98
     */
99
    protected $globalAfterResolvingCallbacks = [];
100

    
101
    /**
102
     * All of the resolving callbacks by class type.
103
     *
104
     * @var array
105
     */
106
    protected $resolvingCallbacks = [];
107

    
108
    /**
109
     * All of the after resolving callbacks by class type.
110
     *
111
     * @var array
112
     */
113
    protected $afterResolvingCallbacks = [];
114

    
115
    /**
116
     * Define a contextual binding.
117
     *
118
     * @param  string  $concrete
119
     * @return \Illuminate\Contracts\Container\ContextualBindingBuilder
120
     */
121
    public function when($concrete)
122
    {
123
        $concrete = $this->normalize($concrete);
124

    
125
        return new ContextualBindingBuilder($this, $concrete);
126
    }
127

    
128
    /**
129
     * Determine if the given abstract type has been bound.
130
     *
131
     * @param  string  $abstract
132
     * @return bool
133
     */
134
    public function bound($abstract)
135
    {
136
        $abstract = $this->normalize($abstract);
137

    
138
        return isset($this->bindings[$abstract]) || isset($this->instances[$abstract]) || $this->isAlias($abstract);
139
    }
140

    
141
    /**
142
     * Determine if the given abstract type has been resolved.
143
     *
144
     * @param  string  $abstract
145
     * @return bool
146
     */
147
    public function resolved($abstract)
148
    {
149
        $abstract = $this->normalize($abstract);
150

    
151
        if ($this->isAlias($abstract)) {
152
            $abstract = $this->getAlias($abstract);
153
        }
154

    
155
        return isset($this->resolved[$abstract]) || isset($this->instances[$abstract]);
156
    }
157

    
158
    /**
159
     * Determine if a given string is an alias.
160
     *
161
     * @param  string  $name
162
     * @return bool
163
     */
164
    public function isAlias($name)
165
    {
166
        return isset($this->aliases[$this->normalize($name)]);
167
    }
168

    
169
    /**
170
     * Register a binding with the container.
171
     *
172
     * @param  string|array  $abstract
173
     * @param  \Closure|string|null  $concrete
174
     * @param  bool  $shared
175
     * @return void
176
     */
177
    public function bind($abstract, $concrete = null, $shared = false)
178
    {
179
        $abstract = $this->normalize($abstract);
180

    
181
        $concrete = $this->normalize($concrete);
182

    
183
        // If the given types are actually an array, we will assume an alias is being
184
        // defined and will grab this "real" abstract class name and register this
185
        // alias with the container so that it can be used as a shortcut for it.
186
        if (is_array($abstract)) {
187
            list($abstract, $alias) = $this->extractAlias($abstract);
188

    
189
            $this->alias($abstract, $alias);
190
        }
191

    
192
        // If no concrete type was given, we will simply set the concrete type to the
193
        // abstract type. After that, the concrete type to be registered as shared
194
        // without being forced to state their classes in both of the parameters.
195
        $this->dropStaleInstances($abstract);
196

    
197
        if (is_null($concrete)) {
198
            $concrete = $abstract;
199
        }
200

    
201
        // If the factory is not a Closure, it means it is just a class name which is
202
        // bound into this container to the abstract type and we will just wrap it
203
        // up inside its own Closure to give us more convenience when extending.
204
        if (! $concrete instanceof Closure) {
205
            $concrete = $this->getClosure($abstract, $concrete);
206
        }
207

    
208
        $this->bindings[$abstract] = compact('concrete', 'shared');
209

    
210
        // If the abstract type was already resolved in this container we'll fire the
211
        // rebound listener so that any objects which have already gotten resolved
212
        // can have their copy of the object updated via the listener callbacks.
213
        if ($this->resolved($abstract)) {
214
            $this->rebound($abstract);
215
        }
216
    }
217

    
218
    /**
219
     * Get the Closure to be used when building a type.
220
     *
221
     * @param  string  $abstract
222
     * @param  string  $concrete
223
     * @return \Closure
224
     */
225
    protected function getClosure($abstract, $concrete)
226
    {
227
        return function ($c, $parameters = []) use ($abstract, $concrete) {
228
            $method = ($abstract == $concrete) ? 'build' : 'make';
229

    
230
            return $c->$method($concrete, $parameters);
231
        };
232
    }
233

    
234
    /**
235
     * Add a contextual binding to the container.
236
     *
237
     * @param  string  $concrete
238
     * @param  string  $abstract
239
     * @param  \Closure|string  $implementation
240
     * @return void
241
     */
242
    public function addContextualBinding($concrete, $abstract, $implementation)
243
    {
244
        $this->contextual[$this->normalize($concrete)][$this->normalize($abstract)] = $this->normalize($implementation);
245
    }
246

    
247
    /**
248
     * Register a binding if it hasn't already been registered.
249
     *
250
     * @param  string  $abstract
251
     * @param  \Closure|string|null  $concrete
252
     * @param  bool  $shared
253
     * @return void
254
     */
255
    public function bindIf($abstract, $concrete = null, $shared = false)
256
    {
257
        if (! $this->bound($abstract)) {
258
            $this->bind($abstract, $concrete, $shared);
259
        }
260
    }
261

    
262
    /**
263
     * Register a shared binding in the container.
264
     *
265
     * @param  string|array  $abstract
266
     * @param  \Closure|string|null  $concrete
267
     * @return void
268
     */
269
    public function singleton($abstract, $concrete = null)
270
    {
271
        $this->bind($abstract, $concrete, true);
272
    }
273

    
274
    /**
275
     * Wrap a Closure such that it is shared.
276
     *
277
     * @param  \Closure  $closure
278
     * @return \Closure
279
     */
280
    public function share(Closure $closure)
281
    {
282
        return function ($container) use ($closure) {
283
            // We'll simply declare a static variable within the Closures and if it has
284
            // not been set we will execute the given Closures to resolve this value
285
            // and return it back to these consumers of the method as an instance.
286
            static $object;
287

    
288
            if (is_null($object)) {
289
                $object = $closure($container);
290
            }
291

    
292
            return $object;
293
        };
294
    }
295

    
296
    /**
297
     * "Extend" an abstract type in the container.
298
     *
299
     * @param  string    $abstract
300
     * @param  \Closure  $closure
301
     * @return void
302
     *
303
     * @throws \InvalidArgumentException
304
     */
305
    public function extend($abstract, Closure $closure)
306
    {
307
        $abstract = $this->normalize($abstract);
308

    
309
        if (isset($this->instances[$abstract])) {
310
            $this->instances[$abstract] = $closure($this->instances[$abstract], $this);
311

    
312
            $this->rebound($abstract);
313
        } else {
314
            $this->extenders[$abstract][] = $closure;
315
        }
316
    }
317

    
318
    /**
319
     * Register an existing instance as shared in the container.
320
     *
321
     * @param  string  $abstract
322
     * @param  mixed   $instance
323
     * @return void
324
     */
325
    public function instance($abstract, $instance)
326
    {
327
        $abstract = $this->normalize($abstract);
328

    
329
        // First, we will extract the alias from the abstract if it is an array so we
330
        // are using the correct name when binding the type. If we get an alias it
331
        // will be registered with the container so we can resolve it out later.
332
        if (is_array($abstract)) {
333
            list($abstract, $alias) = $this->extractAlias($abstract);
334

    
335
            $this->alias($abstract, $alias);
336
        }
337

    
338
        unset($this->aliases[$abstract]);
339

    
340
        // We'll check to determine if this type has been bound before, and if it has
341
        // we will fire the rebound callbacks registered with the container and it
342
        // can be updated with consuming classes that have gotten resolved here.
343
        $bound = $this->bound($abstract);
344

    
345
        $this->instances[$abstract] = $instance;
346

    
347
        if ($bound) {
348
            $this->rebound($abstract);
349
        }
350
    }
351

    
352
    /**
353
     * Assign a set of tags to a given binding.
354
     *
355
     * @param  array|string  $abstracts
356
     * @param  array|mixed   ...$tags
357
     * @return void
358
     */
359
    public function tag($abstracts, $tags)
360
    {
361
        $tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);
362

    
363
        foreach ($tags as $tag) {
364
            if (! isset($this->tags[$tag])) {
365
                $this->tags[$tag] = [];
366
            }
367

    
368
            foreach ((array) $abstracts as $abstract) {
369
                $this->tags[$tag][] = $this->normalize($abstract);
370
            }
371
        }
372
    }
373

    
374
    /**
375
     * Resolve all of the bindings for a given tag.
376
     *
377
     * @param  string  $tag
378
     * @return array
379
     */
380
    public function tagged($tag)
381
    {
382
        $results = [];
383

    
384
        if (isset($this->tags[$tag])) {
385
            foreach ($this->tags[$tag] as $abstract) {
386
                $results[] = $this->make($abstract);
387
            }
388
        }
389

    
390
        return $results;
391
    }
392

    
393
    /**
394
     * Alias a type to a different name.
395
     *
396
     * @param  string  $abstract
397
     * @param  string  $alias
398
     * @return void
399
     */
400
    public function alias($abstract, $alias)
401
    {
402
        $this->aliases[$alias] = $this->normalize($abstract);
403
    }
404

    
405
    /**
406
     * Extract the type and alias from a given definition.
407
     *
408
     * @param  array  $definition
409
     * @return array
410
     */
411
    protected function extractAlias(array $definition)
412
    {
413
        return [key($definition), current($definition)];
414
    }
415

    
416
    /**
417
     * Bind a new callback to an abstract's rebind event.
418
     *
419
     * @param  string    $abstract
420
     * @param  \Closure  $callback
421
     * @return mixed
422
     */
423
    public function rebinding($abstract, Closure $callback)
424
    {
425
        $this->reboundCallbacks[$this->normalize($abstract)][] = $callback;
426

    
427
        if ($this->bound($abstract)) {
428
            return $this->make($abstract);
429
        }
430
    }
431

    
432
    /**
433
     * Refresh an instance on the given target and method.
434
     *
435
     * @param  string  $abstract
436
     * @param  mixed   $target
437
     * @param  string  $method
438
     * @return mixed
439
     */
440
    public function refresh($abstract, $target, $method)
441
    {
442
        return $this->rebinding($this->normalize($abstract), function ($app, $instance) use ($target, $method) {
443
            $target->{$method}($instance);
444
        });
445
    }
446

    
447
    /**
448
     * Fire the "rebound" callbacks for the given abstract type.
449
     *
450
     * @param  string  $abstract
451
     * @return void
452
     */
453
    protected function rebound($abstract)
454
    {
455
        $instance = $this->make($abstract);
456

    
457
        foreach ($this->getReboundCallbacks($abstract) as $callback) {
458
            call_user_func($callback, $this, $instance);
459
        }
460
    }
461

    
462
    /**
463
     * Get the rebound callbacks for a given type.
464
     *
465
     * @param  string  $abstract
466
     * @return array
467
     */
468
    protected function getReboundCallbacks($abstract)
469
    {
470
        if (isset($this->reboundCallbacks[$abstract])) {
471
            return $this->reboundCallbacks[$abstract];
472
        }
473

    
474
        return [];
475
    }
476

    
477
    /**
478
     * Wrap the given closure such that its dependencies will be injected when executed.
479
     *
480
     * @param  \Closure  $callback
481
     * @param  array  $parameters
482
     * @return \Closure
483
     */
484
    public function wrap(Closure $callback, array $parameters = [])
485
    {
486
        return function () use ($callback, $parameters) {
487
            return $this->call($callback, $parameters);
488
        };
489
    }
490

    
491
    /**
492
     * Call the given Closure / class@method and inject its dependencies.
493
     *
494
     * @param  callable|string  $callback
495
     * @param  array  $parameters
496
     * @param  string|null  $defaultMethod
497
     * @return mixed
498
     */
499
    public function call($callback, array $parameters = [], $defaultMethod = null)
500
    {
501
        if ($this->isCallableWithAtSign($callback) || $defaultMethod) {
502
            return $this->callClass($callback, $parameters, $defaultMethod);
503
        }
504

    
505
        $dependencies = $this->getMethodDependencies($callback, $parameters);
506

    
507
        return call_user_func_array($callback, $dependencies);
508
    }
509

    
510
    /**
511
     * Determine if the given string is in Class@method syntax.
512
     *
513
     * @param  mixed  $callback
514
     * @return bool
515
     */
516
    protected function isCallableWithAtSign($callback)
517
    {
518
        return is_string($callback) && strpos($callback, '@') !== false;
519
    }
520

    
521
    /**
522
     * Get all dependencies for a given method.
523
     *
524
     * @param  callable|string  $callback
525
     * @param  array  $parameters
526
     * @return array
527
     */
528
    protected function getMethodDependencies($callback, array $parameters = [])
529
    {
530
        $dependencies = [];
531

    
532
        foreach ($this->getCallReflector($callback)->getParameters() as $parameter) {
533
            $this->addDependencyForCallParameter($parameter, $parameters, $dependencies);
534
        }
535

    
536
        return array_merge($dependencies, $parameters);
537
    }
538

    
539
    /**
540
     * Get the proper reflection instance for the given callback.
541
     *
542
     * @param  callable|string  $callback
543
     * @return \ReflectionFunctionAbstract
544
     */
545
    protected function getCallReflector($callback)
546
    {
547
        if (is_string($callback) && strpos($callback, '::') !== false) {
548
            $callback = explode('::', $callback);
549
        }
550

    
551
        if (is_array($callback)) {
552
            return new ReflectionMethod($callback[0], $callback[1]);
553
        }
554

    
555
        return new ReflectionFunction($callback);
556
    }
557

    
558
    /**
559
     * Get the dependency for the given call parameter.
560
     *
561
     * @param  \ReflectionParameter  $parameter
562
     * @param  array  $parameters
563
     * @param  array  $dependencies
564
     * @return mixed
565
     */
566
    protected function addDependencyForCallParameter(ReflectionParameter $parameter, array &$parameters, &$dependencies)
567
    {
568
        if (array_key_exists($parameter->name, $parameters)) {
569
            $dependencies[] = $parameters[$parameter->name];
570

    
571
            unset($parameters[$parameter->name]);
572
        } elseif ($parameter->getClass()) {
573
            $dependencies[] = $this->make($parameter->getClass()->name);
574
        } elseif ($parameter->isDefaultValueAvailable()) {
575
            $dependencies[] = $parameter->getDefaultValue();
576
        }
577
    }
578

    
579
    /**
580
     * Call a string reference to a class using Class@method syntax.
581
     *
582
     * @param  string  $target
583
     * @param  array  $parameters
584
     * @param  string|null  $defaultMethod
585
     * @return mixed
586
     *
587
     * @throws \InvalidArgumentException
588
     */
589
    protected function callClass($target, array $parameters = [], $defaultMethod = null)
590
    {
591
        $segments = explode('@', $target);
592

    
593
        // If the listener has an @ sign, we will assume it is being used to delimit
594
        // the class name from the handle method name. This allows for handlers
595
        // to run multiple handler methods in a single class for convenience.
596
        $method = count($segments) == 2 ? $segments[1] : $defaultMethod;
597

    
598
        if (is_null($method)) {
599
            throw new InvalidArgumentException('Method not provided.');
600
        }
601

    
602
        return $this->call([$this->make($segments[0]), $method], $parameters);
603
    }
604

    
605
    /**
606
     * Resolve the given type from the container.
607
     *
608
     * @param  string  $abstract
609
     * @param  array   $parameters
610
     * @return mixed
611
     */
612
    public function make($abstract, array $parameters = [])
613
    {
614
        $abstract = $this->getAlias($this->normalize($abstract));
615

    
616
        // If an instance of the type is currently being managed as a singleton we'll
617
        // just return an existing instance instead of instantiating new instances
618
        // so the developer can keep using the same objects instance every time.
619
        if (isset($this->instances[$abstract])) {
620
            return $this->instances[$abstract];
621
        }
622

    
623
        $concrete = $this->getConcrete($abstract);
624

    
625
        // We're ready to instantiate an instance of the concrete type registered for
626
        // the binding. This will instantiate the types, as well as resolve any of
627
        // its "nested" dependencies recursively until all have gotten resolved.
628
        if ($this->isBuildable($concrete, $abstract)) {
629
            $object = $this->build($concrete, $parameters);
630
        } else {
631
            $object = $this->make($concrete, $parameters);
632
        }
633

    
634
        // If we defined any extenders for this type, we'll need to spin through them
635
        // and apply them to the object being built. This allows for the extension
636
        // of services, such as changing configuration or decorating the object.
637
        foreach ($this->getExtenders($abstract) as $extender) {
638
            $object = $extender($object, $this);
639
        }
640

    
641
        // If the requested type is registered as a singleton we'll want to cache off
642
        // the instances in "memory" so we can return it later without creating an
643
        // entirely new instance of an object on each subsequent request for it.
644
        if ($this->isShared($abstract)) {
645
            $this->instances[$abstract] = $object;
646
        }
647

    
648
        $this->fireResolvingCallbacks($abstract, $object);
649

    
650
        $this->resolved[$abstract] = true;
651

    
652
        return $object;
653
    }
654

    
655
    /**
656
     * Get the concrete type for a given abstract.
657
     *
658
     * @param  string  $abstract
659
     * @return mixed   $concrete
660
     */
661
    protected function getConcrete($abstract)
662
    {
663
        if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
664
            return $concrete;
665
        }
666

    
667
        // If we don't have a registered resolver or concrete for the type, we'll just
668
        // assume each type is a concrete name and will attempt to resolve it as is
669
        // since the container should be able to resolve concretes automatically.
670
        if (! isset($this->bindings[$abstract])) {
671
            return $abstract;
672
        }
673

    
674
        return $this->bindings[$abstract]['concrete'];
675
    }
676

    
677
    /**
678
     * Get the contextual concrete binding for the given abstract.
679
     *
680
     * @param  string  $abstract
681
     * @return string|null
682
     */
683
    protected function getContextualConcrete($abstract)
684
    {
685
        if (isset($this->contextual[end($this->buildStack)][$abstract])) {
686
            return $this->contextual[end($this->buildStack)][$abstract];
687
        }
688
    }
689

    
690
    /**
691
     * Normalize the given class name by removing leading slashes.
692
     *
693
     * @param  mixed  $service
694
     * @return mixed
695
     */
696
    protected function normalize($service)
697
    {
698
        return is_string($service) ? ltrim($service, '\\') : $service;
699
    }
700

    
701
    /**
702
     * Get the extender callbacks for a given type.
703
     *
704
     * @param  string  $abstract
705
     * @return array
706
     */
707
    protected function getExtenders($abstract)
708
    {
709
        if (isset($this->extenders[$abstract])) {
710
            return $this->extenders[$abstract];
711
        }
712

    
713
        return [];
714
    }
715

    
716
    /**
717
     * Instantiate a concrete instance of the given type.
718
     *
719
     * @param  string  $concrete
720
     * @param  array   $parameters
721
     * @return mixed
722
     *
723
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
724
     */
725
    public function build($concrete, array $parameters = [])
726
    {
727
        // If the concrete type is actually a Closure, we will just execute it and
728
        // hand back the results of the functions, which allows functions to be
729
        // used as resolvers for more fine-tuned resolution of these objects.
730
        if ($concrete instanceof Closure) {
731
            return $concrete($this, $parameters);
732
        }
733

    
734
        $reflector = new ReflectionClass($concrete);
735

    
736
        // If the type is not instantiable, the developer is attempting to resolve
737
        // an abstract type such as an Interface of Abstract Class and there is
738
        // no binding registered for the abstractions so we need to bail out.
739
        if (! $reflector->isInstantiable()) {
740
            if (! empty($this->buildStack)) {
741
                $previous = implode(', ', $this->buildStack);
742

    
743
                $message = "Target [$concrete] is not instantiable while building [$previous].";
744
            } else {
745
                $message = "Target [$concrete] is not instantiable.";
746
            }
747

    
748
            throw new BindingResolutionException($message);
749
        }
750

    
751
        $this->buildStack[] = $concrete;
752

    
753
        $constructor = $reflector->getConstructor();
754

    
755
        // If there are no constructors, that means there are no dependencies then
756
        // we can just resolve the instances of the objects right away, without
757
        // resolving any other types or dependencies out of these containers.
758
        if (is_null($constructor)) {
759
            array_pop($this->buildStack);
760

    
761
            return new $concrete;
762
        }
763

    
764
        $dependencies = $constructor->getParameters();
765

    
766
        // Once we have all the constructor's parameters we can create each of the
767
        // dependency instances and then use the reflection instances to make a
768
        // new instance of this class, injecting the created dependencies in.
769
        $parameters = $this->keyParametersByArgument(
770
            $dependencies, $parameters
771
        );
772

    
773
        $instances = $this->getDependencies(
774
            $dependencies, $parameters
775
        );
776

    
777
        array_pop($this->buildStack);
778

    
779
        return $reflector->newInstanceArgs($instances);
780
    }
781

    
782
    /**
783
     * Resolve all of the dependencies from the ReflectionParameters.
784
     *
785
     * @param  array  $parameters
786
     * @param  array  $primitives
787
     * @return array
788
     */
789
    protected function getDependencies(array $parameters, array $primitives = [])
790
    {
791
        $dependencies = [];
792

    
793
        foreach ($parameters as $parameter) {
794
            $dependency = $parameter->getClass();
795

    
796
            // If the class is null, it means the dependency is a string or some other
797
            // primitive type which we can not resolve since it is not a class and
798
            // we will just bomb out with an error since we have no-where to go.
799
            if (array_key_exists($parameter->name, $primitives)) {
800
                $dependencies[] = $primitives[$parameter->name];
801
            } elseif (is_null($dependency)) {
802
                $dependencies[] = $this->resolveNonClass($parameter);
803
            } else {
804
                $dependencies[] = $this->resolveClass($parameter);
805
            }
806
        }
807

    
808
        return $dependencies;
809
    }
810

    
811
    /**
812
     * Resolve a non-class hinted dependency.
813
     *
814
     * @param  \ReflectionParameter  $parameter
815
     * @return mixed
816
     *
817
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
818
     */
819
    protected function resolveNonClass(ReflectionParameter $parameter)
820
    {
821
        if (! is_null($concrete = $this->getContextualConcrete('$'.$parameter->name))) {
822
            if ($concrete instanceof Closure) {
823
                return call_user_func($concrete, $this);
824
            } else {
825
                return $concrete;
826
            }
827
        }
828

    
829
        if ($parameter->isDefaultValueAvailable()) {
830
            return $parameter->getDefaultValue();
831
        }
832

    
833
        $message = "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->getName()}";
834

    
835
        throw new BindingResolutionException($message);
836
    }
837

    
838
    /**
839
     * Resolve a class based dependency from the container.
840
     *
841
     * @param  \ReflectionParameter  $parameter
842
     * @return mixed
843
     *
844
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
845
     */
846
    protected function resolveClass(ReflectionParameter $parameter)
847
    {
848
        try {
849
            return $this->make($parameter->getClass()->name);
850
        }
851

    
852
        // If we can not resolve the class instance, we will check to see if the value
853
        // is optional, and if it is we will return the optional parameter value as
854
        // the value of the dependency, similarly to how we do this with scalars.
855
        catch (BindingResolutionException $e) {
856
            if ($parameter->isOptional()) {
857
                return $parameter->getDefaultValue();
858
            }
859

    
860
            throw $e;
861
        }
862
    }
863

    
864
    /**
865
     * If extra parameters are passed by numeric ID, rekey them by argument name.
866
     *
867
     * @param  array  $dependencies
868
     * @param  array  $parameters
869
     * @return array
870
     */
871
    protected function keyParametersByArgument(array $dependencies, array $parameters)
872
    {
873
        foreach ($parameters as $key => $value) {
874
            if (is_numeric($key)) {
875
                unset($parameters[$key]);
876

    
877
                $parameters[$dependencies[$key]->name] = $value;
878
            }
879
        }
880

    
881
        return $parameters;
882
    }
883

    
884
    /**
885
     * Register a new resolving callback.
886
     *
887
     * @param  string    $abstract
888
     * @param  \Closure|null  $callback
889
     * @return void
890
     */
891
    public function resolving($abstract, Closure $callback = null)
892
    {
893
        if ($callback === null && $abstract instanceof Closure) {
894
            $this->resolvingCallback($abstract);
895
        } else {
896
            $this->resolvingCallbacks[$this->normalize($abstract)][] = $callback;
897
        }
898
    }
899

    
900
    /**
901
     * Register a new after resolving callback for all types.
902
     *
903
     * @param  string   $abstract
904
     * @param  \Closure|null $callback
905
     * @return void
906
     */
907
    public function afterResolving($abstract, Closure $callback = null)
908
    {
909
        if ($abstract instanceof Closure && $callback === null) {
910
            $this->afterResolvingCallback($abstract);
911
        } else {
912
            $this->afterResolvingCallbacks[$this->normalize($abstract)][] = $callback;
913
        }
914
    }
915

    
916
    /**
917
     * Register a new resolving callback by type of its first argument.
918
     *
919
     * @param  \Closure  $callback
920
     * @return void
921
     */
922
    protected function resolvingCallback(Closure $callback)
923
    {
924
        $abstract = $this->getFunctionHint($callback);
925

    
926
        if ($abstract) {
927
            $this->resolvingCallbacks[$abstract][] = $callback;
928
        } else {
929
            $this->globalResolvingCallbacks[] = $callback;
930
        }
931
    }
932

    
933
    /**
934
     * Register a new after resolving callback by type of its first argument.
935
     *
936
     * @param  \Closure  $callback
937
     * @return void
938
     */
939
    protected function afterResolvingCallback(Closure $callback)
940
    {
941
        $abstract = $this->getFunctionHint($callback);
942

    
943
        if ($abstract) {
944
            $this->afterResolvingCallbacks[$abstract][] = $callback;
945
        } else {
946
            $this->globalAfterResolvingCallbacks[] = $callback;
947
        }
948
    }
949

    
950
    /**
951
     * Get the type hint for this closure's first argument.
952
     *
953
     * @param  \Closure  $callback
954
     * @return mixed
955
     */
956
    protected function getFunctionHint(Closure $callback)
957
    {
958
        $function = new ReflectionFunction($callback);
959

    
960
        if ($function->getNumberOfParameters() == 0) {
961
            return;
962
        }
963

    
964
        $expected = $function->getParameters()[0];
965

    
966
        if (! $expected->getClass()) {
967
            return;
968
        }
969

    
970
        return $expected->getClass()->name;
971
    }
972

    
973
    /**
974
     * Fire all of the resolving callbacks.
975
     *
976
     * @param  string  $abstract
977
     * @param  mixed   $object
978
     * @return void
979
     */
980
    protected function fireResolvingCallbacks($abstract, $object)
981
    {
982
        $this->fireCallbackArray($object, $this->globalResolvingCallbacks);
983

    
984
        $this->fireCallbackArray(
985
            $object, $this->getCallbacksForType(
986
                $abstract, $object, $this->resolvingCallbacks
987
            )
988
        );
989

    
990
        $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks);
991

    
992
        $this->fireCallbackArray(
993
            $object, $this->getCallbacksForType(
994
                $abstract, $object, $this->afterResolvingCallbacks
995
            )
996
        );
997
    }
998

    
999
    /**
1000
     * Get all callbacks for a given type.
1001
     *
1002
     * @param  string  $abstract
1003
     * @param  object  $object
1004
     * @param  array   $callbacksPerType
1005
     *
1006
     * @return array
1007
     */
1008
    protected function getCallbacksForType($abstract, $object, array $callbacksPerType)
1009
    {
1010
        $results = [];
1011

    
1012
        foreach ($callbacksPerType as $type => $callbacks) {
1013
            if ($type === $abstract || $object instanceof $type) {
1014
                $results = array_merge($results, $callbacks);
1015
            }
1016
        }
1017

    
1018
        return $results;
1019
    }
1020

    
1021
    /**
1022
     * Fire an array of callbacks with an object.
1023
     *
1024
     * @param  mixed  $object
1025
     * @param  array  $callbacks
1026
     * @return void
1027
     */
1028
    protected function fireCallbackArray($object, array $callbacks)
1029
    {
1030
        foreach ($callbacks as $callback) {
1031
            $callback($object, $this);
1032
        }
1033
    }
1034

    
1035
    /**
1036
     * Determine if a given type is shared.
1037
     *
1038
     * @param  string  $abstract
1039
     * @return bool
1040
     */
1041
    public function isShared($abstract)
1042
    {
1043
        $abstract = $this->normalize($abstract);
1044

    
1045
        if (isset($this->instances[$abstract])) {
1046
            return true;
1047
        }
1048

    
1049
        if (! isset($this->bindings[$abstract]['shared'])) {
1050
            return false;
1051
        }
1052

    
1053
        return $this->bindings[$abstract]['shared'] === true;
1054
    }
1055

    
1056
    /**
1057
     * Determine if the given concrete is buildable.
1058
     *
1059
     * @param  mixed   $concrete
1060
     * @param  string  $abstract
1061
     * @return bool
1062
     */
1063
    protected function isBuildable($concrete, $abstract)
1064
    {
1065
        return $concrete === $abstract || $concrete instanceof Closure;
1066
    }
1067

    
1068
    /**
1069
     * Get the alias for an abstract if available.
1070
     *
1071
     * @param  string  $abstract
1072
     * @return string
1073
     */
1074
    protected function getAlias($abstract)
1075
    {
1076
        if (! isset($this->aliases[$abstract])) {
1077
            return $abstract;
1078
        }
1079

    
1080
        return $this->getAlias($this->aliases[$abstract]);
1081
    }
1082

    
1083
    /**
1084
     * Get the container's bindings.
1085
     *
1086
     * @return array
1087
     */
1088
    public function getBindings()
1089
    {
1090
        return $this->bindings;
1091
    }
1092

    
1093
    /**
1094
     * Drop all of the stale instances and aliases.
1095
     *
1096
     * @param  string  $abstract
1097
     * @return void
1098
     */
1099
    protected function dropStaleInstances($abstract)
1100
    {
1101
        unset($this->instances[$abstract], $this->aliases[$abstract]);
1102
    }
1103

    
1104
    /**
1105
     * Remove a resolved instance from the instance cache.
1106
     *
1107
     * @param  string  $abstract
1108
     * @return void
1109
     */
1110
    public function forgetInstance($abstract)
1111
    {
1112
        unset($this->instances[$this->normalize($abstract)]);
1113
    }
1114

    
1115
    /**
1116
     * Clear all of the instances from the container.
1117
     *
1118
     * @return void
1119
     */
1120
    public function forgetInstances()
1121
    {
1122
        $this->instances = [];
1123
    }
1124

    
1125
    /**
1126
     * Flush the container of all bindings and resolved instances.
1127
     *
1128
     * @return void
1129
     */
1130
    public function flush()
1131
    {
1132
        $this->aliases = [];
1133
        $this->resolved = [];
1134
        $this->bindings = [];
1135
        $this->instances = [];
1136
    }
1137

    
1138
    /**
1139
     * Set the globally available instance of the container.
1140
     *
1141
     * @return static
1142
     */
1143
    public static function getInstance()
1144
    {
1145
        return static::$instance;
1146
    }
1147

    
1148
    /**
1149
     * Set the shared instance of the container.
1150
     *
1151
     * @param  \Illuminate\Contracts\Container\Container  $container
1152
     * @return void
1153
     */
1154
    public static function setInstance(ContainerContract $container)
1155
    {
1156
        static::$instance = $container;
1157
    }
1158

    
1159
    /**
1160
     * Determine if a given offset exists.
1161
     *
1162
     * @param  string  $key
1163
     * @return bool
1164
     */
1165
    public function offsetExists($key)
1166
    {
1167
        return $this->bound($key);
1168
    }
1169

    
1170
    /**
1171
     * Get the value at a given offset.
1172
     *
1173
     * @param  string  $key
1174
     * @return mixed
1175
     */
1176
    public function offsetGet($key)
1177
    {
1178
        return $this->make($key);
1179
    }
1180

    
1181
    /**
1182
     * Set the value at a given offset.
1183
     *
1184
     * @param  string  $key
1185
     * @param  mixed   $value
1186
     * @return void
1187
     */
1188
    public function offsetSet($key, $value)
1189
    {
1190
        // If the value is not a Closure, we will make it one. This simply gives
1191
        // more "drop-in" replacement functionality for the Pimple which this
1192
        // container's simplest functions are base modeled and built after.
1193
        if (! $value instanceof Closure) {
1194
            $value = function () use ($value) {
1195
                return $value;
1196
            };
1197
        }
1198

    
1199
        $this->bind($key, $value);
1200
    }
1201

    
1202
    /**
1203
     * Unset the value at a given offset.
1204
     *
1205
     * @param  string  $key
1206
     * @return void
1207
     */
1208
    public function offsetUnset($key)
1209
    {
1210
        $key = $this->normalize($key);
1211

    
1212
        unset($this->bindings[$key], $this->instances[$key], $this->resolved[$key]);
1213
    }
1214

    
1215
    /**
1216
     * Dynamically access container services.
1217
     *
1218
     * @param  string  $key
1219
     * @return mixed
1220
     */
1221
    public function __get($key)
1222
    {
1223
        return $this[$key];
1224
    }
1225

    
1226
    /**
1227
     * Dynamically set container services.
1228
     *
1229
     * @param  string  $key
1230
     * @param  mixed   $value
1231
     * @return void
1232
     */
1233
    public function __set($key, $value)
1234
    {
1235
        $this[$key] = $value;
1236
    }
1237
}
(1-1/3)