16
16
use Symfony \Component \DependencyInjection \Argument \ServiceClosureArgument ;
17
17
use Symfony \Component \DependencyInjection \Variable ;
18
18
use Symfony \Component \DependencyInjection \Definition ;
19
+ use Symfony \Component \DependencyInjection \Compiler \AnalyzeServiceReferencesPass ;
19
20
use Symfony \Component \DependencyInjection \ContainerBuilder ;
20
21
use Symfony \Component \DependencyInjection \Container ;
21
22
use Symfony \Component \DependencyInjection \ContainerInterface ;
@@ -66,6 +67,7 @@ class PhpDumper extends Dumper
66
67
private $ hotPathTag ;
67
68
private $ inlineRequires ;
68
69
private $ inlinedRequires = array ();
70
+ private $ circularReferences = array ();
69
71
70
72
/**
71
73
* @var ProxyDumper
@@ -133,6 +135,14 @@ public function dump(array $options = array())
133
135
134
136
$ this ->initializeMethodNamesMap ('Container ' === $ baseClass ? Container::class : $ baseClass );
135
137
138
+ (new AnalyzeServiceReferencesPass ())->process ($ this ->container );
139
+ $ this ->circularReferences = array ();
140
+ $ checkedNodes = array ();
141
+ foreach ($ this ->container ->getCompiler ()->getServiceReferenceGraph ()->getNodes () as $ id => $ node ) {
142
+ $ currentPath = array ($ id => $ id );
143
+ $ this ->analyzeCircularReferences ($ node ->getOutEdges (), $ checkedNodes , $ currentPath );
144
+ }
145
+
136
146
$ this ->docStar = $ options ['debug ' ] ? '* ' : '' ;
137
147
138
148
if (!empty ($ options ['file ' ]) && is_dir ($ dir = dirname ($ options ['file ' ]))) {
@@ -228,6 +238,7 @@ class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}:
228
238
229
239
$ this ->targetDirRegex = null ;
230
240
$ this ->inlinedRequires = array ();
241
+ $ this ->circularReferences = array ();
231
242
232
243
$ unusedEnvs = array ();
233
244
foreach ($ this ->container ->getEnvCounters () as $ env => $ use ) {
@@ -272,18 +283,18 @@ private function addServiceLocalTempVariables($cId, Definition $definition, arra
272
283
array_unshift ($ inlinedDefinitions , $ definition );
273
284
274
285
$ collectLineage = $ this ->inlineRequires && !$ this ->isHotPath ($ definition );
275
- $ isNonLazyShared = !$ this ->getProxyDumper ()->isProxyCandidate ($ definition ) && $ definition ->isShared ();
286
+ $ isNonLazyShared = isset ( $ this -> circularReferences [ $ cId ]) && !$ this ->getProxyDumper ()->isProxyCandidate ($ definition ) && $ definition ->isShared ();
276
287
$ lineage = $ calls = $ behavior = array ();
277
288
foreach ($ inlinedDefinitions as $ iDefinition ) {
278
289
if ($ collectLineage && !$ iDefinition ->isDeprecated () && $ class = is_array ($ factory = $ iDefinition ->getFactory ()) && is_string ($ factory [0 ]) ? $ factory [0 ] : $ iDefinition ->getClass ()) {
279
290
$ this ->collectLineage ($ class , $ lineage );
280
291
}
281
- $ this ->getServiceCallsFromArguments ($ iDefinition ->getArguments (), $ calls , $ behavior , $ isNonLazyShared );
292
+ $ this ->getServiceCallsFromArguments ($ iDefinition ->getArguments (), $ calls , $ behavior , $ isNonLazyShared, $ cId );
282
293
$ isPreInstantiation = $ isNonLazyShared && $ iDefinition !== $ definition && !$ this ->hasReference ($ cId , $ iDefinition ->getMethodCalls (), true ) && !$ this ->hasReference ($ cId , $ iDefinition ->getProperties (), true );
283
- $ this ->getServiceCallsFromArguments ($ iDefinition ->getMethodCalls (), $ calls , $ behavior , $ isPreInstantiation );
284
- $ this ->getServiceCallsFromArguments ($ iDefinition ->getProperties (), $ calls , $ behavior , $ isPreInstantiation );
285
- $ this ->getServiceCallsFromArguments (array ($ iDefinition ->getConfigurator ()), $ calls , $ behavior , $ isPreInstantiation );
286
- $ this ->getServiceCallsFromArguments (array ($ iDefinition ->getFactory ()), $ calls , $ behavior , $ isNonLazyShared );
294
+ $ this ->getServiceCallsFromArguments ($ iDefinition ->getMethodCalls (), $ calls , $ behavior , $ isPreInstantiation, $ cId );
295
+ $ this ->getServiceCallsFromArguments ($ iDefinition ->getProperties (), $ calls , $ behavior , $ isPreInstantiation, $ cId );
296
+ $ this ->getServiceCallsFromArguments (array ($ iDefinition ->getConfigurator ()), $ calls , $ behavior , $ isPreInstantiation, $ cId );
297
+ $ this ->getServiceCallsFromArguments (array ($ iDefinition ->getFactory ()), $ calls , $ behavior , $ isNonLazyShared, $ cId );
287
298
}
288
299
289
300
$ code = '' ;
@@ -336,6 +347,33 @@ private function addServiceLocalTempVariables($cId, Definition $definition, arra
336
347
return $ code ;
337
348
}
338
349
350
+ private function analyzeCircularReferences (array $ edges , &$ checkedNodes , &$ currentPath )
351
+ {
352
+ foreach ($ edges as $ edge ) {
353
+ $ node = $ edge ->getDestNode ();
354
+ $ id = $ node ->getId ();
355
+
356
+ if (isset ($ checkedNodes [$ id ])) {
357
+ continue ;
358
+ }
359
+
360
+ if ($ node ->getValue () && ($ edge ->isLazy () || $ edge ->isWeak ())) {
361
+ // no-op
362
+ } elseif (isset ($ currentPath [$ id ])) {
363
+ foreach (array_reverse ($ currentPath ) as $ parentId ) {
364
+ $ this ->circularReferences [$ parentId ][$ id ] = $ id ;
365
+ $ id = $ parentId ;
366
+ }
367
+ } else {
368
+ $ currentPath [$ id ] = $ id ;
369
+ $ this ->analyzeCircularReferences ($ node ->getOutEdges (), $ checkedNodes , $ currentPath );
370
+ }
371
+
372
+ $ checkedNodes [$ id ] = true ;
373
+ array_pop ($ currentPath );
374
+ }
375
+ }
376
+
339
377
private function collectLineage ($ class , array &$ lineage )
340
378
{
341
379
if (isset ($ lineage [$ class ])) {
@@ -562,15 +600,15 @@ private function isTrivialInstance(Definition $definition)
562
600
}
563
601
564
602
foreach ($ definition ->getArguments () as $ arg ) {
565
- if (!$ arg || ( $ arg instanceof Reference && ' service_container ' === ( string ) $ arg ) ) {
603
+ if (!$ arg ) {
566
604
continue ;
567
605
}
568
606
if (is_array ($ arg ) && 3 >= count ($ arg )) {
569
607
foreach ($ arg as $ k => $ v ) {
570
608
if ($ this ->dumpValue ($ k ) !== $ this ->dumpValue ($ k , false )) {
571
609
return false ;
572
610
}
573
- if (!$ v || ( $ v instanceof Reference && ' service_container ' === ( string ) $ v ) ) {
611
+ if (!$ v ) {
574
612
continue ;
575
613
}
576
614
if ($ v instanceof Reference && $ this ->container ->has ($ id = (string ) $ v ) && $ this ->container ->findDefinition ($ id )->isSynthetic ()) {
@@ -1501,16 +1539,16 @@ private function getServiceConditionals($value)
1501
1539
/**
1502
1540
* Builds service calls from arguments.
1503
1541
*/
1504
- private function getServiceCallsFromArguments (array $ arguments , array &$ calls , array &$ behavior , $ isPreInstantiation )
1542
+ private function getServiceCallsFromArguments (array $ arguments , array &$ calls , array &$ behavior , $ isPreInstantiation, $ callerId )
1505
1543
{
1506
1544
foreach ($ arguments as $ argument ) {
1507
1545
if (is_array ($ argument )) {
1508
- $ this ->getServiceCallsFromArguments ($ argument , $ calls , $ behavior , $ isPreInstantiation );
1546
+ $ this ->getServiceCallsFromArguments ($ argument , $ calls , $ behavior , $ isPreInstantiation, $ callerId );
1509
1547
} elseif ($ argument instanceof Reference) {
1510
1548
$ id = (string ) $ argument ;
1511
1549
1512
1550
if (!isset ($ calls [$ id ])) {
1513
- $ calls [$ id ] = (int ) ($ isPreInstantiation && $ this -> container -> has ( $ id ) && ! $ this ->container -> findDefinition ( $ id )-> isSynthetic ( ));
1551
+ $ calls [$ id ] = (int ) ($ isPreInstantiation && isset ( $ this ->circularReferences [ $ callerId ][ $ id ] ));
1514
1552
}
1515
1553
if (!isset ($ behavior [$ id ])) {
1516
1554
$ behavior [$ id ] = $ argument ->getInvalidBehavior ();
@@ -1582,6 +1620,10 @@ private function getDefinitionsFromArguments(array $arguments)
1582
1620
*/
1583
1621
private function hasReference ($ id , array $ arguments , $ deep = false , array &$ visited = array ())
1584
1622
{
1623
+ if (!isset ($ this ->circularReferences [$ id ])) {
1624
+ return false ;
1625
+ }
1626
+
1585
1627
foreach ($ arguments as $ argument ) {
1586
1628
if (is_array ($ argument )) {
1587
1629
if ($ this ->hasReference ($ id , $ argument , $ deep , $ visited )) {
@@ -1595,7 +1637,7 @@ private function hasReference($id, array $arguments, $deep = false, array &$visi
1595
1637
return true ;
1596
1638
}
1597
1639
1598
- if (!$ deep || isset ($ visited [$ argumentId ]) || ' service_container ' === $ argumentId ) {
1640
+ if (!$ deep || isset ($ visited [$ argumentId ]) || ! isset ( $ this -> circularReferences [ $ id ][ $ argumentId]) ) {
1599
1641
continue ;
1600
1642
}
1601
1643
0 commit comments