8000 [DI] Fix missing DefinitionDecorator resolution in ContainerBuilder::… · symfony/symfony@1d4d6e7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1d4d6e7

Browse files
[DI] Fix missing DefinitionDecorator resolution in ContainerBuilder::createService()
1 parent 05c3e86 commit 1d4d6e7

File tree

4 files changed

+132
-89
lines changed

4 files changed

+132
-89
lines changed

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

Lines changed: 1 addition & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -124,95 +124,7 @@ private function doResolveDefinition(ContainerBuilder $container, DefinitionDeco
124124
}
125125

126126
$this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent));
127-
$def = new Definition();
128-
129-
// merge in parent definition
130-
// purposely ignored attributes: abstract, tags
131-
$def->setClass($parentDef->getClass());
132-
$def->setArguments($parentDef->getArguments());
133-
$def->setMethodCalls($parentDef->getMethodCalls());
134-
$def->setProperties($parentDef->getProperties());
135-
$def->setAutowiringTypes($parentDef->getAutowiringTypes());
136-
if ($parentDef->isDeprecated()) {
137-
$def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%'));
138-
}
139-
$def->setFactory($parentDef->getFactory());
140-
$def->setConfigurator($parentDef->getConfigurator());
141-
$def->setFile($parentDef->getFile());
142-
$def->setPublic($parentDef->isPublic());
143-
$def->setLazy($parentDef->isLazy());
144-
$def->setAutowired($parentDef->isAutowired());
145-
146-
// overwrite with values specified in the decorator
147-
$changes = $definition->getChanges();
148-
if (isset($changes['class'])) {
149-
$def->setClass($definition->getClass());
150-
}
151-
if (isset($changes['factory'])) {
152-
$def->setFactory($definition->getFactory());
153-
}
154-
if (isset($changes['configurator'])) {
155-
$def->setConfigurator($definition->getConfigurator());
156-
}
157-
if (isset($changes['file'])) {
158-
$def->setFile($definition->getFile());
159-
}
160-
if (isset($changes['public'])) {
161-
$def->setPublic($definition->isPublic());
162-
}
163-
if (isset($changes['lazy'])) {
164-
$def->setLazy($definition->isLazy());
165-
}
166-
if (isset($changes['deprecated'])) {
167-
$def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%'));
168-
}
169-
if (isset($changes['autowire'])) {
170-
$def->setAutowired($definition->isAutowired());
171-
}
172-
if (isset($changes['decorated_service'])) {
173-
$decoratedService = $definition->getDecoratedService();
174-
if (null === $decoratedService) {
175-
$def->setDecoratedService($decoratedService);
176-
} else {
177-
$def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]);
178-
}
179-
}
180-
181-
// merge arguments
182-
foreach ($definition->getArguments() as $k => $v) {
183-
if (is_numeric($k)) {
184-
$def->addArgument($v);
185-
continue;
186-
}
187-
188-
if (0 !== strpos($k, 'index_')) {
189-
throw new RuntimeException(sprintf('Invalid argument key "%s" found.', $k));
190-
}
191-
192-
$index = (int) substr($k, strlen('index_'));
193-
$def->replaceArgument($index, $v);
194-
}
195-
196-
// merge properties
197-
foreach ($definition->getProperties() as $k => $v) {
198-
$def->setProperty($k, $v);
199-
}
200-
201-
// append method calls
202-
if (count($calls = $definition->getMethodCalls()) > 0) {
203-
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
204-
}
205-
206-
// merge autowiring types
207-
foreach ($definition->getAutowiringTypes() as $autowiringType) {
208-
$def->addAutowiringType($autowiringType);
209-
}
210-
211-
// these attributes are always taken from the child
212-
$def->setAbstract($definition->isAbstract());
213-
$def->setShared($definition->isShared());
214-
$def->setTags($definition->getTags());
215127

216-
return $def;
128+
return $definition->resolveChanges($parentDef);
217129
}
218130
}

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,10 @@ public function findDefinition($id)
835835
*/
836836
private function createService(Definition $definition, $id, $tryProxy = true)
837837
{
838+
if ($definition instanceof DefinitionDecorator) {
839+
$definition = $this->resolveDefinitionDecorator($definition, $id);
840+
}
841+
838842
if ($definition->isSynthetic()) {
839843
throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id));
840844
}
@@ -1120,6 +1124,19 @@ private function callMethod($service, $call)
11201124
call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1]))));
11211125
}
11221126

1127+
private function resolveDefinitionDecorator(DefinitionDecorator $definition, $id)
1128+
{
1129+
if (!$this->has($parent = $definition->getParent())) {
1130+
throw new RuntimeException(sprintf('Service "%s": Parent definition "%s" does not exist.', $id, $parent));
1131+
}
1132+
$parentDef = $this->findDefinition($parent);
1133+
if ($parentDef instanceof DefinitionDecorator) {
1134+
$parentDef = $this->resolveDefinitionDecorator($parentDef, $parent);
1135+
}
1136+
1137+
return $definition->resolveChanges($parentDef);
1138+
}
1139+
11231140
/**
11241141
* Shares a given service in the container.
11251142
*

src/Symfony/Component/DependencyInjection/DefinitionDecorator.php

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

1414
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1515
use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
16+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
1617

1718
/**
1819
* This definition decorates another definition.
@@ -196,4 +197,106 @@ public function replaceArgument($index, $value)
196197

197198
return $this;
198199
}
200+
201+
/**
202+
* Creates a new Definition by merging the current decorator with the given parent definition.
203+
*
204+
* @return Definition
205+
*/
206+
public function resolveChanges(Definition $parentDef)
207+
{
208+
if ($parentDef instanceof self) {
209+
throw new InvalidArgumentException('$parenfDef must be a resolved Definition.');
210+
}
211+
$def = new Definition();
212+
213+
// merge in parent definition
214+
// purposely ignored attributes: abstract, tags
215+
$def->setClass($parentDef->getClass());
216+
$def->setArguments($parentDef->getArguments());
217+
$def->setMethodCalls($parentDef->getMethodCalls());
218+
$def->setProperties($parentDef->getProperties());
219+
$def->setAutowiringTypes($parentDef->getAutowiringTypes());
220+
if ($parentDef->isDeprecated()) {
221+
$def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%'));
222+
}
223+
$def->setFactory($parentDef->getFactory());
224+
$def->setConfigurator($parentDef->getConfigurator());
225+
$def->setFile($parentDef->getFile());
226+
$def->setPublic($parentDef->isPublic());
227+
$def->setLazy($parentDef->isLazy());
228+
$def->setAutowired($parentDef->isAutowired());
229+
230+
// overwrite with values specified in the decorator
231+
$changes = $this->getChanges();
232+
if (isset($changes['class'])) {
233+
$def->setClass($this->getClass());
234+
}
235+
if (isset($changes['factory'])) {
236+
$def->setFactory($this->getFactory());
237+
}
238+
if (isset($changes['configurator'])) {
239+
$def->setConfigurator($this->getConfigurator());
240+
}
241+
if (isset($changes['file'])) {
242+
$def->setFile($this->getFile());
243+
}
244+
if (isset($changes['public'])) {
245+
$def->setPublic($this->isPublic());
246+
}
247+
if (isset($changes['lazy'])) {
248+
$def->setLazy($this->isLazy());
249+
}
250+
if (isset($changes['deprecated'])) {
251+
$def->setDeprecated($this->isDeprecated(), $this->getDeprecationMessage('%service_id%'));
252+
}
253+
if (isset($changes['autowire'])) {
254+
$def->setAutowired($this->isAutowired());
255+
}
256+
if (isset($changes['decorated_service'])) {
257+
$decoratedService = $this->getDecoratedService();
258+
if (null === $decoratedService) {
259+
$def->setDecoratedService($decoratedService);
260+
} else {
261+
$def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]);
262+
}
263+
}
264+
265+
// merge arguments
266+
foreach ($this->getArguments() as $k => $v) {
267+
if (is_numeric($k)) {
268+
$def->addArgument($v);
269+
continue;
270+
}
271+
272+
if (0 !== strpos($k, 'index_')) {
< 1241 /code>273+
throw new RuntimeException(sprintf('Invalid argument key "%s" found.', $k));
274+
}
275+
276+
$index = (int) substr($k, strlen('index_'));
277+
$def->replaceArgument($index, $v);
278+
}
279+
280+
// merge properties
281+
foreach ($this->getProperties() as $k => $v) {
282+
$def->setProperty($k, $v);
283+
}
284+
285+
// append method calls
286+
if (count($calls = $this->getMethodCalls()) > 0) {
287+
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
288+
}
289+
290+
// merge autowiring types
291+
foreach ($this->getAutowiringTypes() as $autowiringType) {
292+
$def->addAutowiringType($autowiringType);
293+
}
294+
295+
// these attributes are always taken from the child
296+
$def->setAbstract($this->isAbstract());
297+
$def->setShared($this->isShared());
298+
$def->setTags($this->getTags());
299+
300+
return $def;
301+
}
199302
}

src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\Component\DependencyInjection\ContainerBuilder;
2121
use Symfony\Component\DependencyInjection\ContainerInterface;
2222
use Symfony\Component\DependencyInjection\Definition;
23+
use Symfony\Component\DependencyInjection\DefinitionDecorator;
2324
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
2425
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
2526
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
@@ -412,6 +413,16 @@ public function testResolveServices()
412413
$this->assertEquals($builder->get('foo'), $builder->resolveServices(new Expression('service("foo")')), '->resolveServices() resolves expressions');
413414
}
414415

416+
public function testResolveServicesWithDecoratedDefinition()
417+
{
418+
$builder = new ContainerBuilder();
419+
$builder->setDefinition('grandpa', new Definition('stdClass'));
420+
$builder->setDefinition('parent', new DefinitionDecorator('grandpa'));
421+
$builder->setDefinition('foo', new DefinitionDecorator('parent'));
422+
423+
$this->assertInstanceOf('stdClass', $builder->get('foo'));
424+
}
425+
415426
public function testMerge()
416427
{
417428
$container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo')));

0 commit comments

Comments
 (0)
0