8000 [DI] fix combinatorial explosion when analyzing the service graph · symfony/symfony@0d0be12 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0d0be12

Browse files
[DI] fix combinatorial explosion when analyzing the service graph
1 parent acce087 commit 0d0be12

File tree

2 files changed

+57
-21
lines changed

2 files changed

+57
-21
lines changed

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,19 @@ private function isInlineableDefinition($id, Definition $definition, ServiceRefe
127127
}
128128

129129
$ids = array();
130+
$isReferencedByConstructor = false;
130131
foreach ($graph->getNode($id)->getInEdges() as $edge) {
132+
$isReferencedByConstructor = $isReferencedByConstructor || $edge->isReferencedByConstructor();
131133
if ($edge->isWeak()) {
132134
return false;
133135
}
134136
$ids[] = $edge->getSourceNode()->getId();
135137
}
136138

139+
if (!$ids) {
140+
return true;
141+
}
142+
137143
if (\count(array_unique($ids)) > 1) {
138144
return false;
139145
}
@@ -142,6 +148,10 @@ private function isInlineableDefinition($id, Definition $definition, ServiceRefe
142148
return false;
143149
}
144150

145-
return !$ids || $this->container->getDefinition($ids[0])->isShared();
151+
if ($isReferencedByConstructor && $this->container->getDefinition($ids[0])->isLazy() && ($definition->getProperties() || $definition->getMethodCalls() || $definition->getConfigurator())) {
152+
return false;
153+
}
154+
155+
return $this->container->getDefinition($ids[0])->isShared();
146156
}
147157
}

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

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -155,17 +155,18 @@ public function dump(array $options = array())
155155
}
156156

157157
(new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container);
158+
$checkedNodes = array();
158159
$this->circularReferences = array( 8000 );
159-
foreach (array(true, false) as $byConstructor) {
160-
foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) {
161-
if (!$node->getValue() instanceof Definition) {
162-
continue;
163-
}
164-
$currentPath = array($id => true);
165-
$this->analyzeCircularReferences($node->getOutEdges(), $currentPath, $id, $byConstructor);
160+
foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) {
161+
if (!$node->getValue() instanceof Definition) {
162+
continue;
163+
}
164+
if (!isset($checkedNodes[$id])) {
165+
$this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes);
166166
}
167167
}
168168
$this->container->getCompiler()->getServiceReferenceGraph()->clear();
169+
$checkedNodes = array();
169170

170171
$this->docStar = $options['debug'] ? '*' : '';
171172

@@ -301,12 +302,12 @@ private function getProxyDumper()
301302
return $this->proxyDumper;
302303
}
303304

304-
private function analyzeCircularReferences(array $edges, &$currentPath, $sourceId, $byConstructor)
305+
private function analyzeCircularReferences($sourceId, array $edges, &$checkedNodes, &$currentPath = array())
305306
{
307+
$checkedNodes[$sourceId] = true;
308+
$currentPath[$sourceId] = $sourceId;
309+
306310
foreach ($edges as $edge) {
307-
if ($byConstructor && !$edge->isReferencedByConstructor()) {
308-
continue;
309-
}
310311
$node = $edge->getDestNode();
311312
$id = $node->getId();
312313

@@ -315,20 +316,42 @@ private function analyzeCircularReferences(array $edges, &$currentPath, $sourceI
315316
} elseif (isset($currentPath[$id])) {
316317
$currentId = $id;
317318
foreach (array_reverse($currentPath) as $parentId) {
318-
if (!isset($this->circularReferences[$parentId][$currentId])) {
319-
$this->circularReferences[$parentId][$currentId] = $byConstructor;
319+
$this->circularReferences[$parentId][$currentId] = $currentId;
320+
if ($parentId === $id) {
321+
break;
320322
}
323+
$currentId = $parentId;
324+
}
325+
} elseif (!isset($checkedNodes[$id])) {
326+
$this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath);
327+
} elseif (isset($this->circularReferences[$id])) {
328+
$this->connectCircularReferences($id, $currentPath);
329+
}
330+
}
331+
unset($currentPath[$sourceId]);
332+
}
333+
334+
private function connectCircularReferences($sourceId, &$currentPath, &$subPath = array())
335+
{
336+
$subPath[$sourceId] = $sourceId;
337+
$currentPath[$sourceId] = $sourceId;
338+
339+
foreach ($this->circularReferences[$sourceId] as $id) {
340+
if (isset($currentPath[$id])) {
341+
$currentId = $id;
342+
foreach (array_reverse($currentPath) as $parentId) {
343+
$this->circularReferences[$parentId][$currentId] = $currentId;
321344
if ($parentId === $id) {
322345
break;
323346
}
324347
$currentId = $parentId;
325348
}
326-
} else {
327-
$currentPath[$id] = $id;
328-
$this->analyzeCircularReferences($node->getOutEdges(), $currentPath, $id, $byConstructor);
329-
unset($currentPath[$id]);
349+
} elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) {
350+
$this->connectCircularReferences($id, $currentPath, $subPath);
330351
}
331352
}
353+
unset($currentPath[$sourceId]);
354+
unset($subPath[$sourceId]);
332355
}
333356

334357
private function collectLineage($class, array &$lineage)
@@ -569,8 +592,11 @@ private function addServiceConfigurator(Definition $definition, $variableName =
569592

570593
if (\is_array($callable)) {
571594
if ($callable[0] instanceof Reference
572-
|| ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
573-
return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
595+
|| ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))
596+
) {
597+
$callable[0] = $this->dumpValue($callable[0]);
598+
599+
return sprintf(' '.('$' === $callable[0][0] ? '%s' : '(%s)')."->%s(\$%s);\n", $callable[0], $callable[1], $variableName);
574600
}
575601

576602
$class = $this->dumpValue($callable[0]);
@@ -724,7 +750,7 @@ private function addInlineReference($id, Definition $definition, $targetId, $for
724750

725751
$hasSelfRef = isset($this->circularReferences[$id][$targetId]);
726752
$forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]);
727-
$code = $hasSelfRef && $this->circularReferences[$id][$targetId] && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : '';
753+
$code = $hasSelfRef && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : '';
728754

729755
if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) {
730756
return $code;

0 commit comments

Comments
 (0)
0