9
9
use Illuminate \Contracts \Container \CircularDependencyException ;
10
10
use Illuminate \Contracts \Container \Container as ContainerContract ;
11
11
use Illuminate \Contracts \Container \ContextualAttribute ;
12
+ use Illuminate \Support \Collection ;
12
13
use LogicException ;
13
14
use ReflectionAttribute ;
14
15
use ReflectionClass ;
15
16
use ReflectionException ;
16
17
use ReflectionFunction ;
18
+ use ReflectionIntersectionType ;
17
19
use ReflectionParameter ;
20
+ use ReflectionUnionType ;
18
21
use TypeError ;
19
22
20
23
class Container implements ArrayAccess, ContainerContract
@@ -268,15 +271,22 @@ public function isAlias($name)
268
271
/**
269
272
* Register a binding with the container.
270
273
*
271
- * @param string $abstract
274
+ * @param \Closure| string $abstract
272
275
* @param \Closure|string|null $concrete
273
276
* @param bool $shared
274
277
* @return void
275
278
*
276
279
* @throws \TypeError
280
+ * @throws ReflectionException
277
281
*/
278
282
public function bind ($ abstract , $ concrete = null , $ shared = false )
279
283
{
284
+ if ($ abstract instanceof Closure) {
285
+ return $ this ->bindBasedOnClosureReturnTypes (
286
+ $ abstract , $ concrete , $ shared
287
+ );
288
+ }
289
+
280
290
$ this ->dropStaleInstances ($ abstract );
281
291
282
292
// If no concrete type was given, we will simply set the concrete type to the
@@ -381,7 +391,7 @@ public function callMethodBinding($method, $instance)
381
391
* Add a contextual binding to the container.
382
392
*
383
393
* @param string $concrete
384
- * @param string $abstract
394
+ * @param \Closure| string $abstract
385
395
* @param \Closure|string $implementation
386
396
* @return void
387
397
*/
@@ -393,7 +403,7 @@ public function addContextualBinding($concrete, $abstract, $implementation)
393
403
/**
394
404
* Register a binding if it hasn't already been registered.
395
405
*
396
- * @param string $abstract
406
+ * @param \Closure| string $abstract
397
407
* @param \Closure|string|null $concrete
398
408
* @param bool $shared
399
409
* @return void
@@ -408,7 +418,7 @@ public function bindIf($abstract, $concrete = null, $shared = false)
408
418
/**
409
419
* Register a shared binding in the container.
410
420
*
411
- * @param string $abstract
421
+ * @param \Closure| string $abstract
412
422
* @param \Closure|string|null $concrete
413
423
* @return void
414
424
*/
@@ -420,7 +430,7 @@ public function singleton($abstract, $concrete = null)
420
430
/**
421
431
* Register a shared binding if it hasn't already been registered.
422
432
*
423
- * @param string $abstract
433
+ * @param \Closure| string $abstract
424
434
* @param \Closure|string|null $concrete
425
435
* @return void
426
436
*/
@@ -434,7 +444,7 @@ public function singletonIf($abstract, $concrete = null)
434
444
/**
435
445
* Register a scoped binding in the container.
436
446
*
437
- * @param string $abstract
447
+ * @param \Closure| string $abstract
438
448
* @param \Closure|string|null $concrete
439
449
* @return void
440
450
*/
@@ -448,7 +458,7 @@ public function scoped($abstract, $concrete = null)
448
458
/**
449
459
* Register a scoped binding if it hasn't already been registered.
450
460
*
451
- * @param string $abstract
461
+ * @param \Closure| string $abstract
452
462
* @param \Closure|string|null $concrete
453
463
* @return void
454
464
*/
@@ -459,6 +469,54 @@ public function scopedIf($abstract, $concrete = null)
459
469
}
460
470
}
461
471
472
+ /**
473
+ * Register a binding with the container based on the given Closure's return types.
474
+ *
475
+ * @param \Closure|string $abstract
476
+ * @param \Closure|string|null $concrete
477
+ * @param bool $shared
478
+ * @return void
479
+ */
480
+ protected function bindBasedOnClosureReturnTypes ($ abstract , $ concrete = null , $ shared = false )
481
+ {
482
+ $ abstracts = $ this ->closureReturnTypes ($ abstract );
483
+
484
+ $ concrete = $ abstract ;
485
+
486
+ foreach ($ abstracts as $ abstract ) {
487
+ $ this ->bind ($ abstract , $ concrete , $ shared );
488
+ }
489
+ }
490
+
491
+ /**
492
+ * Get the class names / types of the return type of the given Closure.
493
+ *
494
+ * @param \Closure $closure
495
+ * @return list<class-string>
496
+ *
497
+ * @throws \ReflectionException
498
+ */
499
+ protected function closureReturnTypes (Closure $ closure )
500
+ {
501
+ $ reflection = new ReflectionFunction ($ closure );
502
+
503
+ if ($ reflection ->getReturnType () === null ||
504
+ $ reflection ->getReturnType () instanceof ReflectionIntersectionType) {
505
+ return [];
506
+ }
507
+
508
+ $ types = $ reflection ->getReturnType () instanceof ReflectionUnionType
509
+ ? $ reflection ->getReturnType ()->getTypes ()
510
+ : [$ reflection ->getReturnType ()];
511
+
512
+ return (new Collection ($ types ))
513
+ ->reject (fn ($ type ) => $ type ->isBuiltin ())
514
+ ->reject (fn ($ type ) => in_array ($ type ->getName (), ['static ' , 'self ' ]))
515
+ ->map (fn ($ type ) => $ type ->getName ())
516
+ ->values ()
517
+ ->all ();
518
+ }
519
+
462
520
/**
463
521
* "Extend" an abstract type in the container.
464
522
*
0 commit comments