Skip to content

Commit dd60aa9

Browse files
[DI] Fix dumping Doctrine-like service graphs (bis)
1 parent 41ffa3e commit dd60aa9

File tree

4 files changed

+67
-38
lines changed

4 files changed

+67
-38
lines changed

src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ protected function processValue($value, $isRoot = false)
136136
$this->lazy = false;
137137

138138
$byConstructor = $this->byConstructor;
139-
$this->byConstructor = true;
139+
$this->byConstructor = $isRoot || $byConstructor;
140140
$this->processValue($value->getFactory());
141141
$this->processValue($value->getArguments());
142142
$this->byConstructor = $byConstructor;

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ public function dump(array $options = [])
187187
}
188188
$this->container->getCompiler()->getServiceReferenceGraph()->clear();
189189
$checkedNodes = [];
190+
$this->singleUsePrivateIds = array_diff_key($this->singleUsePrivateIds, $this->circularReferences);
190191

191192
$this->docStar = $options['debug'] ? '*' : '';
192193

@@ -338,10 +339,10 @@ private function getProxyDumper(): ProxyDumper
338339
return $this->proxyDumper;
339340
}
340341

341-
private function analyzeCircularReferences($sourceId, array $edges, &$checkedNodes, &$currentPath = [])
342+
private function analyzeCircularReferences($sourceId, array $edges, &$checkedNodes, &$currentPath = [], $byConstructor = true)
342343
{
343344
$checkedNodes[$sourceId] = true;
344-
$currentPath[$sourceId] = $sourceId;
345+
$currentPath[$sourceId] = $byConstructor;
345346

346347
foreach ($edges as $edge) {
347348
$node = $edge->getDestNode();
@@ -350,44 +351,46 @@ private function analyzeCircularReferences($sourceId, array $edges, &$checkedNod
350351
if (!$node->getValue() instanceof Definition || $sourceId === $id || $edge->isLazy() || $edge->isWeak()) {
351352
// no-op
352353
} elseif (isset($currentPath[$id])) {
353-
$currentId = $id;
354-
foreach (array_reverse($currentPath) as $parentId) {
355-
$this->circularReferences[$parentId][$currentId] = $currentId;
356-
if ($parentId === $id) {
357-
break;
358-
}
359-
$currentId = $parentId;
360-
}
354+
$this->addCircularReferences($id, $currentPath, $edge->isReferencedByConstructor());
361355
} elseif (!isset($checkedNodes[$id])) {
362-
$this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath);
356+
$this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath, $edge->isReferencedByConstructor());
363357
} elseif (isset($this->circularReferences[$id])) {
364-
$this->connectCircularReferences($id, $currentPath);
358+
$this->connectCircularReferences($id, $currentPath, $edge->isReferencedByConstructor());
365359
}
366360
}
367361
unset($currentPath[$sourceId]);
368362
}
369363

370-
private function connectCircularReferences($sourceId, &$currentPath, &$subPath = [])
364+
private function connectCircularReferences($sourceId, &$currentPath, $byConstructor, &$subPath = [])
371365
{
372-
$subPath[$sourceId] = $sourceId;
373-
$currentPath[$sourceId] = $sourceId;
366+
$currentPath[$sourceId] = $subPath[$sourceId] = $byConstructor;
374367

375-
foreach ($this->circularReferences[$sourceId] as $id) {
368+
foreach ($this->circularReferences[$sourceId] as $id => $byConstructor) {
376369
if (isset($currentPath[$id])) {
377-
$currentId = $id;
378-
foreach (array_reverse($currentPath) as $parentId) {
379-
$this->circularReferences[$parentId][$currentId] = $currentId;
380-
if ($parentId === $id) {
381-
break;
382-
}
383-
$currentId = $parentId;
384-
}
370+
$this->addCircularReferences($id, $currentPath, $byConstructor);
385371
} elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) {
386-
$this->connectCircularReferences($id, $currentPath, $subPath);
372+
$this->connectCircularReferences($id, $currentPath, $byConstructor, $subPath);
387373
}
388374
}
389-
unset($currentPath[$sourceId]);
390-
unset($subPath[$sourceId]);
375+
unset($currentPath[$sourceId], $subPath[$sourceId]);
376+
}
377+
378+
private function addCircularReferences($id, $currentPath, $byConstructor)
379+
{
380+
$currentPath[$id] = $byConstructor;
381+
$currentId = $id;
382+
383+
foreach (array_reverse($currentPath) as $parentId => $byConstructor) {
384+
if (empty($this->circularReferences[$parentId][$currentId])) {
385+
$this->circularReferences[$parentId][$currentId] = $currentPath[$currentId];
386+
}
387+
388+
if ($parentId === $id) {
389+
break;
390+
}
391+
392+
$currentId = $parentId;
393+
}
391394
}
392395

393396
private function collectLineage($class, array &$lineage)
@@ -655,7 +658,6 @@ private function addService(string $id, Definition $definition): array
655658
$autowired = $definition->isAutowired() ? ' autowired' : '';
656659

657660
if ($definition->isLazy()) {
658-
unset($this->circularReferences[$id]);
659661
$lazyInitialization = '$lazyLoad = true';
660662
} else {
661663
$lazyInitialization = '';
@@ -731,12 +733,12 @@ private function addInlineVariables(string $id, Definition $definition, array $a
731733

732734
private function addInlineReference(string $id, Definition $definition, string $targetId, bool $forConstructor): string
733735
{
734-
list($callCount, $behavior) = $this->serviceCalls[$targetId];
735-
736736
while ($this->container->hasAlias($targetId)) {
737737
$targetId = (string) $this->container->getAlias($targetId);
738738
}
739739

740+
list($callCount, $behavior) = $this->serviceCalls[$targetId];
741+
740742
if ($id === $targetId) {
741743
return $this->addInlineService($id, $definition, $definition);
742744
}
@@ -746,7 +748,7 @@ private function addInlineReference(string $id, Definition $definition, string $
746748
}
747749

748750
$hasSelfRef = isset($this->circularReferences[$id][$targetId]);
749-
$forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]);
751+
$forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]) && !empty($this->circularReferences[$id][$targetId]);
750752
$code = $hasSelfRef && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : '';
751753

752754
if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) {
@@ -1415,6 +1417,10 @@ private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage
14151417
} elseif ($argument instanceof Reference) {
14161418
$id = (string) $argument;
14171419

1420+
while ($this->container->hasAlias($id)) {
1421+
$id = (string) $this->container->getAlias($id);
1422+
}
1423+
14181424
if (!isset($calls[$id])) {
14191425
$calls[$id] = [0, $argument->getInvalidBehavior()];
14201426
} else {
@@ -1548,6 +1554,11 @@ private function dumpValue($value, bool $interpolate = true): string
15481554
return '$'.$value;
15491555
} elseif ($value instanceof Reference) {
15501556
$id = (string) $value;
1557+
1558+
while ($this->container->hasAlias($id)) {
1559+
$id = (string) $this->container->getAlias($id);
1560+
}
1561+
15511562
if (null !== $this->referenceVariables && isset($this->referenceVariables[$id])) {
15521563
return $this->dumpValue($this->referenceVariables[$id], $interpolate);
15531564
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ protected function getListener3Service()
276276
*/
277277
protected function getListener4Service()
278278
{
279-
$a = $this->getManager4Service();
279+
$a = ($this->privates['manager4'] ?? $this->getManager4Service());
280280

281281
if (isset($this->services['listener4'])) {
282282
return $this->services['listener4'];
@@ -345,9 +345,12 @@ protected function getManager2Service()
345345
protected function getManager3Service($lazyLoad = true)
346346
{
347347
$a = new \stdClass();
348+
349+
$this->services['manager3'] = $instance = new \stdClass($a);
350+
348351
$a->listener = [0 => ($this->services['listener3'] ?? $this->getListener3Service())];
349352

350-
return $this->services['manager3'] = new \stdClass($a);
353+
return $instance;
351354
}
352355

353356
/**
@@ -422,8 +425,11 @@ protected function getLevel5Service()
422425
protected function getManager4Service($lazyLoad = true)
423426
{
424427
$a = new \stdClass();
428+
429+
$this->privates['manager4'] = $instance = new \stdClass($a);
430+
425431
$a->listener = [0 => ($this->services['listener4'] ?? $this->getListener4Service())];
426432

427-
return new \stdClass($a);
433+
return $instance;
428434
}
429435
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ protected function getListener3Service()
404404
*/
405405
protected function getListener4Service()
406406
{
407-
$a = $this->getManager4Service();
407+
$a = ($this->privates['manager4'] ?? $this->getManager4Service());
408408

409409
if (isset($this->services['listener4'])) {
410410
return $this->services['listener4'];
@@ -472,7 +472,13 @@ protected function getManager2Service()
472472
*/
473473
protected function getManager3Service($lazyLoad = true)
474474
{
475-
return $this->services['manager3'] = new \stdClass(($this->services['connection3'] ?? $this->getConnection3Service()));
475+
$a = ($this->services['connection3'] ?? $this->getConnection3Service());
476+
477+
if (isset($this->services['manager3'])) {
478+
return $this->services['manager3'];
479+
}
480+
481+
return $this->services['manager3'] = new \stdClass($a);
476482
}
477483

478484
/**
@@ -546,6 +552,12 @@ protected function getLevel5Service()
546552
*/
547553
protected function getManager4Service($lazyLoad = true)
548554
{
549-
return new \stdClass(($this->services['connection4'] ?? $this->getConnection4Service()));
555+
$a = ($this->services['connection4'] ?? $this->getConnection4Service());
556+
557+
if (isset($this->privates['manager4'])) {
558+
return $this->privates['manager4'];
559+
}
560+
561+
return $this->privates['manager4'] = new \stdClass($a);
550562
}
551563
}

0 commit comments

Comments
 (0)