Skip to content

Commit de8d515

Browse files
committed
support contextual binding on first class callables
1 parent cc3ccc3 commit de8d515

File tree

2 files changed

+47
-2
lines changed

2 files changed

+47
-2
lines changed

src/Illuminate/Container/Container.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use LogicException;
1212
use ReflectionClass;
1313
use ReflectionException;
14+
use ReflectionFunction;
1415
use ReflectionParameter;
1516
use TypeError;
1617

@@ -648,8 +649,8 @@ public function call($callback, array $parameters = [], $defaultMethod = null)
648649
{
649650
$pushedToBuildStack = false;
650651

651-
if (is_array($callback) && ! in_array(
652-
$className = (is_string($callback[0]) ? $callback[0] : get_class($callback[0])),
652+
if (($className = $this->getClassForCallable($callback)) && ! in_array(
653+
$className,
653654
$this->buildStack,
654655
true
655656
)) {
@@ -667,6 +668,22 @@ public function call($callback, array $parameters = [], $defaultMethod = null)
667668
return $result;
668669
}
669670

671+
/**
672+
* Get the class name for the given callback, if one can be determined.
673+
*
674+
* @param callable|string $callback
675+
* @return string|false
676+
*/
677+
protected function getClassForCallable($callback)
678+
{
679+
if (is_callable($callback) &&
680+
! ($reflector = new ReflectionFunction($callback(...)))->isAnonymous()) {
681+
return $reflector->getClosureScopeClass()->name ?? false;
682+
}
683+
684+
return false;
685+
}
686+
670687
/**
671688
* Get a closure to resolve the given type from the container.
672689
*

tests/Container/ContextualBindingTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,26 @@ public function testContextualBindingGivesValuesFromConfigArray()
481481
$this->assertSame('hunter42', $resolvedInstance->settings['password']);
482482
$this->assertSame('lumen', $resolvedInstance->settings['alias']);
483483
}
484+
485+
public function testContextualBindingWorksForMethodInvocation()
486+
{
487+
$container = new Container;
488+
489+
$container
490+
->when(ContainerTestContextInjectMethodArgument::class)
491+
->needs(IContainerContextContractStub::class)
492+
->give(ContainerContextImplementationStub::class);
493+
494+
$object = new ContainerTestContextInjectMethodArgument;
495+
496+
// array callable syntax...
497+
$valueResolvedUsingArraySyntax = $container->call([$object, 'method']);
498+
$this->assertInstanceOf(ContainerContextImplementationStub::class, $valueResolvedUsingArraySyntax);
499+
500+
// first class callable syntax...
501+
$valueResolvedUsingFirstClassSyntax = $container->call($object->method(...));
502+
$this->assertInstanceOf(ContainerContextImplementationStub::class, $valueResolvedUsingFirstClassSyntax);
503+
}
484504
}
485505

486506
interface IContainerContextContractStub
@@ -620,3 +640,11 @@ public function __construct($settings)
620640
$this->settings = $settings;
621641
}
622642
}
643+
644+
class ContainerTestContextInjectMethodArgument
645+
{
646+
public function method(IContainerContextContractStub $dependency)
647+
{
648+
return $dependency;
649+
}
650+
}

0 commit comments

Comments
 (0)