8000 bug #25312 [DI] Fix deep-inlining of non-shared refs (nicolas-grekas) · symfony/symfony@73ff764 · GitHub
[go: up one dir, main page]

Skip to content

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 73ff764

Browse files
committed
bug #25312 [DI] Fix deep-inlining of non-shared refs (nicolas-grekas)
This PR was merged into the 3.3 branch. Discussion ---------- [DI] Fix deep-inlining of non-shared refs | Q | A | ------------- | --- | Branch? | 3.3 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #25263 | License | MIT | Doc PR | - Non-shared definitions should deep-clone their inlined non-shared definitions. Commits ------- eb2a152 [DI] Fix deep-inlining of non-shared refs
2 parents c08602c + eb2a152 commit 73ff764

File tree

2 files changed

+90
-8
lines changed

2 files changed

+90
-8
lines changed

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

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1515
use Symfony\Component\DependencyInjection\Definition;
16+
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
1617
use Symfony\Component\DependencyInjection\Reference;
1718

1819
/**
@@ -23,6 +24,7 @@
2324
class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface
2425
{
2526
private $repeatedPass;
27+
private $cloningIds = array();
2628
private $inlinedServiceIds = array();
2729

2830
/**
@@ -54,18 +56,44 @@ protected function processValue($value, $isRoot = false)
5456
// Reference found in ArgumentInterface::getValues() are not inlineable
5557
return $value;
5658
}
57-
if ($value instanceof Reference && $this->container->hasDefinition($id = (string) $value)) {
58-
$definition = $this->container->getDefinition($id);
5959

60-
if ($this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) {
61-
$this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId));
62-
$this->inlinedServiceIds[$id][] = $this->currentId;
63-
64-
return $definition->isShared() ? $definition : clone $definition;
60+
if ($value instanceof Definition && $this->cloningIds) {
61+
if ($value->isShared()) {
62+
return $value;
6563
}
64+
$value = clone $value;
65+
}
66+
67+
if (!$value instanceof Reference || !$this->container->hasDefinition($id = (string) $value)) {
68+
return parent::processValue($value, $isRoot);
69+
}
70+
71+
$definition = $this->container->getDefinition($id);
72+
73+
if (!$this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) {
74+
return $value;
6675
}
6776

68-
return parent::processValue($value, $isRoot);
77+
$this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId));
78+
$this->inlinedServiceIds[$id][] = $this->currentId;
79+
80+
if ($definition->isShared()) {
81+
return $definition;
82+
}
83+
84+
if (isset($this->cloningIds[$id])) {
85+
$ids = array_keys($this->cloningIds);
86+
$ids[] = $id;
87+
88+
throw new ServiceCircularReferenceException($id, array_slice($ids, array_search($id, $ids)));
89+
}
90+
91+
$this->cloningIds[$id] = true;
92+
try {
93+
return $this->processValue($definition);
94+
} finally {
95+
unset($this->cloningIds[$id]);
96+
}
6997
}
7098

7199
/**

src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,60 @@ public function testProcessInlinesMixedServicesLoop()
111111
$this->assertEquals($container->getDefinition('foo')->getArgument(0), $container->getDefinition('bar'));
112112
}
113113

114+
/**
115+
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
116+
* @expectedExceptionMessage Circular reference detected for service "bar", path: "bar -> foo -> bar".
117+
*/
118+
public function testProcessThrowsOnNonSharedLoops()
119+
{
120+
$container = new ContainerBuilder();
121+
$container
122+
->register('foo')
123+
->addArgument(new Reference('bar'))
124+
->setShared(false)
125+
;
126+
$container
127+
->register('bar')
128+
->setShared(false)
129+
->addMethodCall('setFoo', array(new Reference('foo')))
130+
;
131+
132+
$this->process($container);
133+
}
134+
135+
public function testProcessNestedNonSharedServices()
136+
{
137+
$container = new ContainerBuilder();
138+
$container
139+
->register('foo')
140+
->addArgument(new Reference('bar1'))
141+
->addArgument(new Reference('bar2'))
142+
;
143+
$container
144+
->register('bar1')
145+
->setShared(false)
146+
->addArgument(new Reference('baz'))
147+
;
148+
$container
149+
->register('bar2')
150+
->setShared(false)
151+
->addArgument(new Reference('baz'))
152+
;
153+
$container
154+
->register('baz')
155+
->setShared(false)
156+
;
157+
158+
$this->process($container);
159+
160+
$baz1 = $container->getDefinition('foo')->getArgument(0)->getArgument(0);
161+
$baz2 = $container->getDefinition('foo')->getArgument(1)->getArgument(0);
162+
163+
$this->assertEquals($container->getDefinition('baz'), $baz1);
164+
$this->assertEquals($container->getDefinition('baz'), $baz2);
165+
$this->assertNotSame($baz1, $baz2);
166+
}
167+
114168
public function testProcessInlinesIfMultipleReferencesButAllFromTheSameDefinition()
115169
{
116170
$container = new ContainerBuilder();

0 commit comments

Comments
 (0)
0