Skip to content

Navigation Menu

Sign in
Appearance settings

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 acbee81

Browse filesBrowse files
committed
[DependencyInjection] Resolve ChildDefinition in AbstractRecursivePass
1 parent 6e54976 commit acbee81
Copy full SHA for acbee81

File tree

2 files changed

+149
-7
lines changed
Filter options

2 files changed

+149
-7
lines changed

‎src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+22-7Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

1414
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
15+
use Symfony\Component\DependencyInjection\ChildDefinition;
1516
use Symfony\Component\DependencyInjection\ContainerBuilder;
1617
use Symfony\Component\DependencyInjection\Definition;
1718
use Symfony\Component\DependencyInjection\Exception\LogicException;
@@ -131,25 +132,35 @@ protected function getConstructor(Definition $definition, $required)
131132

132133
if ($factory) {
133134
[$class, $method] = $factory;
135+
136+
if ('__construct' === $method) {
137+
throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId));
138+
}
139+
134140
if ($class instanceof Reference) {
135-
$class = $this->container->findDefinition((string) $class)->getClass();
141+
$factoryDefinition = $this->container->findDefinition((string) $class);
142+
while ((null === $class = $factoryDefinition->getClass()) && $factoryDefinition instanceof ChildDefinition) {
143+
$factoryDefinition = $this->container->findDefinition($factoryDefinition->getParent());
144+
}
136145
} elseif ($class instanceof Definition) {
137146
$class = $class->getClass();
138147
} elseif (null === $class) {
139148
$class = $definition->getClass();
140149
}
141150

142-
if ('__construct' === $method) {
143-
throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId));
144-
}
145-
146151
return $this->getReflectionMethod(new Definition($class), $method);
147152
}
148153

149-
$class = $definition->getClass();
154+
while ((null === $class = $definition->getClass()) && $definition instanceof ChildDefinition) {
155+
$definition = $this->container->findDefinition($definition->getParent());
156+
}
150157

151158
try {
152159
if (!$r = $this->container->getReflectionClass($class)) {
160+
if (null === $class) {
161+
throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId));
162+
}
163+
153164
throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
154165
}
155166
} catch (\ReflectionException $e) {
@@ -179,7 +190,11 @@ protected function getReflectionMethod(Definition $definition, $method)
179190
return $this->getConstructor($definition, true);
180191
}
181192

182-
if (!$class = $definition->getClass()) {
193+
while ((null === $class = $definition->getClass()) && $definition instanceof ChildDefinition) {
194+
$definition = $this->container->findDefinition($definition->getParent());
195+
}
196+
197+
if (null === $class) {
183198
throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId));
184199
}
185200

+127Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\ChildDefinition;
16+
use Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass;
17+
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
use Symfony\Component\DependencyInjection\Definition;
19+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
20+
use Symfony\Component\DependencyInjection\Reference;
21+
use Symfony\Component\DependencyInjection\Tests\Fixtures\Bar;
22+
use Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryDummy;
23+
24+
class AbstractRecursivePassTest extends TestCase
25+
{
26+
public function testGetConstructorResolvesFactoryChildDefinitionsClass()
27+
{
28+
$container = new ContainerBuilder();
29+
$container->setParameter('factory_dummy_class', FactoryDummy::class);
30+
$container
31+
->register('parent', '%factory_dummy_class%')
32+
->setAbstract(true);
33+
$container->setDefinition('child', new ChildDefinition('parent'));
34+
$container
35+
->register('foo', \stdClass::class)
36+
->setFactory([new Reference('child'), 'createFactory']);
37+
38+
$pass = new class() extends AbstractRecursivePass {
39+
public $actual;
40+
41+
protected function processValue($value, $isRoot = false)
42+
{
43+
if ($value instanceof Definition && 'foo' === $this->currentId) {
44+
$this->actual = $this->getConstructor($value, true);
45+
}
46+
47+
return parent::processValue($value, $isRoot);
48+
}
49+
};
50+
$pass->process($container);
51+
52+
$this->assertInstanceOf(\ReflectionMethod::class, $pass->actual);
53+
$this->assertSame(FactoryDummy::class, $pass->actual->class);
54+
}
55+
56+
public function testGetConstructorResolvesChildDefinitionsClass()
57+
{
58+
$container = new ContainerBuilder();
59+
$container
60+
->register('parent', Bar::class)
61+
->setAbstract(true);
62+
$container->setDefinition('foo', new ChildDefinition('parent'));
63+
64+
$pass = new class() extends AbstractRecursivePass {
65+
public $actual;
66+
67+
protected function processValue($value, $isRoot = false)
68+
{
69+
if ($value instanceof Definition && 'foo' === $this->currentId) {
70+
$this->actual = $this->getConstructor($value, true);
71+
}
72+
73+
return parent::processValue($value, $isRoot);
74+
}
75+
};
76+
$pass->process($container);
77+
78+
$this->assertInstanceOf(\ReflectionMethod::class, $pass->actual);
79+
$this->assertSame(Bar::class, $pass->actual->class);
80+
}
81+
82+
public function testGetReflectionMethodResolvesChildDefinitionsClass()
83+
{
84+
$container = new ContainerBuilder();
85+
$container
86+
->register('parent', Bar::class)
87+
->setAbstract(true);
88+
$container->setDefinition('foo', new ChildDefinition('parent'));
89+
90+
$pass = new class() extends AbstractRecursivePass {
91+
public $actual;
92+
93+
protected function processValue($value, $isRoot = false)
94+
{
95+
if ($value instanceof Definition && 'foo' === $this->currentId) {
96+
$this->actual = $this->getReflectionMethod($value, 'create');
97+
}
98+
99+
return parent::processValue($value, $isRoot);
100+
}
101+
};
102+
$pass->process($container);
103+
104+
$this->assertInstanceOf(\ReflectionMethod::class, $pass->actual);
105+
$this->assertSame(Bar::class, $pass->actual->class);
106+
}
107+
108+
public function testGetConstructorDefinitionNoClass()
109+
{
110+
$this->expectException(RuntimeException::class);
111+
$this->expectExceptionMessage('Invalid service "foo": the class is not set.');
112+
113+
$container = new ContainerBuilder();
114+
$container->register('foo');
115+
116+
(new class() extends AbstractRecursivePass {
117+
protected function processValue($value, $isRoot = false)
118+
{
119+
if ($value instanceof Definition && 'foo' === $this->currentId) {
120+
$this->getConstructor($value, true);
121+
}
122+
123+
return parent::processValue($value, $isRoot);
124+
}
125+
})->process($container);
126+
}
127+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.