diff --git a/src/Illuminate/Container/Attributes/Give.php b/src/Illuminate/Container/Attributes/Give.php new file mode 100644 index 000000000000..88048a9f7c25 --- /dev/null +++ b/src/Illuminate/Container/Attributes/Give.php @@ -0,0 +1,35 @@ + $class + * @param array|null $params + */ + public function __construct( + public string $class, + public array $params = [] + ) {} + + /** + * Resolve the dependency. + * + * @param self $attribute + * @param \Illuminate\Contracts\Container\Container $container + * @return mixed + */ + public static function resolve(self $attribute, Container $container): mixed + { + return $container->make($attribute->class, $attribute->params); + } +} diff --git a/tests/Container/ContextualAttributeBindingTest.php b/tests/Container/ContextualAttributeBindingTest.php index 4eb73ad12d7d..64aec56d6394 100644 --- a/tests/Container/ContextualAttributeBindingTest.php +++ b/tests/Container/ContextualAttributeBindingTest.php @@ -15,6 +15,7 @@ use Illuminate\Container\Attributes\CurrentUser; use Illuminate\Container\Attributes\Database; use Illuminate\Container\Attributes\Log; +use Illuminate\Container\Attributes\Give; use Illuminate\Container\Attributes\RouteParameter; use Illuminate\Container\Attributes\Storage; use Illuminate\Container\Attributes\Tag; @@ -46,7 +47,7 @@ public function testDependencyCanBeResolvedFromAttributeBinding() { $container = new Container; - $container->bind(ContainerTestContract::class, fn () => new ContainerTestImplB); + $container->bind(ContainerTestContract::class, fn (): ContainerTestImplB => new ContainerTestImplB); $container->whenHasAttribute(ContainerTestAttributeThatResolvesContractImpl::class, function (ContainerTestAttributeThatResolvesContractImpl $attribute) { return match ($attribute->name) { 'A' => new ContainerTestImplA, @@ -65,6 +66,30 @@ public function testDependencyCanBeResolvedFromAttributeBinding() $this->assertInstanceOf(ContainerTestImplB::class, $classB->property); } + public function testSimpleDependencyCanBeResolvedCorrectlyFromGiveAttributeBinding() + { + $container = new Container; + + $container->bind(ContainerTestContract::class, concrete: ContainerTestImplA::class); + + $resolution = $container->make(GiveTestSimple::class); + + $this->assertInstanceOf(SimpleDependency::class, $resolution->dependency); + } + + + public function testComplexDependencyCanBeResolvedCorrectlyFromGiveAttributeBinding() + { + $container = new Container; + + $container->bind(ContainerTestContract::class, concrete: ContainerTestImplA::class); + + $resolution = $container->make(GiveTestComplex::class); + + $this->assertInstanceOf(ComplexDependency::class, $resolution->dependency); + $this->assertTrue($resolution->dependency->param); + } + public function testScalarDependencyCanBeResolvedFromAttributeBinding() { $container = new Container; @@ -161,6 +186,7 @@ public function testConfigAttribute() $container->make(ConfigTest::class); } + public function testDatabaseAttribute() { $container = new Container; @@ -435,6 +461,13 @@ public function __construct( } } +final class SimpleDependency implements ContainerTestContract {} + +final class ComplexDependency implements ContainerTestContract +{ + public function __construct(public bool $param) {} +} + final class AuthedTest { public function __construct(#[Authenticated('foo')] AuthenticatableContract $foo, #[CurrentUser('bar')] AuthenticatableContract $bar) @@ -505,6 +538,24 @@ public function __construct(#[Storage('foo')] Filesystem $foo, #[Storage('bar')] } } +final class GiveTestSimple +{ + public function __construct( + #[Give(SimpleDependency::class)] + public readonly ContainerTestContract $dependency + ) { + } +} + +final class GiveTestComplex +{ + public function __construct( + #[Give(ComplexDependency::class, ['param' => true])] + public readonly ContainerTestContract $dependency + ) { + } +} + final class TimezoneObject { public function __construct(