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
|
}
|