Skip to content

Commit 6b11d5f

Browse files
authored
[12.x] Add Singleton and Scoped attributes to Container (#56334)
* Add Singleton and Scoped attributes * Add to instances on subsequent calls so reflection isn't necessary after * Don't add it to the instances
1 parent 2fd6f3c commit 6b11d5f

File tree

4 files changed

+85
-3
lines changed

4 files changed

+85
-3
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Illuminate\Container\Attributes;
4+
5+
use Attribute;
6+
7+
#[Attribute(Attribute::TARGET_CLASS)]
8+
final class Scoped
9+
{
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Illuminate\Container\Attributes;
4+
5+
use Attribute;
6+
7+
#[Attribute(Attribute::TARGET_CLASS)]
8+
final class Singleton
9+
{
10+
}

src/Illuminate/Container/Container.php

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use ArrayAccess;
66
use Closure;
77
use Exception;
8+
use Illuminate\Container\Attributes\Scoped;
9+
use Illuminate\Container\Attributes\Singleton;
810
use Illuminate\Contracts\Container\BindingResolutionException;
911
use Illuminate\Contracts\Container\CircularDependencyException;
1012
use Illuminate\Contracts\Container\Container as ContainerContract;
@@ -252,9 +254,33 @@ public function resolved($abstract)
252254
*/
253255
public function isShared($abstract)
254256
{
255-
return isset($this->instances[$abstract]) ||
256-
(isset($this->bindings[$abstract]['shared']) &&
257-
$this->bindings[$abstract]['shared'] === true);
257+
if (isset($this->instances[$abstract])) {
258+
return true;
259+
}
260+
261+
if (isset($this->bindings[$abstract]['shared']) && $this->bindings[$abstract]['shared'] === true) {
262+
return true;
263+
}
264+
265+
if (! class_exists($abstract)) {
266+
return false;
267+
}
268+
269+
$reflection = new ReflectionClass($abstract);
270+
271+
if (! empty($reflection->getAttributes(Singleton::class))) {
272+
return true;
273+
}
274+
275+
if (! empty($reflection->getAttributes(Scoped::class))) {
276+
if (! in_array($abstract, $this->scopedInstances, true)) {
277+
$this->scopedInstances[] = $abstract;
278+
}
279+
280+
return true;
281+
}
282+
283+
return false;
258284
}
259285

260286
/**

tests/Container/ContainerTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace Illuminate\Tests\Container;
44

55
use Attribute;
6+
use Illuminate\Container\Attributes\Scoped;
7+
use Illuminate\Container\Attributes\Singleton;
68
use Illuminate\Container\Container;
79
use Illuminate\Container\EntryNotFoundException;
810
use Illuminate\Contracts\Container\BindingResolutionException;
@@ -740,6 +742,30 @@ public function testMethodLevelContextualBinding()
740742
$this->assertInstanceOf(ContainerImplementationStub::class, $result);
741743
}
742744

745+
public function testContainerSingletonAttribute()
746+
{
747+
$container = new Container;
748+
$firstInstantiation = $container->get(ContainerSingletonAttribute::class);
749+
750+
$secondInstantiation = $container->get(ContainerSingletonAttribute::class);
751+
752+
$this->assertSame($firstInstantiation, $secondInstantiation);
753+
}
754+
755+
public function testContainerScopedAttribute()
756+
{
757+
$container = new Container;
758+
$firstInstantiation = $container->get(ContainerScopedAttribute::class);
759+
$secondInstantiation = $container->get(ContainerScopedAttribute::class);
760+
761+
$this->assertSame($firstInstantiation, $secondInstantiation);
762+
763+
$container->forgetScopedInstances();
764+
765+
$thirdInstantiation = $container->get(ContainerScopedAttribute::class);
766+
$this->assertNotSame($firstInstantiation, $thirdInstantiation);
767+
}
768+
743769
// public function testContainerCanCatchCircularDependency()
744770
// {
745771
// $this->expectException(\Illuminate\Contracts\Container\CircularDependencyException::class);
@@ -899,3 +925,13 @@ public function __construct(
899925
$this->currentlyResolving = $currentlyResolving;
900926
}
901927
}
928+
929+
#[Singleton]
930+
class ContainerSingletonAttribute
931+
{
932+
}
933+
934+
#[Scoped]
935+
class ContainerScopedAttribute
936+
{
937+
}

0 commit comments

Comments
 (0)