From 53c93168e8a82f8af2eaba0aabe8f0aa2cd977d1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 6 Feb 2019 19:04:48 +0100 Subject: [PATCH] [DI] Fix dumping Doctrine-like service graphs (bis) --- .../Compiler/AnalyzeServiceReferencesPass.php | 2 +- .../DependencyInjection/Dumper/PhpDumper.php | 34 +++++++++++++++---- .../php/services_almost_circular_private.php | 10 ++++-- .../php/services_almost_circular_public.php | 16 +++++++-- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php index fc4047f902e13..8070920ff7ffe 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php @@ -122,7 +122,7 @@ protected function processValue($value, $isRoot = false) $this->lazy = false; $byConstructor = $this->byConstructor; - $this->byConstructor = true; + $this->byConstructor = $isRoot || $byConstructor; $this->processValue($value->getFactory()); $this->processValue($value->getArguments()); $this->byConstructor = $byConstructor; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 73c868f1e54c9..fedc270ce356b 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -157,7 +157,8 @@ public function dump(array $options = []) (new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container); $checkedNodes = []; $this->circularReferences = []; - foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { + $graph = $this->container->getCompiler()->getServiceReferenceGraph(); + foreach ($graph->getNodes() as $id => $node) { if (!$node->getValue() instanceof Definition) { continue; } @@ -165,7 +166,18 @@ public function dump(array $options = []) $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes); } } - $this->container->getCompiler()->getServiceReferenceGraph()->clear(); + + foreach ($this->circularReferences as $parentId => $targetIds) { + foreach ($graph->getNode($parentId)->getOutEdges() as $edge) { + if (!isset($targetIds[$id = $edge->getDestNode()->getId()])) { + continue; + } + if ($this->circularReferences[$parentId][$id] = $edge->isReferencedByConstructor()) { + unset($targetIds[$id]); + } + } + } + $graph->clear(); $checkedNodes = []; $this->docStar = $options['debug'] ? '*' : ''; @@ -661,7 +673,6 @@ private function addService($id, Definition $definition, &$file = null) $autowired = $definition->isAutowired() ? ' autowired' : ''; if ($definition->isLazy()) { - unset($this->circularReferences[$id]); $lazyInitialization = '$lazyLoad = true'; } else { $lazyInitialization = ''; @@ -736,12 +747,12 @@ private function addInlineVariables($id, Definition $definition, array $argument private function addInlineReference($id, Definition $definition, $targetId, $forConstructor) { - list($callCount, $behavior) = $this->serviceCalls[$targetId]; - while ($this->container->hasAlias($targetId)) { $targetId = (string) $this->container->getAlias($targetId); } + list($callCount, $behavior) = $this->serviceCalls[$targetId]; + if ($id === $targetId) { return $this->addInlineService($id, $definition, $definition); } @@ -751,7 +762,7 @@ private function addInlineReference($id, Definition $definition, $targetId, $for } $hasSelfRef = isset($this->circularReferences[$id][$targetId]); - $forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]); + $forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]) && !empty($this->circularReferences[$id][$targetId]); $code = $hasSelfRef && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : ''; if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) { @@ -1562,6 +1573,10 @@ private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage } elseif ($argument instanceof Reference) { $id = $this->container->normalizeId($argument); + while ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } + if (!isset($calls[$id])) { $calls[$id] = [0, $argument->getInvalidBehavior()]; } else { @@ -1716,7 +1731,12 @@ private function dumpValue($value, $interpolate = true) } elseif ($value instanceof Variable) { return '$'.$value; } elseif ($value instanceof Reference) { - $id = $this->container->normalizeId($value); + $id = $this->container->normalizeId($id); + + while ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } + if (null !== $this->referenceVariables && isset($this->referenceVariables[$id])) { return $this->dumpValue($this->referenceVariables[$id], $interpolate); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php index 55ddf616c1d9d..25c0f205dd004 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php @@ -370,9 +370,12 @@ protected function getManager2Service() protected function getManager3Service($lazyLoad = true) { $a = new \stdClass(); + + $this->services['manager3'] = $instance = new \stdClass($a); + $a->listener = [0 => ${($_ = isset($this->services['listener3']) ? $this->services['listener3'] : $this->getListener3Service()) && false ?: '_'}]; - return $this->services['manager3'] = new \stdClass($a); + return $instance; } /** @@ -489,9 +492,12 @@ protected function getLevel6Service() protected function getManager4Service($lazyLoad = true) { $a = new \stdClass(); + + $this->services['manager4'] = $instance = new \stdClass($a); + $a->listener = [0 => ${($_ = isset($this->services['listener4']) ? $this->services['listener4'] : $this->getListener4Service()) && false ?: '_'}]; - return $this->services['manager4'] = new \stdClass($a); + return $instance; } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php index 09ca4591bf80c..f41f831b3c5d8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php @@ -497,7 +497,13 @@ protected function getManager2Service() */ protected function getManager3Service($lazyLoad = true) { - return $this->services['manager3'] = new \stdClass(${($_ = isset($this->services['connection3']) ? $this->services['connection3'] : $this->getConnection3Service()) && false ?: '_'}); + $a = ${($_ = isset($this->services['connection3']) ? $this->services['connection3'] : $this->getConnection3Service()) && false ?: '_'}; + + if (isset($this->services['manager3'])) { + return $this->services['manager3']; + } + + return $this->services['manager3'] = new \stdClass($a); } /** @@ -613,7 +619,13 @@ protected function getLevel6Service() */ protected function getManager4Service($lazyLoad = true) { - return $this->services['manager4'] = new \stdClass(${($_ = isset($this->services['connection4']) ? $this->services['connection4'] : $this->getConnection4Service()) && false ?: '_'}); + $a = ${($_ = isset($this->services['connection4']) ? $this->services['connection4'] : $this->getConnection4Service()) && false ?: '_'}; + + if (isset($this->services['manager4'])) { + return $this->services['manager4']; + } + + return $this->services['manager4'] = new \stdClass($a); } /**