@@ -409,58 +409,66 @@ private function getProxyDumper(): ProxyDumper
409
409
return $ this ->proxyDumper ;
410
410
}
411
411
412
- private function analyzeCircularReferences (string $ sourceId , array $ edges , array &$ checkedNodes , array & $ currentPath = [], bool $ byConstructor = true )
412
+ private function analyzeCircularReferences (string $ sourceId , array $ edges , array &$ checkedNodes , bool $ byConstructor = true )
413
413
{
414
- $ checkedNodes [$ sourceId ] = true ;
415
- $ currentPath [$ sourceId ] = $ byConstructor ;
414
+ $ newNodes = [];
415
+ $ this ->collectReferences ($ sourceId , $ edges , $ checkedNodes , $ newNodes );
416
+ $ this ->flattendNewReferences ($ checkedNodes , $ newNodes );
416
417
418
+ foreach ($ newNodes as $ newNodeId => $ _ ) {
419
+ if (isset ($ checkedNodes [$ newNodeId ][$ newNodeId ])) {
420
+ $ this ->addCircularReferences ($ newNodeId , $ checkedNodes [$ newNodeId ][$ newNodeId ][0 ], $ byConstructor && $ checkedNodes [$ newNodeId ][$ newNodeId ][1 ]);
421
+ }
422
+ }
423
+ }
424
+
425
+ private function collectReferences (string $ sourceId , array $ edges , array &$ checkedNodes , array &$ newNodes )
426
+ {
427
+ $ checkedNodes [$ sourceId ] = [];
428
+ $ newNodes [$ sourceId ] = [];
417
429
foreach ($ edges as $ edge ) {
418
430
$ node = $ edge ->getDestNode ();
419
431
$ id = $ node ->getId ();
420
-
421
432
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 ());
433
+ continue ;
429
434
}
430
- }
431
- unset($ currentPath [$ sourceId ]);
432
- }
433
435
434
- private function connectCircularReferences ( string $ sourceId , array & $ currentPath , bool $ byConstructor , array & $ subPath = [])
435
- {
436
- $ currentPath [ $ sourceId ] = $ subPath [ $ sourceId ] = $ byConstructor ;
436
+ if (! isset ( $ checkedNodes [ $ id ])) {
437
+ $ this -> collectReferences ( $ id , $ node -> getOutEdges (), $ checkedNodes , $ newNodes );
438
+ }
437
439
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 );
440
+ $ checkedNodes [$ sourceId ][$ id ] = [[], $ edge ->isReferencedByConstructor ()];
441
+ if (isset ($ newNodes [$ id ])) {
442
+ $ newNodes [$ id ][$ sourceId ] = true ;
443
443
}
444
444
}
445
- unset($ currentPath [$ sourceId ], $ subPath [$ sourceId ]);
446
445
}
447
446
448
- private function addCircularReferences ( string $ id , array $ currentPath , bool $ byConstructor )
447
+ private function flattendNewReferences ( array & $ checkedNodes , array $ newNodes )
449
448
{
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 ;
449
+ $ nodesToFlatten = array_keys ($ newNodes );
450
+ do {
451
+ $ changedNodes = [];
452
+ foreach ($ nodesToFlatten as $ newNodeId ) {
453
+ $ deps = &$ checkedNodes [$ newNodeId ];
454
+ foreach ($ deps as $ id => [$ path , $ depsByConstructor ]) {
455
+ foreach ($ checkedNodes [$ id ] as $ depsId => [$ subPath , $ subDepsByConstructor ]) {
456
+ if (!isset ($ deps [$ depsId ]) || ($ depsByConstructor && $ subDepsByConstructor && !$ deps [$ depsId ][1 ])) {
457
+ $ deps [$ depsId ] = [array_merge ([$ id ], $ subPath ), $ depsByConstructor && $ subDepsByConstructor ];
458
+ $ changedNodes += $ newNodes [$ newNodeId ] ?? [];
459
+ }
460
+ }
461
+ }
459
462
}
460
- }
463
+ $ nodesToFlatten = array_keys ($ changedNodes );
464
+ } while (!empty ($ nodesToFlatten ));
465
+ }
461
466
462
- $ currentId = $ id ;
463
- foreach ($ circularRefs as $ parentId ) {
467
+ private function addCircularReferences (string $ sourceId , array $ currentPath , bool $ byConstructor )
468
+ {
469
+ $ currentId = $ sourceId ;
470
+ array_unshift ($ currentPath , $ currentId );
471
+ foreach (array_reverse ($ currentPath ) as $ parentId ) {
464
472
if (empty ($ this ->circularReferences [$ parentId ][$ currentId ])) {
465
473
$ this ->circularReferences [$ parentId ][$ currentId ] = $ byConstructor ;
466
474
}
0 commit comments