@@ -195,6 +195,7 @@ public function dump(array $options = [])
195195 $ this ->singleUsePrivateIds [$ id ] = $ id ;
196196 }
197197 }
198+
198199 $ this ->container ->getCompiler ()->getServiceReferenceGraph ()->clear ();
199200 $ checkedNodes = [];
200201 $ this ->singleUsePrivateIds = array_diff_key ($ this ->singleUsePrivateIds , $ this ->circularReferences );
@@ -409,58 +410,66 @@ private function getProxyDumper(): ProxyDumper
409410 return $ this ->proxyDumper ;
410411 }
411412
412- private function analyzeCircularReferences (string $ sourceId , array $ edges , array &$ checkedNodes , array & $ currentPath = [], bool $ byConstructor = true )
413+ private function analyzeCircularReferences (string $ sourceId , array $ edges , array &$ checkedNodes , bool $ byConstructor = true )
413414 {
414- $ checkedNodes [$ sourceId ] = true ;
415- $ currentPath [$ sourceId ] = $ byConstructor ;
415+ $ newNodes = [];
416+ $ this ->collectReferences ($ sourceId , $ edges , $ checkedNodes , $ newNodes );
417+ $ this ->flattendNewReferences ($ checkedNodes , $ newNodes );
418+
419+ foreach ($ newNodes as $ newNodeId => $ _ ) {
420+ if (isset ($ checkedNodes [$ newNodeId ][$ newNodeId ])) {
421+ $ this ->addCircularReferences ($ newNodeId , $ checkedNodes [$ newNodeId ][$ newNodeId ][0 ], $ byConstructor && $ checkedNodes [$ newNodeId ][$ newNodeId ][1 ]);
422+ }
423+ }
424+ }
416425
426+ private function collectReferences (string $ sourceId , array $ edges , array &$ checkedNodes , array &$ newNodes )
427+ {
428+ $ checkedNodes [$ sourceId ] = [];
429+ $ newNodes [$ sourceId ] = [];
417430 foreach ($ edges as $ edge ) {
418431 $ node = $ edge ->getDestNode ();
419432 $ id = $ node ->getId ();
420-
421433 if (!$ node ->getValue () instanceof Definition || $ sourceId === $ id || $ edge ->isLazy () || $ edge ->isWeak ()) {
422- // no-op
423- } elseif (isset ($ currentPath [$ id ])) {
424- $ this ->addCircularReferences ($ id , $ currentPath , $ edge ->isReferencedByConstructor ());
425- } elseif (!isset ($ checkedNodes [$ id ])) {
426- $ this ->analyzeCircularReferences ($ id , $ node ->getOutEdges (), $ checkedNodes , $ currentPath , $ edge ->isReferencedByConstructor ());
427- } elseif (isset ($ this ->circularReferences [$ id ])) {
428- $ this ->connectCircularReferences ($ id , $ currentPath , $ edge ->isReferencedByConstructor ());
434+ continue ;
429435 }
430- }
431- unset($ currentPath [$ sourceId ]);
432- }
433436
434- private function connectCircularReferences ( string $ sourceId , array & $ currentPath , bool $ byConstructor , array & $ subPath = [])
435- {
436- $ currentPath [ $ sourceId ] = $ subPath [ $ sourceId ] = $ byConstructor ;
437+ if (! isset ( $ checkedNodes [ $ id ])) {
438+ $ this -> collectReferences ( $ id , $ node -> getOutEdges (), $ checkedNodes , $ newNodes );
439+ }
437440
438- foreach ($ this ->circularReferences [$ sourceId ] as $ id => $ byConstructor ) {
439- if (isset ($ currentPath [$ id ])) {
440- $ this ->addCircularReferences ($ id , $ currentPath , $ byConstructor );
441- } elseif (!isset ($ subPath [$ id ]) && isset ($ this ->circularReferences [$ id ])) {
442- $ this ->connectCircularReferences ($ id , $ currentPath , $ byConstructor , $ subPath );
441+ $ checkedNodes [$ sourceId ][$ id ] = [[], $ edge ->isReferencedByConstructor ()];
442+ if (isset ($ newNodes [$ id ])) {
443+ $ newNodes [$ id ][$ sourceId ] = true ;
443444 }
444445 }
445- unset($ currentPath [$ sourceId ], $ subPath [$ sourceId ]);
446446 }
447447
448- private function addCircularReferences ( string $ id , array $ currentPath , bool $ byConstructor )
448+ private function flattendNewReferences ( array & $ checkedNodes , array $ newNodes )
449449 {
450- $ currentPath [$ id ] = $ byConstructor ;
451- $ circularRefs = [];
452-
453- foreach (array_reverse ($ currentPath ) as $ parentId => $ v ) {
454- $ byConstructor = $ byConstructor && $ v ;
455- $ circularRefs [] = $ parentId ;
456-
457- if ($ parentId === $ id ) {
458- break ;
450+ $ nodesToFlatten = array_keys ($ newNodes );
451+ do {
452+ $ changedNodes = [];
453+ foreach ($ nodesToFlatten as $ newNodeId ) {
454+ $ deps = &$ checkedNodes [$ newNodeId ];
455+ foreach ($ deps as $ id => [$ path , $ depsByConstructor ]) {
456+ foreach ($ checkedNodes [$ id ] as $ depsId => [$ subPath , $ subDepsByConstructor ]) {
457+ if (!isset ($ deps [$ depsId ]) || ($ depsByConstructor && $ subDepsByConstructor && !$ deps [$ depsId ][1 ])) {
458+ $ deps [$ depsId ] = [array_merge ([$ id ], $ subPath ), $ depsByConstructor && $ subDepsByConstructor ];
459+ $ changedNodes += $ newNodes [$ newNodeId ] ?? [];
460+ }
461+ }
462+ }
459463 }
460- }
464+ $ nodesToFlatten = array_keys ($ changedNodes );
465+ } while (!empty ($ nodesToFlatten ));
466+ }
461467
462- $ currentId = $ id ;
463- foreach ($ circularRefs as $ parentId ) {
468+ private function addCircularReferences (string $ sourceId , array $ currentPath , bool $ byConstructor )
469+ {
470+ $ currentId = $ sourceId ;
471+ array_unshift ($ currentPath , $ currentId );
472+ foreach (array_reverse ($ currentPath ) as $ parentId ) {
464473 if (empty ($ this ->circularReferences [$ parentId ][$ currentId ])) {
465474 $ this ->circularReferences [$ parentId ][$ currentId ] = $ byConstructor ;
466475 }
0 commit comments