Skip to content

Commit df261c2

Browse files
[DI] Fix missing DefinitionDecorator resolution in ContainerBuilder::createService()
1 parent a1afa21 commit df261c2

File tree

4 files changed

+134
-89
lines changed

4 files changed

+134
-89
lines changed

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

Lines changed: 1 addition & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -73,96 +73,8 @@ private function resolveDefinition($id, DefinitionDecorator $definition)
7373
}
7474

7575
$this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $id, $parent));
76-
$def = new Definition();
7776

78-
// merge in parent definition
79-
// purposely ignored attributes: scope, abstract, tags
80-
$def->setClass($parentDef->getClass());
81-
$def->setArguments($parentDef->getArguments());
82-
$def->setMethodCalls($parentDef->getMethodCalls());
83-
$def->setProperties($parentDef->getProperties());
84-
if ($parentDef->getFactoryClass(false)) {
85-
$def->setFactoryClass($parentDef->getFactoryClass(false));
86-
}
87-
if ($parentDef->getFactoryMethod(false)) {
88-
$def->setFactoryMethod($parentDef->getFactoryMethod(false));
89-
}
90-
if ($parentDef->getFactoryService(false)) {
91-
$def->setFactoryService($parentDef->getFactoryService(false));
92-
}
93-
$def->setFactory($parentDef->getFactory());
94-
$def->setConfigurator($parentDef->getConfigurator());
95-
$def->setFile($parentDef->getFile());
96-
$def->setPublic($parentDef->isPublic());
97-
$def->setLazy($parentDef->isLazy());
98-
99-
// overwrite with values specified in the decorator
100-
$changes = $definition->getChanges();
101-
if (isset($changes['class'])) {
102-
$def->setClass($definition->getClass());
103-
}
104-
if (isset($changes['factory_class'])) {
105-
$def->setFactoryClass($definition->getFactoryClass(false));
106-
}
107-
if (isset($changes['factory_method'])) {
108-
$def->setFactoryMethod($definition->getFactoryMethod(false));
109-
}
110-
if (isset($changes['factory_service'])) {
111-
$def->setFactoryService($definition->getFactoryService(false));
112-
}
113-
if (isset($changes['factory'])) {
114-
$def->setFactory($definition->getFactory());
115-
}
116-
if (isset($changes['configurator'])) {
117-
$def->setConfigurator($definition->getConfigurator());
118-
}
119-
if (isset($changes['file'])) {
120-
$def->setFile($definition->getFile());
121-
}
122-
if (isset($changes['public'])) {
123-
$def->setPublic($definition->isPublic());
124-
}
125-
if (isset($changes['lazy'])) {
126-
$def->setLazy($definition->isLazy());
127-
}
128-
if (isset($changes['decorated_service'])) {
129-
$decoratedService = $definition->getDecoratedService();
130-
if (null === $decoratedService) {
131-
$def->setDecoratedService($decoratedService);
132-
} else {
133-
$def->setDecoratedService($decoratedService[0], $decoratedService[1]);
134-
}
135-
}
136-
137-
// merge arguments
138-
foreach ($definition->getArguments() as $k => $v) {
139-
if (is_numeric($k)) {
140-
$def->addArgument($v);
141-
continue;
142-
}
143-
144-
if (0 !== strpos($k, 'index_')) {
145-
throw new RuntimeException(sprintf('Invalid argument key "%s" found.', $k));
146-
}
147-
148-
$index = (int) substr($k, strlen('index_'));
149-
$def->replaceArgument($index, $v);
150-
}
151-
152-
// merge properties
153-
foreach ($definition->getProperties() as $k => $v) {
154-
$def->setProperty($k, $v);
155-
}
156-
157-
// append method calls
158-
if (count($calls = $definition->getMethodCalls()) > 0) {
159-
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
160-
}
161-
162-
// these attributes are always taken from the child
163-
$def->setAbstract($definition->isAbstract());
164-
$def->setScope($definition->getScope());
165-
$def->setTags($definition->getTags());
77+
$def = $definition->resolveChanges($parentDef);
16678

16779
// set new definition on container
16880
$this->container->setDefinition($id, $def);

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,24 @@ public function findDefinition($id)
840840
*/
841841
public function createService(Definition $definition, $id, $tryProxy = true)
842842
{
843+
if ($definition instanceof DefinitionDecorator) {
844+
$parents = array();
845+
$currentId = $id;
846+
$currentDef = $definition;
847+
do {
848+
if (!$this->has($parent = $currentDef->getParent())) {
849+
throw new RuntimeException(sprintf('Service "%s": Parent definition "%s" does not exist.', $currentId, $parent));
850+
}
851+
$parents[] = $currentDef = $this->findDefinition($parent);
852+
$currentId = $parent;
853+
} while ($currentDef instanceof DefinitionDecorator);
854+
855+
for ($i = count($parents) - 1; 0 < $i; --$i) {
856+
$parents[$i - 1] = $parents[$i - 1]->resolveChanges($parents[$i]);
857+
}
858+
$definition = $definition->resolveChanges($parents[0]);
859+
}
860+
843861
if ($definition->isSynthetic()) {
844862
throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id));
845863
}

src/Symfony/Component/DependencyInjection/DefinitionDecorator.php

Lines changed: 105 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.
@@ -206,4 +207,108 @@ public function replaceArgument($index, $value)
206207

207208
return $this;
208209
}
210+
211+
/**
212+
* Creates a new Definition by merging the current decorator with the given parent definition.
213+
*
214+
* @return Definition
215+
*/
216+
public function resolveChanges(Definition $parentDef)
217+
{
218+
if ($parentDef instanceof self) {
219+
throw new InvalidArgumentException('$parenfDef must be a resolved Definition.');
220+
}
221+
$def = new Definition();
222+
223+
// merge in parent definition
224+
// purposely ignored attributes: scope, abstract, tags
225+
$def->setClass($parentDef->getClass());
226+
$def->setArguments($parentDef->getArguments());
227+
$def->setMethodCalls($parentDef->getMethodCalls());
228+
$def->setProperties($parentDef->getProperties());
229+
if ($parentDef->getFactoryClass(false)) {
230+
$def->setFactoryClass($parentDef->getFactoryClass(false));
231+
}
232+
if ($parentDef->getFactoryMethod(false)) {
233+
$def->setFactoryMethod($parentDef->getFactoryMethod(false));
234+
}
235+
if ($parentDef->getFactoryService(false)) {
236+
$def->setFactoryService($parentDef->getFactoryService(false));
237+
}
238+
$def->setFactory($parentDef->getFactory());
239+
$def->setConfigurator($parentDef->getConfigurator());
240+
$def->setFile($parentDef->getFile());
241+
$def->setPublic($parentDef->isPublic());
242+
$def->setLazy($parentDef->isLazy());
243+
244+
// overwrite with values specified in the decorator
245+
$changes = $this->getChanges();
246+
if (isset($changes['class'])) {
247+
$def->setClass($this->getClass());
248+
}
249+
if (isset($changes['factory_class'])) {
250+
$def->setFactoryClass($this->getFactoryClass(false));
251+
}
252+
if (isset($changes['factory_method'])) {
253+
$def->setFactoryMethod($this->getFactoryMethod(false));
254+
}
255+
if (isset($changes['factory_service'])) {
256+
$def->setFactoryService($this->getFactoryService(false));
257+
}
258+
if (isset($changes['factory'])) {
259+
$def->setFactory($this->getFactory());
260+
}
261+
if (isset($changes['configurator'])) {
262+
$def->setConfigurator($this->getConfigurator());
263+
}
264+
if (isset($changes['file'])) {
265+
$def->setFile($this->getFile());
266+
}
267+
if (isset($changes['public'])) {
268+
$def->setPublic($this->isPublic());
269+
}
270+
if (isset($changes['lazy'])) {
271+
$def->setLazy($this->isLazy());
272+
}
273+
if (isset($changes['decorated_service'])) {
274+
$decoratedService = $this->getDecoratedService();
275+
if (null === $decoratedService) {
276+
$def->setDecoratedService($decoratedService);
277+
} else {
278+
$def->setDecoratedService($decoratedService[0], $decoratedService[1]);
279+
}
280+
}
281+
282+
// merge arguments
283+
foreach ($this->getArguments() as $k => $v) {
284+
if (is_numeric($k)) {
285+
$def->addArgument($v);
286+
continue;
287+
}
288+
289+
if (0 !== strpos($k, 'index_')) {
290+
throw new RuntimeException(sprintf('Invalid argument key "%s" found.', $k));
291+
}
292+
293+
$index = (int) substr($k, strlen('index_'));
294+
$def->replaceArgument($index, $v);
295+
}
296+
297+
// merge properties
298+
foreach ($this->getProperties() as $k => $v) {
299+
$def->setProperty($k, $v);
300+
}
301+
302+
// append method calls
303+
if (count($calls = $this->getMethodCalls()) > 0) {
304+
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
305+
}
306+
307+
// these attributes are always taken from the child
308+
$def->setAbstract($this->isAbstract());
309+
$def->setScope($this->getScope());
310+
$def->setTags($this->getTags());
311+
312+
return $def;
313+
}
209314
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\DependencyInjection\ContainerBuilder;
2020
use Symfony\Component\DependencyInjection\ContainerInterface;
2121
use Symfony\Component\DependencyInjection\Definition;
22+
use Symfony\Component\DependencyInjection\DefinitionDecorator;
2223
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
2324
use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
2425
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
@@ -404,6 +405,15 @@ public function testResolveServices()
404405
$this->assertEquals($builder->get('foo'), $builder->resolveServices(new Expression('service("foo")')), '->resolveServices() resolves expressions');
405406
}
406407

408+
public function testResolveServicesWithDecoratedDefinition()
409+
{
410+
$builder = new ContainerBuilder();
411+
$builder->setDefinition('grandpa', new Definition('stdClass'));
412+
$builder->setDefinition('parent', new DefinitionDecorator('grandpa'));
413+
414+
$this->assertInstanceOf('stdClass', $builder->resolveServices(new DefinitionDecorator('parent')));
415+
}
416+
407417
public function testMerge()
408418
{
409419
$container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo')));

0 commit comments

Comments
 (0)