@@ -180,25 +180,7 @@ public function dump(array $options = [])
180
180
}
181
181
}
182
182
183
- (new AnalyzeServiceReferencesPass (false , !$ this ->getProxyDumper () instanceof NullDumper))->process ($ this ->container );
184
- $ checkedNodes = [];
185
- $ this ->circularReferences = [];
186
- $ this ->singleUsePrivateIds = [];
187
- foreach ($ this ->container ->getCompiler ()->getServiceReferenceGraph ()->getNodes () as $ id => $ node ) {
188
- if (!$ node ->getValue () instanceof Definition) {
189
- continue ;
190
- }
191
- if (!isset ($ checkedNodes [$ id ])) {
192
- $ this ->analyzeCircularReferences ($ id , $ node ->getOutEdges (), $ checkedNodes );
193
- }
194
- if ($ this ->isSingleUsePrivateNode ($ node )) {
195
- $ this ->singleUsePrivateIds [$ id ] = $ id ;
196
- }
197
- }
198
- $ this ->container ->getCompiler ()->getServiceReferenceGraph ()->clear ();
199
- $ checkedNodes = [];
200
- $ this ->singleUsePrivateIds = array_diff_key ($ this ->singleUsePrivateIds , $ this ->circularReferences );
201
-
183
+ $ this ->analyzeReferences ();
202
184
$ this ->docStar = $ options ['debug ' ] ? '* ' : '' ;
203
185
204
186
if (!empty ($ options ['file ' ]) && is_dir ($ dir = \dirname ($ options ['file ' ]))) {
@@ -409,58 +391,92 @@ private function getProxyDumper(): ProxyDumper
409
391
return $ this ->proxyDumper ;
410
392
}
411
393
412
- private function analyzeCircularReferences ( string $ sourceId , array $ edges , array & $ checkedNodes , array & $ currentPath = [], bool $ byConstructor = true )
394
+ private function analyzeReferences ( )
413
395
{
414
- $ checkedNodes [$ sourceId ] = true ;
415
- $ currentPath [$ sourceId ] = $ byConstructor ;
396
+ (new AnalyzeServiceReferencesPass (false , !$ this ->getProxyDumper () instanceof NullDumper))->process ($ this ->container );
397
+ $ checkedNodes = [];
398
+ $ this ->circularReferences = [];
399
+ $ this ->singleUsePrivateIds = [];
400
+ foreach ($ this ->container ->getCompiler ()->getServiceReferenceGraph ()->getNodes () as $ id => $ node ) {
401
+ if (!$ node ->getValue () instanceof Definition) {
402
+ continue ;
403
+ }
416
404
417
- foreach ( $ edges as $ edge ) {
418
- $ node = $ edge -> getDestNode () ;
419
- $ id = $ node -> getId ();
405
+ if ( $ this -> isSingleUsePrivateNode ( $ node ) ) {
406
+ $ this -> singleUsePrivateIds [ $ id ] = $ id ;
407
+ }
420
408
421
- 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 ());
409
+ $ newNodes = [];
410
+ if (!$ this ->collectReferences ($ id , $ node ->getOutEdges (), $ checkedNodes , $ newNodes )) {
411
+ foreach ($ newNodes as $ newNode => $ _ ) {
412
+ $ checkedNodes [$ newNode ] = [];
413
+ }
414
+ continue ;
429
415
}
430
- }
431
- unset($ currentPath [$ sourceId ]);
432
- }
433
416
434
- private function connectCircularReferences (string $ sourceId , array &$ currentPath , bool $ byConstructor , array &$ subPath = [])
435
- {
436
- $ currentPath [$ sourceId ] = $ subPath [$ sourceId ] = $ byConstructor ;
417
+ $ nodesToFlatten = $ newNodes ;
418
+ do {
419
+ $ changedNodes = [];
420
+ foreach ($ nodesToFlatten as $ newNodeId => $ _ ) {
421
+ $ deps = &$ checkedNodes [$ newNodeId ];
422
+ foreach ($ deps as $ id => [$ path , $ depsByConstructor ]) {
423
+ foreach ($ checkedNodes [$ id ] as $ depsId => [$ subPath , $ subDepsByConstructor ]) {
424
+ if (!isset ($ deps [$ depsId ]) || ($ depsByConstructor && $ subDepsByConstructor && !$ deps [$ depsId ][1 ])) {
425
+ array_unshift ($ subPath , $ id );
426
+ $ deps [$ depsId ] = [$ subPath , $ depsByConstructor && $ subDepsByConstructor ];
427
+ $ changedNodes += $ newNodes [$ newNodeId ] ?? [];
428
+ }
429
+ }
430
+ }
431
+ }
432
+ } while ($ nodesToFlatten = $ changedNodes );
437
433
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 );
434
+ foreach ($ newNodes as $ newNodeId => $ _ ) {
435
+ if (null !== $ n = $ checkedNodes [$ newNodeId ][$ newNodeId ] ?? null ) {
436
+ $ this ->addCircularReferences ($ newNodeId , $ n [0 ], $ n [1 ]);
437
+ }
443
438
}
444
439
}
445
- unset($ currentPath [$ sourceId ], $ subPath [$ sourceId ]);
440
+
441
+ $ this ->container ->getCompiler ()->getServiceReferenceGraph ()->clear ();
442
+ $ this ->singleUsePrivateIds = array_diff_key ($ this ->singleUsePrivateIds , $ this ->circularReferences );
446
443
}
447
444
448
- private function addCircularReferences (string $ id , array $ currentPath , bool $ byConstructor )
445
+ private function collectReferences (string $ sourceId , array $ edges , array & $ checkedNodes , array & $ newNodes , array $ path = [] )
449
446
{
450
- $ currentPath [$ id ] = $ byConstructor ;
451
- $ circularRefs = [];
447
+ $ path [$ sourceId ] = true ;
448
+ $ checkedNodes [$ sourceId ] = [];
449
+ $ newNodes [$ sourceId ] = [];
450
+ $ circular = false ;
451
+ foreach ($ edges as $ edge ) {
452
+ $ node = $ edge ->getDestNode ();
453
+ $ id = $ node ->getId ();
454
+ if (!$ node ->getValue () instanceof Definition || $ sourceId === $ id || $ edge ->isLazy () || $ edge ->isWeak ()) {
455
+ continue ;
456
+ }
452
457
453
- foreach (array_reverse ($ currentPath ) as $ parentId => $ v ) {
454
- $ byConstructor = $ byConstructor && $ v ;
455
- $ circularRefs [] = $ parentId ;
458
+ if (isset ($ path [$ id ])) {
459
+ $ circular = true ;
460
+ } elseif (!isset ($ checkedNodes [$ id ])) {
461
+ $ circular = $ this ->collectReferences ($ id , $ node ->getOutEdges (), $ checkedNodes , $ newNodes , $ path ) || $ circular ;
462
+ }
456
463
457
- if ($ parentId === $ id ) {
458
- break ;
464
+ $ checkedNodes [$ sourceId ][$ id ] = [[], $ edge ->isReferencedByConstructor ()];
465
+ if (isset ($ newNodes [$ id ])) {
466
+ $ newNodes [$ id ][$ sourceId ] = true ;
459
467
}
460
468
}
469
+ unset($ path [$ sourceId ]);
461
470
462
- $ currentId = $ id ;
463
- foreach ($ circularRefs as $ parentId ) {
471
+ return $ circular ;
472
+ }
473
+
474
+ private function addCircularReferences (string $ sourceId , array $ currentPath , bool $ byConstructor )
475
+ {
476
+ $ currentId = $ sourceId ;
477
+ $ currentPath = array_reverse ($ currentPath );
478
+ $ currentPath [] = $ currentId ;
479
+ foreach ($ currentPath as $ parentId ) {
464
480
if (empty ($ this ->circularReferences [$ parentId ][$ currentId ])) {
465
481
$ this ->circularReferences [$ parentId ][$ currentId ] = $ byConstructor ;
466
482
}
0 commit comments