10000 [DI] Fix deep-inlining of non-shared refs · symfony/symfony@eb2a152 · GitHub
[go: up one dir, main page]

Skip to content

Commit eb2a152

Browse files
[DI] Fix deep-inlining of non-shared refs
1 parent c9f72e2 commit eb2a152

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