Skip to content

Commit 0d2bef3

Browse files
committed
Container: detects circular reference for parameters
1 parent 5fe4eb9 commit 0d2bef3

File tree

3 files changed

+41
-18
lines changed

3 files changed

+41
-18
lines changed

src/DI/Container.php

+25-16
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Container
4040
/** @var object[] service name => instance */
4141
private $instances = [];
4242

43-
/** @var array circular reference detector */
43+
/** @var array<string, true> circular reference detector */
4444
private $creating;
4545

4646
/** @var array<string, string|\Closure> */
@@ -66,7 +66,9 @@ public function getParameters(): array
6666
public function getParameter($key)
6767
{
6868
if (!array_key_exists($key, $this->parameters)) {
69-
$this->parameters[$key] = $this->getDynamicParameter($key);
69+
$this->parameters[$key] = $this->preventDeadLock("%$key%", function () use ($key) {
70+
return $this->getDynamicParameter($key);
71+
});
7072
}
7173
return $this->parameters[$key];
7274
}
@@ -219,28 +221,21 @@ public function createService(string $name, array $args = []): object
219221
{
220222
$name = $this->aliases[$name] ?? $name;
221223
$method = self::getMethodName($name);
222-
$cb = $this->methods[$method] ?? null;
223-
if (isset($this->creating[$name])) {
224-
throw new Nette\InvalidStateException(sprintf('Circular reference detected for services: %s.', implode(', ', array_keys($this->creating))));
225-
226-
} elseif ($cb === null) {
224+
$callback = $this->methods[$method] ?? null;
225+
if ($callback === null) {
227226
throw new MissingServiceException(sprintf("Service '%s' not found.", $name));
228227
}
229228

230-
try {
231-
$this->creating[$name] = true;
232-
$service = $cb instanceof \Closure
233-
? $cb(...$args)
229+
$service = $this->preventDeadLock($name, function () use ($callback, $args, $method) {
230+
return $callback instanceof \Closure
231+
? $callback(...$args)
234232
: $this->$method(...$args);
235-
236-
} finally {
237-
unset($this->creating[$name]);
238-
}
233+
});
239234

240235
if (!is_object($service)) {
241236
throw new Nette\UnexpectedValueException(sprintf(
242237
"Unable to create service '$name', value returned by %s is not object.",
243-
$cb instanceof \Closure ? 'closure' : "method $method()"
238+
$callback instanceof \Closure ? 'closure' : "method $method()"
244239
));
245240
}
246241

@@ -326,6 +321,20 @@ public function findByTag(string $tag): array
326321
}
327322

328323

324+
private function preventDeadLock(string $key, \Closure $callback)
325+
{
326+
if (isset($this->creating[$key])) {
327+
throw new Nette\InvalidStateException(sprintf('Circular reference detected for: %s.', implode(', ', array_keys($this->creating))));
328+
}
329+
try {
330+
$this->creating[$key] = true;
331+
return $callback();
332+
} finally {
333+
unset($this->creating[$key]);
334+
}
335+
}
336+
337+
329338
/********************* autowiring ****************d*g**/
330339

331340

tests/DI/Compiler.dynamicParameters.phpt

+15-1
Original file line numberDiff line numberDiff line change
@@ -140,5 +140,19 @@ test('Reference as parameter', function () {
140140
');
141141
Assert::exception(function () use ($container) {
142142
$container->getService('one');
143-
}, Nette\InvalidStateException::class, 'Circular reference detected for services: one.');
143+
}, Nette\InvalidStateException::class, 'Circular reference detected for: one, %dynamic%.');
144+
});
145+
146+
147+
test('Circula references', function () {
148+
$compiler = new DI\Compiler;
149+
$compiler->setDynamicParameterNames(['one', 'two']);
150+
$container = createContainer($compiler, '
151+
parameters:
152+
one: %two%
153+
two: %one%
154+
');
155+
Assert::exception(function () use ($container) {
156+
$container->getParameter('one');
157+
}, Nette\InvalidStateException::class, 'Circular reference detected for: %one%, %two%.');
144158
});

tests/DI/Container.circular.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ $container = new MyContainer;
3232

3333
Assert::exception(function () use ($container) {
3434
$container->getService('one');
35-
}, Nette\InvalidStateException::class, 'Circular reference detected for services: one, two.');
35+
}, Nette\InvalidStateException::class, 'Circular reference detected for: one, two.');

0 commit comments

Comments
 (0)