@@ -185,25 +185,7 @@ public function dump(array $options = [])
185
185
}
186
186
}
187
187
188
- (new AnalyzeServiceReferencesPass (false , !$ this ->getProxyDumper () instanceof NullDumper))->process ($ this ->container );
189
- $ checkedNodes = [];
190
- $ this ->circularReferences = [];
191
- $ this ->singleUsePrivateIds = [];
192
- foreach ($ this ->container ->getCompiler ()->getServiceReferenceGraph ()->getNodes () as $ id => $ node ) {
193
- if (!$ node ->getValue () instanceof Definition) {
194
- continue ;
195
- }
196
- if (!isset ($ checkedNodes [$ id ])) {
197
- $ this ->analyzeCircularReferences ($ id , $ node ->getOutEdges (), $ checkedNodes );
198
- }
199
- if ($ this ->isSingleUsePrivateNode ($ node )) {
200
- $ this ->singleUsePrivateIds [$ id ] = $ id ;
201
- }
202
- }
203
- $ this ->container ->getCompiler ()->getServiceReferenceGraph ()->clear ();
204
- $ checkedNodes = [];
205
- $ this ->singleUsePrivateIds = array_diff_key ($ this ->singleUsePrivateIds , $ this ->circularReferences );
206
-
188
+ $ this ->analyzeReferences ();
207
189
$ this ->docStar = $ options ['debug ' ] ? '* ' : '' ;
208
190
209
191
if (!empty ($ options ['file ' ]) && is_dir ($ dir = \dirname ($ options ['file ' ]))) {
@@ -429,61 +411,92 @@ private function getProxyDumper(): ProxyDumper
429
411
return $ this ->proxyDumper ;
430
412
}
431
413
432
- /**
433
- * @param ServiceReferenceGraphEdge[] $edges
434
- */
435
- private function analyzeCircularReferences (string $ sourceId , array $ edges , array &$ checkedNodes , array &$ currentPath = [], bool $ byConstructor = true )
414
+ private function analyzeReferences ()
436
415
{
437
- $ checkedNodes [$ sourceId ] = true ;
438
- $ currentPath [$ sourceId ] = $ byConstructor ;
416
+ (new AnalyzeServiceReferencesPass (false , !$ this ->getProxyDumper () instanceof NullDumper))->process ($ this ->container );
417
+ $ checkedNodes = [];
418
+ $ this ->circularReferences = [];
419
+ $ this ->singleUsePrivateIds = [];
420
+ foreach ($ this ->container ->getCompiler ()->getServiceReferenceGraph ()->getNodes () as $ id => $ node ) {
421
+ if (!$ node ->getValue () instanceof Definition) {
422
+ continue ;
423
+ }
439
424
440
- foreach ( $ edges as $ edge ) {
441
- $ node = $ edge -> getDestNode () ;
442
- $ id = $ node -> getId ();
425
+ if ( $ this -> isSingleUsePrivateNode ( $ node ) ) {
426
+ $ this -> singleUsePrivateIds [ $ id ] = $ id ;
427
+ }
443
428
444
- if (!$ node ->getValue () instanceof Definition || $ sourceId === $ id || $ edge ->isLazy () || $ edge ->isWeak ()) {
445
- // no-op
446
- } elseif (isset ($ currentPath [$ id ])) {
447
- $ this ->addCircularReferences ($ id , $ currentPath , $ edge ->isReferencedByConstructor ());
448
- } elseif (!isset ($ checkedNodes [$ id ])) {
449
- $ this ->analyzeCircularReferences ($ id , $ node ->getOutEdges (), $ checkedNodes , $ currentPath , $ edge ->isReferencedByConstructor ());
450
- } elseif (isset ($ this ->circularReferences [$ id ])) {
451
- $ this ->connectCircularReferences ($ id , $ currentPath , $ edge ->isReferencedByConstructor ());
429
+ $ newNodes = [];
430
+ if (!$ this ->collectCircularReferences ($ id , $ node ->getOutEdges (), $ checkedNodes , $ newNodes )) {
431
+ foreach ($ newNodes as $ newNodeId => $ _ ) {
432
+ $ checkedNodes [$ newNodeId ] = [];
433
+ }
434
+ continue ;
452
435
}
453
- }
454
- unset($ currentPath [$ sourceId ]);
455
- }
456
436
457
- private function connectCircularReferences (string $ sourceId , array &$ currentPath , bool $ byConstructor , array &$ subPath = [])
458
- {
459
- $ currentPath [$ sourceId ] = $ subPath [$ sourceId ] = $ byConstructor ;
437
+ $ nodesToFlatten = $ newNodes ;
438
+ do {
439
+ $ changedNodes = [];
440
+ foreach ($ nodesToFlatten as $ newNodeId => $ _ ) {
441
+ $ deps = &$ checkedNodes [$ newNodeId ];
442
+ foreach ($ deps as $ id => [$ path , $ depsByConstructor ]) {
443
+ foreach ($ checkedNodes [$ id ] as $ depsId => [$ subPath , $ subDepsByConstructor ]) {
444
+ if (!isset ($ deps [$ depsId ]) || ($ depsByConstructor && $ subDepsByConstructor && !$ deps [$ depsId ][1 ])) {
445
+ array_unshift ($ subPath , $ id );
446
+ $ deps [$ depsId ] = [$ subPath , $ depsByConstructor && $ subDepsByConstructor ];
447
+ $ changedNodes += $ newNodes [$ newNodeId ] ?? [];
448
+ }
449
+ }
450
+ }
451
+ }
452
+ } while ($ nodesToFlatten = $ changedNodes );
460
453
461
- foreach ($ this ->circularReferences [$ sourceId ] as $ id => $ byConstructor ) {
462
- if (isset ($ currentPath [$ id ])) {
463
- $ this ->addCircularReferences ($ id , $ currentPath , $ byConstructor );
464
- } elseif (!isset ($ subPath [$ id ]) && isset ($ this ->circularReferences [$ id ])) {
465
- $ this ->connectCircularReferences ($ id , $ currentPath , $ byConstructor , $ subPath );
454
+ foreach ($ newNodes as $ newNodeId => $ _ ) {
455
+ if (null !== $ n = $ checkedNodes [$ newNodeId ][$ newNodeId ] ?? null ) {
456
+ $ this ->addCircularReferences ($ newNodeId , $ n [0 ], $ n [1 ]);
457
+ }
466
458
}
467
459
}
468
- unset($ currentPath [$ sourceId ], $ subPath [$ sourceId ]);
460
+
461
+ $ this ->container ->getCompiler ()->getServiceReferenceGraph ()->clear ();
462
+ $ this ->singleUsePrivateIds = array_diff_key ($ this ->singleUsePrivateIds , $ this ->circularReferences );
469
463
}
470
464
471
- private function addCircularReferences (string $ id , array $ curr
179B
entPath , bool $ byConstructor )
465
+ private function collectCircularReferences (string $ sourceId , array $ edges , array & $ checkedNodes , array & $ newNodes , array $ path = []): bool
472
466
{
473
- $ currentPath [$ id ] = $ byConstructor ;
474
- $ circularRefs = [];
467
+ $ path [$ sourceId ] = true ;
468
+ $ checkedNodes [$ sourceId ] = [];
469
+ $ newNodes [$ sourceId ] = [];
470
+ $ circular = false ;
471
+ foreach ($ edges as $ edge ) {
472
+ $ node = $ edge ->getDestNode ();
473
+ $ id = $ node ->getId ();
474
+ if (!$ node ->getValue () instanceof Definition || $ sourceId === $ id || $ edge ->isLazy () || $ edge ->isWeak ()) {
475
+ continue ;
476
+ }
475
477
476
- foreach (array_reverse ($ currentPath ) as $ parentId => $ v ) {
477
- $ byConstructor = $ byConstructor && $ v ;
478
- $ circularRefs [] = $ parentId ;
478
+ if (isset ($ path [$ id ])) {
479
+ $ circular = true ;
480
+ } elseif (!isset ($ checkedNodes [$ id ])) {
481
+ $ circular = $ this ->collectCircularReferences ($ id , $ node ->getOutEdges (), $ checkedNodes , $ newNodes , $ path ) || $ circular ;
482
+ }
479
483
480
- if ($ parentId === $ id ) {
481
- break ;
484
+ $ checkedNodes [$ sourceId ][$ id ] = [[], $ edge ->isReferencedByConstructor ()];
485
+ if (isset ($ newNodes [$ id ])) {
486
+ $ newNodes [$ id ][$ sourceId ] = true ;
482
487
}
483
488
}
489
+ unset($ path [$ sourceId ]);
484
490
485
- $ currentId = $ id ;
486
- foreach ($ circularRefs as $ parentId ) {
491
+ return $ circular ;
492
+ }
493
+
494
+ private function addCircularReferences (string $ sourceId , array $ currentPath , bool $ byConstructor )
495
+ {
496
+ $ currentId = $ sourceId ;
497
+ $ currentPath = array_reverse ($ currentPath );
498
+ $ currentPath [] = $ currentId ;
499
+ foreach ($ currentPath as $ parentId ) {
487
500
if (empty ($ this ->circularReferences [$ parentId ][$ currentId ])) {
488
501
$ this ->circularReferences [$ parentId ][$ currentId ] = $ byConstructor ;
489
502
}
0 commit comments