Skip to content

Commit f0a1614

Browse files
committed
Merge branch '6.4' into 7.2
* 6.4: [DependencyInjection] Defer check for circular references instead of skipping them. [Validator] Synchronize IBAN formats
2 parents 2eba71f + b343c3b commit f0a1614

File tree

2 files changed

+43
-10
lines changed

2 files changed

+43
-10
lines changed

Compiler/CheckCircularReferencesPass.php

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class CheckCircularReferencesPass implements CompilerPassInterface
2828
{
2929
private array $currentPath;
3030
private array $checkedNodes;
31+
private array $checkedLazyNodes;
3132

3233
/**
3334
* Checks the ContainerBuilder object for circular references.
@@ -57,22 +58,36 @@ private function checkOutEdges(array $edges): void
5758
$node = $edge->getDestNode();
5859
$id = $node->getId();
5960

60-
if (empty($this->checkedNodes[$id])) {
61-
// Don't check circular references for lazy edges
62-
if (!$node->getValue() || (!$edge->isLazy() && !$edge->isWeak())) {
63-
$searchKey = array_search($id, $this->currentPath);
64-
$this->currentPath[] = $id;
61+
if (!empty($this->checkedNodes[$id])) {
62+
continue;
63+
}
64+
65+
$isLeaf = !!$node->getValue();
66+
$isConcrete = !$edge->isLazy() && !$edge->isWeak();
67+
68+
// Skip already checked lazy services if they are still lazy. Will not gain any new information.
69+
if (!empty($this->checkedLazyNodes[$id]) && (!$isLeaf || !$isConcrete)) {
70+
continue;
71+
}
6572

66-
if (false !== $searchKey) {
67-
throw new ServiceCircularReferenceException($id, \array_slice($this->currentPath, $searchKey));
68-
}
73+
// Process concrete references, otherwise defer check circular references for lazy edges.
74+
if (!$isLeaf || $isConcrete) {
75+
$searchKey = array_search($id, $this->currentPath);
76+
$this->currentPath[] = $id;
6977

70-
$this->checkOutEdges($node->getOutEdges());
78+
if (false !== $searchKey) {
79+
throw new ServiceCircularReferenceException($id, \array_slice($this->currentPath, $searchKey));
7180
}
7281

82+
$this->checkOutEdges($node->getOutEdges());
83+
7384
$this->checkedNodes[$id] = true;
74-
array_pop($this->currentPath);
85+
unset($this->checkedLazyNodes[$id]);
86+
} else {
87+
$this->checkedLazyNodes[$id] = true;
7588
}
89+
90+
array_pop($this->currentPath);
7691
}
7792
}
7893
}

Tests/Compiler/CheckCircularReferencesPassTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
16+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
17+
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
1618
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
1719
use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass;
1820
use Symfony\Component\DependencyInjection\Compiler\Compiler;
21+
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
1922
use Symfony\Component\DependencyInjection\ContainerBuilder;
2023
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
2124
use Symfony\Component\DependencyInjection\Reference;
@@ -126,6 +129,21 @@ public function testProcessIgnoresLazyServices()
126129
$this->addToAssertionCount(1);
127130
}
128131

132+
public function testProcessDefersLazyServices()
133+
{
134+
$container = new ContainerBuilder();
135+
136+
$container->register('a')->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('tag', needsIndexes: true)));
137+
$container->register('b')->addArgument(new Reference('c'))->addTag('tag');
138+
$container->register('c')->addArgument(new Reference('b'));
139+
140+
(new ServiceLocatorTagPass())->process($container);
141+
142+
$this->expectException(ServiceCircularReferenceException::class);
143+
144+
$this->process($container);
145+
}
146+
129147
public function testProcessIgnoresIteratorArguments()
130148
{
131149
$container = new ContainerBuilder();

0 commit comments

Comments
 (0)