Skip to content

Commit 2dd0c86

Browse files
committed
Reset singletons
1 parent 26899c5 commit 2dd0c86

2 files changed

Lines changed: 54 additions & 20 deletions

File tree

packages/container/src/GenericContainer.php

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ public function __construct(
3030
/** @var ArrayIterator<array-key, mixed> $definitions */
3131
private(set) ArrayIterator $definitions = new ArrayIterator(),
3232

33-
/** @var ArrayIterator<array-key, mixed> $singletons */
34-
private(set) ArrayIterator $singletons = new ArrayIterator(),
33+
/** @var ArrayIterator<array-key, mixed> $singletonDefinitions */
34+
private(set) ArrayIterator $singletonDefinitions = new ArrayIterator(),
35+
36+
/** @var ArrayIterator<array-key, object> $resolvedSingletons */
37+
private(set) ArrayIterator $resolvedSingletons = new ArrayIterator(),
3538

3639
/** @var ArrayIterator<array-key, class-string> $initializers */
3740
private(set) ArrayIterator $initializers = new ArrayIterator(),
@@ -61,7 +64,7 @@ public function setDefinitions(array $definitions): self
6164

6265
public function setSingletons(array $singletons): self
6366
{
64-
$this->singletons = new ArrayIterator($singletons);
67+
$this->resolvedSingletons = new ArrayIterator($singletons);
6568

6669
return $this;
6770
}
@@ -97,7 +100,7 @@ public function getDefinitions(): array
97100
*/
98101
public function getSingletons(?string $interface = null): array
99102
{
100-
$singletons = $this->singletons->getArrayCopy();
103+
$singletons = $this->resolvedSingletons->getArrayCopy();
101104

102105
if (is_null($interface)) {
103106
return $singletons;
@@ -134,24 +137,34 @@ public function register(string $className, callable $definition): self
134137

135138
public function unregister(string $className, bool $tagged = false): self
136139
{
137-
unset($this->definitions[$className], $this->singletons[$className]);
140+
unset($this->definitions[$className]);
141+
unset($this->singletonDefinitions[$className]);
142+
unset($this->resolvedSingletons[$className]);
138143

139144
if ($tagged) {
140-
$singletons = array_filter(
141-
array: $this->getSingletons(),
142-
callback: static fn (mixed $_, string $key) => ! str_starts_with($key, "{$className}#"),
143-
mode: \ARRAY_FILTER_USE_BOTH,
144-
);
145+
foreach ($this->singletonDefinitions as $key => $definition) {
146+
if (! str_starts_with($key, "{$className}#")) {
147+
continue;
148+
}
149+
150+
unset($this->singletonDefinitions[$key]);
151+
}
145152

146-
$this->setSingletons($singletons);
153+
foreach ($this->resolvedSingletons as $key => $definition) {
154+
if (! str_starts_with($key, "{$className}#")) {
155+
continue;
156+
}
157+
158+
unset($this->resolvedSingletons[$key]);
159+
}
147160
}
148161

149162
return $this;
150163
}
151164

152165
public function has(string $className, null|string|UnitEnum $tag = null): bool
153166
{
154-
return isset($this->definitions[$className]) || isset($this->singletons[$this->resolveTaggedName($className, $tag)]);
167+
return isset($this->definitions[$className]) || isset($this->singletonDefinitions[$this->resolveTaggedName($className, $tag)]);
155168
}
156169

157170
public function singleton(string $className, mixed $definition, null|string|UnitEnum $tag = null): self
@@ -160,9 +173,9 @@ public function singleton(string $className, mixed $definition, null|string|Unit
160173
$tag = $definition->tag;
161174
}
162175

163-
$className = $this->resolveTaggedName($className, $tag);
176+
$dependencyName = $this->resolveTaggedName($className, $tag);
164177

165-
$this->singletons[$className] = $definition;
178+
$this->singletonDefinitions[$dependencyName] = $definition;
166179

167180
return $this;
168181
}
@@ -350,12 +363,18 @@ private function resolveDependency(string $className, null|string|UnitEnum $tag
350363

351364
$dependencyName = $this->resolveTaggedName($className, $tag);
352365

366+
// Check if a resolved singleton is present
367+
if ($instance = $this->resolvedSingletons[$dependencyName] ?? null) {
368+
$this->resolveChain()->add($class);
369+
370+
return $instance;
371+
}
372+
353373
// Check if the class has been registered as a singleton.
354-
if ($instance = $this->singletons[$dependencyName] ?? null) {
355-
if ($instance instanceof Closure) {
356-
$instance = $instance($this);
357-
$this->singletons[$className] = $instance;
358-
}
374+
if ($singletonDefinition = $this->singletonDefinitions[$dependencyName] ?? null) {
375+
$instance = $singletonDefinition instanceof Closure ? $singletonDefinition($this) : $singletonDefinition;
376+
377+
$this->resolvedSingletons[$dependencyName] = $instance;
359378

360379
$this->resolveChain()->add($class);
361380

@@ -696,6 +715,8 @@ public function addResettable(string|ClassReflector $resettableClass): Container
696715

697716
public function reset(): self
698717
{
718+
$this->resolvedSingletons = new ArrayIterator();
719+
699720
foreach ($this->resettables as $resettableClass) {
700721
/** @var Resettable $resettable */
701722
$resettable = $this->get($resettableClass);

packages/container/tests/ContainerTest.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@
7474
*/
7575
final class ContainerTest extends TestCase
7676
{
77+
protected function setUp(): void
78+
{
79+
parent::setUp();
80+
SingletonClass::$count = 0;
81+
}
82+
7783
public function test_get_with_autowire(): void
7884
{
7985
$container = new GenericContainer();
@@ -692,16 +698,23 @@ public function test_returns_decorator_without_constructor(): void
692698
$this->assertInstanceOf(DecoratorWithoutConstructor::class, $instance);
693699
}
694700

695-
public function test_resettables(): void
701+
public function test_reset(): void
696702
{
697703
ResettableDependency::$reset = false;
698704

699705
$container = new GenericContainer();
700706

701707
$container->addResettable(ResettableDependency::class);
708+
$container->singleton(SingletonClass::class, fn () => new SingletonClass());
709+
$container->get(SingletonClass::class);
710+
$container->get(SingletonClass::class);
711+
$this->assertSame(1, SingletonClass::$count);
702712

703713
$container->reset();
704714

715+
$container->get(SingletonClass::class);
716+
$container->get(SingletonClass::class);
717+
$this->assertSame(2, SingletonClass::$count); // constructed twice, once before and once after reset
705718
$this->assertTrue(ResettableDependency::$reset);
706719
}
707720
}

0 commit comments

Comments
 (0)