Skip to content

Commit 32cbfd4

Browse files
committed
feature #15096 [DependencyInjection] Allow anonymous DefinitionDecorator resolving (nicolas-grekas)
This PR was merged into the 2.8 branch. Discussion ---------- [DependencyInjection] Allow anonymous DefinitionDecorator resolving | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - This PR allows injecting anonymous DefinitionDecorator into services' arguments/properties, such as: ```php $container->register('foo_service_name', 'FooClass') ->setProperty('bar', new DefinitionDecorator('definition_decorated_service')) ; ``` Commits ------- e5763ce [DependencyInjection] Allow anonymous DefinitionDecorator resolving
2 parents cad16ac + e5763ce commit 32cbfd4

File tree

3 files changed

+102
-42
lines changed

3 files changed

+102
-42
lines changed

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

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -48,40 +48,24 @@ public function process(ContainerBuilder $container)
4848
$this->formatter = $this->compiler->getLoggingFormatter();
4949
$this->graph = $this->compiler->getServiceReferenceGraph();
5050

51-
foreach ($container->getDefinitions() as $id => $definition) {
52-
$this->currentId = $id;
53-
54-
$definition->setArguments(
55-
$this->inlineArguments($container, $definition->getArguments())
56-
);
57-
58-
$definition->setMethodCalls(
59-
$this->inlineArguments($container, $definition->getMethodCalls())
60-
);
61-
62-
$definition->setProperties(
63-
$this->inlineArguments($container, $definition->getProperties())
64-
);
65-
66-
$configurator = $this->inlineArguments($container, array($definition->getConfigurator()));
67-
$definition->setConfigurator($configurator[0]);
68-
69-
$factory = $this->inlineArguments($container, array($definition->getFactory()));
70-
$definition->setFactory($factory[0]);
71-
}
51+
$container->setDefinitions($this->inlineArguments($container, $container->getDefinitions(), true));
7252
}
7353

7454
/**
7555
* Processes inline arguments.
7656
*
7757
* @param ContainerBuilder $container The ContainerBuilder
7858
* @param array $arguments An array of arguments
59+
* @param bool $isRoot If we are processing the root definitions or not
7960
*
8061
* @return array
8162
*/
82-
private function inlineArguments(ContainerBuilder $container, array $arguments)
63+
private function inlineArguments(ContainerBuilder $container, array $arguments, $isRoot = false)
8364
{
8465
foreach ($arguments as $k => $argument) {
66+
if ($isRoot) {
67+
$this->currentId = $k;
68+
}
8569
if (is_array($argument)) {
8670
$arguments[$k] = $this->inlineArguments($container, $argument);
8771
} elseif ($argument instanceof Reference) {
@@ -102,6 +86,12 @@ private function inlineArguments(ContainerBuilder $container, array $arguments)
10286
$argument->setArguments($this->inlineArguments($container, $argument->getArguments()));
10387
$argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls()));
10488
$argument->setProperties($this->inlineArguments($container, $argument->getProperties()));
89+
90+
$configurator = $this->inlineArguments($container, array($argument->getConfigurator()));
91+
$argument->setConfigurator($configurator[0]);
92+
93+
$factory = $this->inlineArguments($container, array($argument->getFactory()));
94+
$argument->setFactory($factory[0]);
10595
}
10696
}
10797

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

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@
2121
* merged Definition instance.
2222
*
2323
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
24+
* @author Nicolas Grekas <p@tchwork.com>
2425
*/
2526
class ResolveDefinitionTemplatesPass implements CompilerPassInterface
2627
{
27-
private $container;
2828
private $compiler;
2929
private $formatter;
30+
private $currentId;
3031

3132
/**
3233
* Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances.
@@ -35,44 +36,80 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface
3536
*/
3637
public function process(ContainerBuilder $container)
3738
{
38-
$this->container = $container;
3939
$this->compiler = $container->getCompiler();
4040
$this->formatter = $this->compiler->getLoggingFormatter();
4141

42-
foreach ($container->getDefinitions() as $id => $definition) {
43-
// yes, we are specifically fetching the definition from the
44-
// container to ensure we are not operating on stale data
45-
$definition = $container->getDefinition($id);
46-
if (!$definition instanceof DefinitionDecorator || $definition->isAbstract()) {
47-
continue;
48-
}
42+
$container->setDefinitions($this->resolveArguments($container, $container->getDefinitions(), true));
43+
}
4944

50-
$this->resolveDefinition($id, $definition);
45+
/**
46+
* Resolves definition decorator arguments.
47+
*
48+
* @param ContainerBuilder $container The ContainerBuilder
49+
* @param array $arguments An array of arguments
50+
* @param bool $isRoot If we are processing the root definitions or not
51+
*
52+
* @return array
53+
*/
54+
private function resolveArguments(ContainerBuilder $container, array $arguments, $isRoot = false)
55+
{
56+
foreach ($arguments as $k => $argument) {
57+
if ($isRoot) {
58+
// yes, we are specifically fetching the definition from the
59+
// container to ensure we are not operating on stale data
60+
$arguments[$k] = $argument = $container->getDefinition($k);
61+
$this->currentId = $k;
62+
}
63+
if (is_array($argument)) {
64+
$arguments[$k] = $this->resolveArguments($container, $argument);
65+
} elseif ($argument instanceof Definition) {
66+
if ($argument instanceof DefinitionDecorator) {
67+
$arguments[$k] = $argument = $this->resolveDefinition($container, $argument);
68+
if ($isRoot) {
69+
$container->setDefinition($k, $argument);
70+
}
71+
}
72+
$argument->setArguments($this->resolveArguments($container, $argument->getArguments()));
73+
$argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls()));
74+
$argument->setProperties($this->resolveArguments($container, $argument->getProperties()));
75+
76+
$configurator = $this->resolveArguments($container, array($argument->getConfigurator()));
77+
$argument->setConfigurator($configurator[0]);
78+
79+
$factory = $this->resolveArguments($container, array($argument->getFactory()));
80+
$argument->setFactory($factory[0]);
81+
}
5182
}
83+
84+
return $arguments;
5285
}
5386

5487
/**
5588
* Resolves the definition.
5689
*
57-
* @param string $id The definition identifier
90+
* @param ContainerBuilder $container The ContainerBuilder
5891
* @param DefinitionDecorator $definition
5992
*
6093
* @return Definition
6194
*
6295
* @throws \RuntimeException When the definition is invalid
6396
*/
64-
private function resolveDefinition($id, DefinitionDecorator $definition)
97+
private function resolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition)
6598
{
66-
if (!$this->container->hasDefinition($parent = $definition->getParent())) {
67-
throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $id));
99+
if (!$container->hasDefinition($parent = $definition->getParent())) {
100+
throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $this->currentId));
68101
}
69102

70-
$parentDef = $this->container->getDefinition($parent);
103+
$parentDef = $container->getDefinition($parent);
71104
if ($parentDef instanceof DefinitionDecorator) {
72-
$parentDef = $this->resolveDefinition($parent, $parentDef);
105+
$id = $this->currentId;
106+
$this->currentId = $parent;
107+
$parentDef = $this->resolveDefinition($container, $parentDef);
108+
$container->setDefinition($parent, $parentDef);
109+
$this->currentId = $id;
73110
}
74111

75-
$this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $id, $parent));
112+
$this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent));
76113
$def = new Definition();
77114

78115
// merge in parent definition
@@ -156,9 +193,6 @@ private function resolveDefinition($id, DefinitionDecorator $definition)
156193
$def->setScope($definition->getScope(false), false);
157194
$def->setTags($definition->getTags());
158195

159-
// set new definition on container
160-
$this->container->setDefinition($id, $def);
161-
162196
return $def;
163197
}
164198
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,42 @@ public function testSetLazyOnServiceIsParent()
176176
$this->assertTrue($container->getDefinition('child1')->isLazy());
177177
}
178178

179+
public function testDeepDefinitionsResolving()
180+
{
181+
$container = new ContainerBuilder();
182+
183+
$container->register('parent', 'parentClass');
184+
$container->register('sibling', 'siblingClass')
185+
->setConfigurator(new DefinitionDecorator('parent'), 'foo')
186+
->setFactory(array(new DefinitionDecorator('parent'), 'foo'))
187+
->addArgument(new DefinitionDecorator('parent'))
188+
->setProperty('prop', new DefinitionDecorator('parent'))
189+
->addMethodCall('meth', array(new DefinitionDecorator('parent')))
190+
;
191+
192+
$this->process($container);
193+
194+
$configurator = $container->getDefinition('sibling')->getConfigurator();
195+
$this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($configurator));
196+
$this->assertSame('parentClass', $configurator->getClass());
197+
198+
$factory = $container->getDefinition('sibling')->getFactory();
199+
$this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($factory[0]));
200+
$this->assertSame('parentClass', $factory[0]->getClass());
201+
202+
$argument = $container->getDefinition('sibling')->getArgument(0);
203+
$this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($argument));
204+
$this->assertSame('parentClass', $argument->getClass());
205+
206+
$properties = $container->getDefinition('sibling')->getProperties();
207+
$this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($properties['prop']));
208+
$this->assertSame('parentClass', $properties['prop']->getClass());
209+
210+
$methodCalls = $container->getDefinition('sibling')->getMethodCalls();
211+
$this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($methodCalls[0][1][0]));
212+
$this->assertSame('parentClass', $methodCalls[0][1][0]->getClass());
213+
}
214+
179215
protected function process(ContainerBuilder $container)
180216
{
181217
$pass = new ResolveDefinitionTemplatesPass();

0 commit comments

Comments
 (0)