@@ -413,32 +413,66 @@ private function analyzeReferences()
413
413
$ this ->singleUsePrivateIds = array_diff_key ($ this ->singleUsePrivateIds , $ this ->circularReferences );
414
414
}
415
415
416
- private function collectCircularReferences (string $ sourceId , array $ edges , array &$ checkedNodes , array $ path = [], bool $ byConstructor = true ): void
416
+ private function collectCircularReferences (string $ sourceId , array $ edges , array &$ checkedNodes , array & $ loops = [], array $ path = [], bool $ byConstructor = true ): void
417
417
{
418
418
$ path [$ sourceId ] = $ byConstructor ;
419
419
$ checkedNodes [$ sourceId ] = true ;
420
420
foreach ($ edges as $ edge ) {
421
421
$ node = $ edge ->getDestNode ();
422
422
$ id = $ node ->getId ();
423
-
424
423
if (!($ definition = $ node ->getValue ()) instanceof Definition || $ sourceId === $ id || ($ edge ->isLazy () && ($ this ->proxyDumper ?? $ this ->getProxyDumper ())->isProxyCandidate ($ definition )) || $ edge ->isWeak ()) {
425
424
continue ;
426
425
}
427
426
428
427
if (isset ($ path [$ id ])) {
429
428
$ loop = null ;
430
429
$ loopByConstructor = $ edge ->isReferencedByConstructor ();
430
+ $ pathInLoop = [$ id , []];
431
431
foreach ($ path as $ k => $ pathByConstructor ) {
432
432
if (null !== $ loop ) {
433
433
$ loop [] = $ k ;
434
+ $ pathInLoop [1 ][$ k ] = $ pathByConstructor ;
435
+ $ loops [$ k ][] = &$ pathInLoop ;
434
436
$ loopByConstructor = $ loopByConstructor && $ pathByConstructor ;
435
437
} elseif ($ k === $ id ) {
436
438
$ loop = [];
437
439
}
438
440
}
439
441
$ this ->addCircularReferences ($ id , $ loop , $ loopByConstructor );
440
442
} elseif (!isset ($ checkedNodes [$ id ])) {
441
- $ this ->collectCircularReferences ($ id , $ node ->getOutEdges (), $ checkedNodes , $ path , $ edge ->isReferencedByConstructor ());
443
+ $ this ->collectCircularReferences ($ id , $ node ->getOutEdges (), $ checkedNodes , $ loops , $ path , $ edge ->isReferencedByConstructor ());
444
+ } elseif (isset ($ loops [$ id ])) {
445
+ // we already had detected loops for this edge
446
+ // let's check if we have a common ancestor in one of the detected loops
447
+ foreach ($ loops [$ id ] as [$ first , $ loopPath ]) {
448
+ if (!isset ($ path [$ first ])) {
449
+ continue ;
450
+ }
451
+ // We have a common ancestor, let's fill the current path
452
+ $ fillPath = null ;
453
+ foreach ($ loopPath as $ k => $ pathByConstructor ) {
454
+ if (null !== $ fillPath ) {
455
+ $ fillPath [$ k ] = $ pathByConstructor ;
456
+ } elseif ($ k === $ id ) {
457
+ $ fillPath = $ path ;
458
+ $ fillPath [$ k ] = $ pathByConstructor ;
459
+ }
460
+ }
461
+
462
+ // we can now build the loop
463
+ $ loop = null ;
464
+ $ loopByConstructor = $ edge ->isReferencedByConstructor ();
465
+ foreach ($ fillPath as $ k => $ pathByConstructor ) {
466
+ if (null !== $ loop ) {
467
+ $ loop [] = $ k ;
468
+ $ loopByConstructor = $ loopByConstructor && $ pathByConstructor ;
469
+ } elseif ($ k === $ first ) {
470
+ $ loop = [];
471
+ }
472
+ }
473
+ $ this ->addCircularReferences ($ first , $ loop , true );
474
+ break ;
475
+ }
442
476
}
443
477
}
444
478
unset($ path [$ sourceId ]);
0 commit comments