8000 [DI] Add support for getter autowiring · symfony/symfony@24820be · GitHub
[go: up one dir, main page]

Skip to content

Commit 24820be

Browse files
committed
[DI] Add support for getter autowiring
1 parent 2183f98 commit 24820be

File tree

3 files changed

+154
-7
lines changed

3 files changed

+154
-7
lines changed

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

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public function process(ContainerBuilder $container)
4242
} finally {
4343
spl_autoload_unregister($throwingAutoloader);
4444

45-
// Free memory and remove circular reference to container
45+
// Free memory
4646
$this->reflectionClasses = array();
4747
$this->definedTypes = array();
4848
$this->types = null;
@@ -97,6 +97,7 @@ protected function processValue($value, $isRoot = false)
9797
}
9898

9999
$methodCalls = $this->autowireMethodCalls($reflectionClass, $methodCalls, $autowiredMethods);
100+
$overriddenGetters = $this->autowireOverridenGetters($value->getOverriddenGetters(), $autowiredMethods);
100101

101102
if ($constructor) {
102103
list(, $arguments) = array_shift($methodCalls);
@@ -110,6 +111,10 @@ protected function processValue($value, $isRoot = false)
110111
$value->setMethodCalls($methodCalls);
111112
}
112113

114+
if ($overriddenGetters !== $value->getOverriddenGetters()) {
115+
$value->setOverriddenGetters($overriddenGetters);
116+
}
117+
113118
return parent::processValue($value, $isRoot);
114119
}
115120

@@ -131,7 +136,7 @@ private function getMethodsToAutowire(\ReflectionClass $reflectionClass, array $
131136
$regexList[] = '/^'.str_replace('\*', '.*', preg_quote($pattern, '/')).'$/i';
132137
}
133138

134-
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
139+
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $reflectionMethod) {
135140
if ($reflectionMethod->isStatic()) {
136141
continue;
137142
}
@@ -171,7 +176,7 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
171176
list($method, $arguments) = $call;
172177
$method = $parameterBag->resolveValue($method);
173178

174-
if (isset($autowiredMethods[$lcMethod = strtolower($method)])) {
179+
if (isset($autowiredMethods[$lcMethod = strtolower($method)]) && $autowiredMethods[$lcMethod]->isPublic()) {
175180
$reflectionMethod = $autowiredMethods[$lcMethod];
176181
unset($autowiredMethods[$lcMethod]);
177182
} else {
@@ -184,15 +189,15 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
184189
}
185190
}
186191

187-
$arguments = $this->autowireMethod($reflectionMethod, $arguments, true);
192+
$arguments = $this->autowireMethodCall($reflectionMethod, $arguments, true);
188193

189194
if ($arguments !== $call[1]) {
190195
$methodCalls[$i][1] = $arguments;
191196
}
192197
}
193198

194199
foreach ($autowiredMethods as $reflectionMethod) {
195-
if ($arguments = $this->autowireMethod($reflectionMethod, array(), false)) {
200+
if ($reflectionMethod->isPublic() && $arguments = $this->autowireMethodCall($reflectionMethod, array(), false)) {
196201
$methodCalls[] = array($reflectionMethod->name, $arguments);
197202
}
198203
}
@@ -201,7 +206,7 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
201206
}
202207

203208
/**
204-
* Autowires the constructor or a setter.
209+
* Autowires the constructor or a method.
205210
*
206211
* @param \ReflectionMethod $reflectionMethod
207212
* @param array $arguments
@@ -211,7 +216,7 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
211216
*
212217
* @throws RuntimeException
213218
*/
214-
private function autowireMethod(\ReflectionMethod $reflectionMethod, array $arguments, $mustAutowire)
219+
private function autowireMethodCall(\ReflectionMethod $reflectionMethod, array $arguments, $mustAutowire)
215220
{
216221
$didAutowire = false; // Whether any arguments have been autowired or not
217222
foreach ($reflectionMethod->getParameters() as $index => $parameter) {
@@ -292,6 +297,52 @@ private function autowireMethod(\ReflectionMethod $reflectionMethod, array $argu
292297
return $arguments;
293298
}
294299

300+
/**
301+
* Autowires getters.
302+
*
303+
* @param array $overridenGetters
304+
* @param array $autowiredMethod
305+
*
306+
* @return array
307+
*/
308+
private function autowireOverridenGetters(array $overridenGetters, array $autowiredMethod)
309+
{
310+
foreach ($autowiredMethod as $reflectionMethod) {
311+
if (
312+
isset($overridenGetters[$reflectionMethod->name]) ||
313+
!method_exists($reflectionMethod, 'getReturnType') ||
314+
0 !== $reflectionMethod->getNumberOfParameters() ||
315+
$reflectionMethod->isFinal() ||
316+
$reflectionMethod->returnsReference() ||
317+
!($returnType = $reflectionMethod->getReturnType())
318+
) {
319+
continue;
320+
}
321+
322+
323+
if (null === $this->types) {
324+
$this->populateAvailableTypes();
325+
}
326+
327+
$class = $returnType->__toString();
328+
if (isset($this->types[$class])) {
329+
$value = new Reference($this->types[$class]);
330+
} else {
331+
try {
332+
$value = $this->createAutowiredDefinition(new \ReflectionClass($class));
333+
} catch (\ReflectionException $e) {
334+
continue;
335+
} catch (RuntimeException $e) {
336+
continue;
337+
}
338+
}
339+
340+
$overridenGetters[$reflectionMethod->name] = $value;
341+
}
342+
343+
return $overridenGetters;
344+
}
345+
295346
/**
296347
* Populates the list of available types.
297348
*/

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\DependencyInjection\ContainerBuilder;
1616
use Symfony\Component\DependencyInjection\Reference;
1717
use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic;
18+
use Symfony\Component\DependencyInjection\Tests\Fixtures\GetterOverriding;
1819

1920
/**
2021
* @author Kévin Dunglas <dunglas@gmail.com>
@@ -515,6 +516,31 @@ public function testExplicitMethodInjection()
515516
);
516517
}
517518

519+
/**
520+
* @requires PHP 7.1
521+
*/
522+
public function testGetterOverriding()
523+
{
524+
$container = new ContainerBuilder();
525+
$container->register('b', B::class);
526+
527+
$container
528+
->register('getter_overriding', GetterOverriding::class)
529+
->setOverriddenGetter('getExplicitlyDefined', new Reference('b'))
530+
->setAutowiredMethods(array('get*'))
531+
;
532+
533+
$pass = new AutowirePass();
534+
$pass->process($container);
535+
536+
$overridenGetters = $container->getDefinition('getter_overriding')->getOverriddenGetters();
537+
$this->assertEquals(array(
538+
'getexplicitlydefined' => new Reference('b'),
539+
'getfoo' => new Reference('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Foo'),
540+
'getbar' => new Reference('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Bar'),
541+
), $overridenGetters);
542+
}
543+
518544
/**
519545
* @dataProvider getCreateResourceTests
520546
*/
@@ -852,6 +878,11 @@ public function notASetter(A $a)
852878
{
853879
// should be called only when explicitly specified
854880
}
881+
882+
protected function setProtectedMethod(A $a)
883+
{
884+
// should not be called
885+
}
855886
}
856887

857888
class SetterInjectionCollision
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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\Fixtures;
13+
14+
use Symfony\Component\DependencyInjection\Tests\Compiler\A;
15+
use Symfony\Component\DependencyInjection\Tests\Compiler\B;
16+
use Symfony\Component\DependencyInjection\Tests\Compiler\Bar;
17+
use Symfony\Component\DependencyInjection\Tests\Compiler\Foo;
18+
19+
/**
20+
* To test getter autowiring with PHP >= 7.1.
21+
*
22+
* @author Kévin Dunglas <dunglas@gmail.com>
23+
*/
24+
class GetterOverriding
25+
{
26+
public function getFoo(): ?Foo
27+
{
28+
// should be called
29+
}
30+
31+
protected function getBar(): Bar
32+
{
33+
// should be called
34+
}
35+
36+
public function getNoTypeHint()
37+
{
38+
// should not be called
39+
}
40+
41+
public function getUnknown(): NotExist
42+
{
43+
// should not be called
44+
}
45+
46+
public function getExplicitlyDefined(): B
47+
{
48+
// should be called but not autowired
49+
}
50+
51+
public function getScalar(): string
52+
{
53+
// should not be called
54+
}
55+
56+
final public function getFinal(): A
57+
{
58+
// should not be called
59+
}
60+
61+
public function &getReference(): A
62+
{
63+
// should not be called
64+
}
65+
}

0 commit comments

Comments
 (0)
0