@@ -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.
@@ -59,22 +60,36 @@ private function checkOutEdges(array $edges): void
5960 $ node = $ edge ->getDestNode ();
6061 $ id = $ node ->getId ();
6162
62- if (empty ($ this ->checkedNodes [$ id ])) {
63- // Don't check circular references for lazy edges
64- if (!$ node ->getValue () || (!$ edge ->isLazy () && !$ edge ->isWeak ())) {
65- $ searchKey = array_search ($ id , $ this ->currentPath );
66- $ this ->currentPath [] = $ id ;
63+ if (!empty ($ this ->checkedNodes [$ id ])) {
64+ continue ;
65+ }
66+
67+ $ isLeaf = !!$ node ->getValue ();
68+ $ isConcrete = !$ edge ->isLazy () && !$ edge ->isWeak ();
69+
70+ // Skip already checked lazy services if they are still lazy. Will not gain any new information.
71+ if (!empty ($ this ->checkedLazyNodes [$ id ]) && (!$ isLeaf || !$ isConcrete )) {
72+ continue ;
73+ }
6774
68- if (false !== $ searchKey ) {
69- throw new ServiceCircularReferenceException ($ id , \array_slice ($ this ->currentPath , $ searchKey ));
70- }
75+ // Process concrete references, otherwise defer check circular references for lazy edges.
76+ if (!$ isLeaf || $ isConcrete ) {
77+ $ searchKey = array_search ($ id , $ this ->currentPath );
78+ $ this ->currentPath [] = $ id ;
7179
72- $ this ->checkOutEdges ($ node ->getOutEdges ());
80+ if (false !== $ searchKey ) {
81+ throw new ServiceCircularReferenceException ($ id , \array_slice ($ this ->currentPath , $ searchKey ));
7382 }
7483
84+ $ this ->checkOutEdges ($ node ->getOutEdges ());
85+
7586 $ this ->checkedNodes [$ id ] = true ;
76- array_pop ($ this ->currentPath );
87+ unset($ this ->checkedLazyNodes [$ id ]);
88+ } else {
89+ $ this ->checkedLazyNodes [$ id ] = true ;
7790 }
91+
92+ array_pop ($ this ->currentPath );
7893 }
7994 }
8095}
0 commit comments