@@ -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}
0 commit comments