Skip to content

Commit 1f77f2a

Browse files
Merge branch '3.4' into 4.1
* 3.4: [DI] fix combinatorial explosion when analyzing the service graph [Debug] workaround opcache bug mutating "$this" !?!
2 parents 9508a26 + 9199e2e commit 1f77f2a

File tree

2 files changed

+57
-21
lines changed

2 files changed

+57
-21
lines changed

Compiler/InlineServiceDefinitionsPass.php

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

117117
$ids = array();
118+
$isReferencedByConstructor = false;
118119
foreach ($graph->getNode($id)->getInEdges() as $edge) {
120+
$isReferencedByConstructor = $isReferencedByConstructor || $edge->isReferencedByConstructor();
119121
if ($edge->isWeak()) {
120122
return false;
121123
}
122124
$ids[] = $edge->getSourceNode()->getId();
123125
}
124126

127+
if (!$ids) {
128+
return true;
129+
}
130+
125131
if (\count(array_unique($ids)) > 1) {
126132
return false;
127133
}
@@ -130,6 +136,10 @@ private function isInlineableDefinition($id, Definition $definition, ServiceRefe
130136
return false;
131137
}
132138

133-
return !$ids || $this->container->getDefinition($ids[0])->isShared();
139+
if ($isReferencedByConstructor && $this->container->getDefinition($ids[0])->isLazy() && ($definition->getProperties() || $definition->getMethodCalls() || $definition->getConfigurator())) {
140+
return false;
141+
}
142+
143+
return $this->container->getDefinition($ids[0])->isShared();
134144
}
135145
}

Dumper/PhpDumper.php

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

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

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

@@ -307,12 +308,12 @@ private function getProxyDumper(): ProxyDumper
307308
return $this->proxyDumper;
308309
}
309310

310-
private function analyzeCircularReferences(array $edges, &$currentPath, $sourceId, $byConstructor)
311+
private function analyzeCircularReferences($sourceId, array $edges, &$checkedNodes, &$currentPath = array())
311312
{
313+
$checkedNodes[$sourceId] = true;
314+
$currentPath[$sourceId] = $sourceId;
315+
312316
foreach ($edges as $edge) {
313-
if ($byConstructor && !$edge->isReferencedByConstructor()) {
314-
continue;
315-
}
316317
$node = $edge->getDestNode();
317318
$id = $node->getId();
318319

@@ -321,20 +322,42 @@ private function analyzeCircularReferences(array $edges, &$currentPath, $sourceI
321322
} elseif (isset($currentPath[$id])) {
322323
$currentId = $id;
323324
foreach (array_reverse($currentPath) as $parentId) {
324-
if (!isset($this->circularReferences[$parentId][$currentId])) {
325-
$this->circularReferences[$parentId][$currentId] = $byConstructor;
325+
$this->circularReferences[$parentId][$currentId] = $currentId;
326+
if ($parentId === $id) {
327+
break;
326328
}
329+
$currentId = $parentId;
330+
}
331+
} elseif (!isset($checkedNodes[$id])) {
332+
$this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath);
333+
} elseif (isset($this->circularReferences[$id])) {
334+
$this->connectCircularReferences($id, $currentPath);
335+
}
336+
}
337+
unset($currentPath[$sourceId]);
338+
}
339+
340+
private function connectCircularReferences($sourceId, &$currentPath, &$subPath = array())
341+
{
342+
$subPath[$sourceId] = $sourceId;
343+
$currentPath[$sourceId] = $sourceId;
344+
345+
foreach ($this->circularReferences[$sourceId] as $id) {
346+
if (isset($currentPath[$id])) {
347+
$currentId = $id;
348+
foreach (array_reverse($currentPath) as $parentId) {
349+
$this->circularReferences[$parentId][$currentId] = $currentId;
327350
if ($parentId === $id) {
328351
break;
329352
}
330353
$currentId = $parentId;
331354
}
332-
} else {
333-
$currentPath[$id] = $id;
334-
$this->analyzeCircularReferences($node->getOutEdges(), $currentPath, $id, $byConstructor);
335-
unset($currentPath[$id]);
355+
} elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) {
356+
$this->connectCircularReferences($id, $currentPath, $subPath);
336357
}
337358
}
359+
unset($currentPath[$sourceId]);
360+
unset($subPath[$sourceId]);
338361
}
339362

340363
private function collectLineage($class, array &$lineage)
@@ -539,8 +562,11 @@ private function addServiceConfigurator(Definition $definition, string $variable
539562

540563
if (\is_array($callable)) {
541564
if ($callable[0] instanceof Reference
542-
|| ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
543-
return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
565+
|| ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))
566+
) {
567+
$callable[0] = $this->dumpValue($callable[0]);
568+
569+
return sprintf(' '.('$' === $callable[0][0] ? '%s' : '(%s)')."->%s(\$%s);\n", $callable[0], $callable[1], $variableName);
544570
}
545571

546572
$class = $this->dumpValue($callable[0]);
@@ -691,7 +717,7 @@ private function addInlineReference(string $id, Definition $definition, string $
691717

692718
$hasSelfRef = isset($this->circularReferences[$id][$targetId]);
693719
$forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]);
694-
$code = $hasSelfRef && $this->circularReferences[$id][$targetId] && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : '';
720+
$code = $hasSelfRef && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : '';
695721

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

0 commit comments

Comments
 (0)