@@ -433,32 +433,66 @@ private function analyzeReferences()
433
433
$ this ->singleUsePrivateIds = array_diff_key ($ this ->singleUsePrivateIds , $ this ->circularReferences );
434
434
}
435
435
436
- private function collectCircularReferences (string $ sourceId , array $ edges , array &$ checkedNodes , array $ path = [], bool $ byConstructor = true ): void
436
+ private function collectCircularReferences (string $ sourceId , array $ edges , array &$ checkedNodes , array & $ loops = [], array $ path = [], bool $ byConstructor = true ): void
437
437
{
438
438
$ path [$ sourceId ] = $ byConstructor ;
439
439
$ checkedNodes [$ sourceId ] = true ;
440
440
foreach ($ edges as $ edge ) {
441
441
$ node = $ edge ->getDestNode ();
442
442
$ id = $ node ->getId ();
443
-
444
443
if (!($ definition = $ node ->getValue ()) instanceof Definition || $ sourceId === $ id || ($ edge ->isLazy () && ($ this ->proxyDumper ?? $ this ->getProxyDumper ())->isProxyCandidate ($ definition )) || $ edge ->isWeak ()) {
445
444
continue ;
446
445
}
447
446
448
447
if (isset ($ path [$ id ])) {
449
448
$ loop = null ;
450
449
$ loopByConstructor = $ edge ->isReferencedByConstructor ();
450
+ $ pathInLoop = [$ id , []];
451
451
foreach ($ path as $ k => $ pathByConstructor ) {
452
452
if (null !== $ loop ) {
453
453
$ loop [] = $ k ;
454
+ $ pathInLoop [1 ][$ k ] = $ pathByConstructor ;
455
+ $ loops [$ k ][] = &$ pathInLoop ;
454
456
$ loopByConstructor = $ loopByConstructor && $ pathByConstructor ;
455
457
} elseif ($ k === $ id ) {
456
458
$ loop = [];
457
459
}
458
460
}
459
461
$ this ->addCircularReferences ($ id , $ loop , $ loopByConstructor );
460
462
} elseif (!isset ($ checkedNodes [$ id ])) {
461
- $ this ->collectCircularReferences ($ id , $ node ->getOutEdges (), $ checkedNodes , $ path , $ edge ->isReferencedByConstructor ());
463
+ $ this ->collectCircularReferences ($ id , $ node ->getOutEdges (), $ checkedNodes , $ loops , $ path , $ edge ->isReferencedByConstructor ());
464
+ } elseif (isset ($ loops [$ id ])) {
465
+ // we already had detected loops for this edge
466
+ // let's check if we have a common ancestor in one of the detected loops
467
+ foreach ($ loops [$ id ] as [$ first , $ loopPath ]) {
468
+ if (!isset ($ path [$ first ])) {
469
+ continue ;
470
+ }
471
+ // We have a common ancestor, let's fill the current path
472
+ $ fillPath = null ;
473
+ foreach ($ loopPath as $ k => $ pathByConstructor ) {
474
+ if (null !== $ fillPath ) {
475
+ $ fillPath [$ k ] = $ pathByConstructor ;
476
+ } elseif ($ k === $ id ) {
477
+ $ fillPath = $ path ;
478
+ $ fillPath [$ k ] = $ pathByConstructor ;
479
+ }
480
+ }
481
+
482
+ // we can now build the loop
483
+ $ loop = null ;
484
+ $ loopByConstructor = $ edge ->isReferencedByConstructor ();
485
+ foreach ($ fillPath as $ k => $ pathByConstructor ) {
486
+ if (null !== $ loop ) {
487
+ $ loop [] = $ k ;
488
+ $ loopByConstructor = $ loopByConstructor && $ pathByConstructor ;
489
+ } elseif ($ k === $ first ) {
490
+ $ loop = [];
491
+ }
492
+ }
493
+ $ this ->addCircularReferences ($ first , $ loop , true );
494
+ break ;
495
+ }
462
496
}
463
497
}
464
498
unset($ path [$ sourceId ]);
0 commit comments