diff --git a/src/Illuminate/Container/Attributes/Scoped.php b/src/Illuminate/Container/Attributes/Scoped.php new file mode 100644 index 000000000000..1cf04e08d4b8 --- /dev/null +++ b/src/Illuminate/Container/Attributes/Scoped.php @@ -0,0 +1,10 @@ +instances[$abstract]) || - (isset($this->bindings[$abstract]['shared']) && - $this->bindings[$abstract]['shared'] === true); + if (isset($this->instances[$abstract])) { + return true; + } + + if (isset($this->bindings[$abstract]['shared']) && $this->bindings[$abstract]['shared'] === true) { + return true; + } + + if (! class_exists($abstract)) { + return false; + } + + $reflection = new ReflectionClass($abstract); + + if (! empty($reflection->getAttributes(Singleton::class))) { + return true; + } + + if (! empty($reflection->getAttributes(Scoped::class))) { + if (! in_array($abstract, $this->scopedInstances, true)) { + $this->scopedInstances[] = $abstract; + } + + return true; + } + + return false; } /** diff --git a/tests/Container/ContainerTest.php b/tests/Container/ContainerTest.php index 0302729bc970..6f2ac5d26ed8 100755 --- a/tests/Container/ContainerTest.php +++ b/tests/Container/ContainerTest.php @@ -3,6 +3,8 @@ namespace Illuminate\Tests\Container; use Attribute; +use Illuminate\Container\Attributes\Scoped; +use Illuminate\Container\Attributes\Singleton; use Illuminate\Container\Container; use Illuminate\Container\EntryNotFoundException; use Illuminate\Contracts\Container\BindingResolutionException; @@ -740,6 +742,30 @@ public function testMethodLevelContextualBinding() $this->assertInstanceOf(ContainerImplementationStub::class, $result); } + public function testContainerSingletonAttribute() + { + $container = new Container; + $firstInstantiation = $container->get(ContainerSingletonAttribute::class); + + $secondInstantiation = $container->get(ContainerSingletonAttribute::class); + + $this->assertSame($firstInstantiation, $secondInstantiation); + } + + public function testContainerScopedAttribute() + { + $container = new Container; + $firstInstantiation = $container->get(ContainerScopedAttribute::class); + $secondInstantiation = $container->get(ContainerScopedAttribute::class); + + $this->assertSame($firstInstantiation, $secondInstantiation); + + $container->forgetScopedInstances(); + + $thirdInstantiation = $container->get(ContainerScopedAttribute::class); + $this->assertNotSame($firstInstantiation, $thirdInstantiation); + } + // public function testContainerCanCatchCircularDependency() // { // $this->expectException(\Illuminate\Contracts\Container\CircularDependencyException::class); @@ -899,3 +925,13 @@ public function __construct( $this->currentlyResolving = $currentlyResolving; } } + +#[Singleton] +class ContainerSingletonAttribute +{ +} + +#[Scoped] +class ContainerScopedAttribute +{ +}