From fc8fd11cc0c1949e200af22449809fd997d03c46 Mon Sep 17 00:00:00 2001 From: Andreas Sundqvist Date: Wed, 3 Dec 2025 19:39:52 +0100 Subject: [PATCH 1/3] [di] Add support for NoCache attribute This is to allow disabling the cache built into the container --- src/Attributes/NoCache.php | 8 +++++++ src/Container.php | 11 ++++++++++ tests/ContainerBuilderTest.php | 39 ++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 src/Attributes/NoCache.php diff --git a/src/Attributes/NoCache.php b/src/Attributes/NoCache.php new file mode 100644 index 0000000..8dcf845 --- /dev/null +++ b/src/Attributes/NoCache.php @@ -0,0 +1,8 @@ +cache[$id])) { $factory = $this->definition->getDefinition($id); + $reflection = null; + if ($factory instanceof \Closure) { + $reflection = new \ReflectionFunction($factory); + } + elseif (is_object($factory)) { + $reflection = new \ReflectionClass($factory); + } + if ($reflection?->getAttributes(NoCache::class) && $factory) { + return $factory($this->rootContainer, $id); + } $this->cache[$id] = $factory ? $factory($this->rootContainer, $id) : null; } return $this->cache[$id]; diff --git a/tests/ContainerBuilderTest.php b/tests/ContainerBuilderTest.php index a2143e9..27b4301 100644 --- a/tests/ContainerBuilderTest.php +++ b/tests/ContainerBuilderTest.php @@ -3,6 +3,7 @@ namespace Stefna\DependencyInjection\Tests; use Stefna\DependencyInjection\AggregateContainer; +use Stefna\DependencyInjection\Attributes\NoCache; use Stefna\DependencyInjection\Container; use Stefna\DependencyInjection\ContainerBuilder; use Stefna\DependencyInjection\Definition\DefinitionArray; @@ -119,4 +120,42 @@ public function testDefinitionOrder(): void $this->assertSame(4, $container->get('test3')); $this->assertSame(12, $container->get('test4')); } + + public function testDefaultCacheService(): void + { + $builder = new ContainerBuilder(); + $builder->addDefinition([ + \DateTimeInterface::class => fn () => new \DateTimeImmutable(), + ]); + + $container = $builder->build(); + + $this->assertInstanceOf(Container::class, $container); + + $this->assertTrue($container->has(\DateTimeInterface::class)); + + $this->assertSame( + $container->get(\DateTimeInterface::class), + $container->get(\DateTimeInterface::class) + ); + } + + public function testDisableServiceCache(): void + { + $builder = new ContainerBuilder(); + $builder->addDefinition([ + \DateTimeInterface::class => #[NoCache] fn () => new \DateTimeImmutable(), + ]); + + $container = $builder->build(); + + $this->assertInstanceOf(Container::class, $container); + + $this->assertTrue($container->has(\DateTimeInterface::class)); + + $this->assertNotSame( + $container->get(\DateTimeInterface::class), + $container->get(\DateTimeInterface::class) + ); + } } From 905d85160f1226d864b456173466ae14322048fe Mon Sep 17 00:00:00 2001 From: Andreas Sundqvist Date: Wed, 3 Dec 2025 19:40:59 +0100 Subject: [PATCH 2/3] [di] Remove instance cache in the aggregated container If cache is to be used it should be determined by the individual containers --- src/AggregateContainer.php | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/AggregateContainer.php b/src/AggregateContainer.php index ad73061..a9e8595 100644 --- a/src/AggregateContainer.php +++ b/src/AggregateContainer.php @@ -12,8 +12,6 @@ final class AggregateContainer implements ContainerInterface, DelegateContainerA private array $containers = []; /** @var string[] */ private array $ids = []; - /** @var array */ - private array $cache = []; /** @var array */ private array $factoryCache = []; /** @var float[] */ @@ -58,17 +56,13 @@ public function haveContainer(ContainerInterface $container): bool */ public function get(string $id) { - if (isset($this->cache[$id])) { - return $this->cache[$id]; - } - if (!$this->has($id)) { throw NotFoundException::withIdentifier($id); } $factory = $this->factoryCache[$id]; unset($this->factoryCache[$id]); - return $this->cache[$id] = $factory(); + return $factory(); } /** @@ -76,9 +70,6 @@ public function get(string $id) */ public function has(string $id): bool { - if (isset($this->cache[$id])) { - return true; - } if (isset($this->factoryCache[$id])) { return true; } From 45c88f82201af77baf31c5a39cda1d086a4e0b1e Mon Sep 17 00:00:00 2001 From: Andreas Sundqvist Date: Fri, 5 Dec 2025 12:53:03 +0100 Subject: [PATCH 3/3] [di] Add test case for change of AggregateContainer --- tests/AggregateContainerTest.php | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/AggregateContainerTest.php b/tests/AggregateContainerTest.php index 9243678..f528e7b 100644 --- a/tests/AggregateContainerTest.php +++ b/tests/AggregateContainerTest.php @@ -4,7 +4,9 @@ use Psr\Container\ContainerInterface; use Stefna\DependencyInjection\AggregateContainer; +use Stefna\DependencyInjection\Attributes\NoCache; use Stefna\DependencyInjection\Container; +use Stefna\DependencyInjection\ContainerBuilder; use Stefna\DependencyInjection\Definition\DefinitionArray; use Stefna\DependencyInjection\Exception\DuplicateEntryException; use PHPUnit\Framework\TestCase; @@ -114,4 +116,55 @@ public function testNested2AggregateContainerGetUsedAsRootContainerWhenCallingFa $entity = $rootAggregateContainer->get(TestWithArgs::class); $this->assertSame($time, $entity->date); } + + public function testDefaultCacheService(): void + { + $time = new \DateTimeImmutable(); + $rootAggregateContainer = new AggregateContainer('1'); + $aggregateContainer1 = new AggregateContainer('2'); + $aggregateContainer2 = new AggregateContainer('3'); + + $aggregateContainer1->addContainer(new Container(new DefinitionArray([ + \DateTimeImmutable::class => fn () => $time, + ]))); + $aggregateContainer2->addContainer(new Container(new DefinitionArray([ + TestWithArgs::class => function (ContainerInterface $c) use ($rootAggregateContainer) { + $this->assertSame($rootAggregateContainer, $c); + return new TestWithArgs($c->get(\DateTimeImmutable::class)); + }, + ]))); + + $rootAggregateContainer->addContainer($aggregateContainer2); + $rootAggregateContainer->addContainer($aggregateContainer1); + + $this->assertSame( + $rootAggregateContainer->get(\DateTimeImmutable::class), + $rootAggregateContainer->get(TestWithArgs::class)->date, + ); + } + + public function testDisableServiceCache(): void + { + $rootAggregateContainer = new AggregateContainer('1'); + $aggregateContainer1 = new AggregateContainer('2'); + $aggregateContainer2 = new AggregateContainer('3'); + + $aggregateContainer1->addContainer(new Container(new DefinitionArray([ + \DateTimeImmutable::class => #[NoCache] fn () => new \DateTimeImmutable(), + ]))); + $aggregateContainer2->addContainer(new Container(new DefinitionArray([ + TestWithArgs::class => function (ContainerInterface $c) use ($rootAggregateContainer) { + $this->assertSame($rootAggregateContainer, $c); + return new TestWithArgs($c->get(\DateTimeImmutable::class)); + }, + ]))); + + $rootAggregateContainer->addContainer($aggregateContainer2); + $rootAggregateContainer->addContainer($aggregateContainer1); + + $this->assertNotSame( + $rootAggregateContainer->get(\DateTimeImmutable::class), + $rootAggregateContainer->get(TestWithArgs::class)->date, + ); + } }