From 62b84e8e41f50c763feda56f1375f5bf3e17d88f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 23 Sep 2023 00:04:07 +0200 Subject: [PATCH] Container: detects circular reference for parameters --- src/DI/Container.php | 33 ++++++++++++++++++++----------- tests/DI/Compiler.parameters.phpt | 2 +- tests/DI/Container.circular.phpt | 2 +- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/DI/Container.php b/src/DI/Container.php index 0406cffdf..e1cfb7e1e 100644 --- a/src/DI/Container.php +++ b/src/DI/Container.php @@ -63,7 +63,9 @@ public function getParameters(): array public function getParameter($key) { if (!array_key_exists($key, $this->parameters)) { - $this->parameters[$key] = $this->getDynamicParameter($key); + $this->parameters[$key] = $this->criticalSection("%$key%", function () use ($key) { + return $this->getDynamicParameter($key); + }); } return $this->parameters[$key]; } @@ -211,22 +213,15 @@ public function createService(string $name, array $args = []): object $name = $this->aliases[$name] ?? $name; $method = self::getMethodName($name); $cb = $this->methods[$method] ?? null; - if (isset($this->creating[$name])) { - throw new Nette\InvalidStateException(sprintf('Circular reference detected for services: %s.', implode(', ', array_keys($this->creating)))); - - } elseif ($cb === null) { + if ($cb === null) { throw new MissingServiceException(sprintf("Service '%s' not found.", $name)); } - try { - $this->creating[$name] = true; - $service = $cb instanceof \Closure + $service = $this->criticalSection($name, function () use ($cb, $args, $method) { + return $cb instanceof \Closure ? $cb(...$args) : $this->$method(...$args); - - } finally { - unset($this->creating[$name]); - } + }); if (!is_object($service)) { throw new Nette\UnexpectedValueException(sprintf( @@ -317,6 +312,20 @@ public function findByTag(string $tag): array } + private function criticalSection(string $key, \Closure $closure) + { + if (isset($this->creating[$key])) { + throw new Nette\InvalidStateException(sprintf('Circular reference detected for: %s.', implode(', ', array_keys($this->creating)))); + } + try { + $this->creating[$key] = true; + return $closure(); + } finally { + unset($this->creating[$key]); + } + } + + /********************* autowiring ****************d*g**/ diff --git a/tests/DI/Compiler.parameters.phpt b/tests/DI/Compiler.parameters.phpt index e25b1d098..e68fbdf9b 100644 --- a/tests/DI/Compiler.parameters.phpt +++ b/tests/DI/Compiler.parameters.phpt @@ -117,6 +117,6 @@ test('', function () { $container->getParameter('bar'); }, Nette\InvalidStateException::class, - 'Circular reference detected for services: one.' + 'Circular reference detected for: %bar%, one.' ); }); diff --git a/tests/DI/Container.circular.phpt b/tests/DI/Container.circular.phpt index 0c1eb6c1e..33a32117b 100644 --- a/tests/DI/Container.circular.phpt +++ b/tests/DI/Container.circular.phpt @@ -32,4 +32,4 @@ $container = new MyContainer; Assert::exception(function () use ($container) { $container->getService('one'); -}, Nette\InvalidStateException::class, 'Circular reference detected for services: one, two.'); +}, Nette\InvalidStateException::class, 'Circular reference detected for: one, two.');