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

Skip to content

Commit 739697c

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

File tree

3 files changed

+156
-7
lines changed

3 files changed

+156
-7
lines changed

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

Lines changed: 60 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;
@@ -96,7 +96,10 @@ protected function processValue($value, $isRoot = false)
9696
throw new RuntimeException(sprintf('Cannot autowire service "%s": class %s has no constructor but arguments are defined.', $this->currentId, $reflectionClass->name, $method));
9797
}
9898

99+
$overriddenGetters = $value->getOverriddenGetters();
100+
99101
$methodCalls = $this->autowireMethodCalls($reflectionClass, $methodCalls, $autowiredMethods);
102+
$overriddenGetters = $this->autowireOverridenGetters($overriddenGetters, $autowiredMethods);
100103

101104
if ($constructor) {
102105
list(, $arguments) = array_shift($methodCalls);
@@ -110,6 +113,10 @@ protected function processValue($value, $isRoot = false)
110113
$value->setMethodCalls($methodCalls);
111114
}
112115

116+
if ($overriddenGetters !== $value->getOverriddenGetters()) {
117+
$value->setOverriddenGetters($overriddenGetters);
118+
}
119+
113120
return parent::processValue($value, $isRoot);
114121
}
115122

@@ -131,7 +138,7 @@ private function getMethodsToAutowire(\ReflectionClass $reflectionClass, array $
131138
$regexList[] = '/^'.str_replace('\*', '.*', preg_quote($pattern, '/')).'$/i';
132139
}
133140

134-
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
141+
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $reflectionMethod) {
135142
if ($reflectionMethod->isStatic()) {
136143
continue;
137144
}
@@ -171,7 +178,7 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
171178
list($method, $arguments) = $call;
172179
$method = $parameterBag->resolveValue($method);
173180

174-
if (isset($autowiredMethods[$lcMethod = strtolower($method)])) {
181+
if (isset($autowiredMethods[$lcMethod = strtolower($method)]) && $autowiredMethods[$lcMethod]->isPublic()) {
175182
$reflectionMethod = $autowiredMethods[$lcMethod];
176183
unset($autowiredMethods[$lcMethod]);
177184
} else {
@@ -184,15 +191,15 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
184191
}
185192
}
186193

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

189196< 6DB6 /code>
if ($arguments !== $call[1]) {
190197
$methodCalls[$i][1] = $arguments;
191198
}
192199
}
193200

194201
foreach ($autowiredMethods as $reflectionMethod) {
195-
if ($arguments = $this->autowireMethod($reflectionMethod, array(), false)) {
202+
if ($reflectionMethod->isPublic() && $arguments = $this->autowireMethodCall($reflectionMethod, array(), false)) {
196203
$methodCalls[] = array($reflectionMethod->name, $arguments);
197204
}
198205
}
@@ -201,7 +208,7 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
201208
}
202209

203210
/**
204-
* Autowires the constructor or a setter.
211+
* Autowires the constructor or a method.
205212
*
206213
* @param \ReflectionMethod $reflectionMethod
207214
* @param array $arguments
@@ -211,7 +218,7 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
211218
*
212219
* @throws RuntimeException
213220
*/
214-
private function autowireMethod(\ReflectionMethod $reflectionMethod, array $arguments, $mustAutowire)
221+
private function autowireMethodCall(\ReflectionMethod $reflectionMethod, array $arguments, $mustAutowire)
215222
{
216223
$didAutowire = false; // Whether any arguments have been autowired or not
217224
foreach ($reflectionMethod->getParameters() as $index => $parameter) {
@@ -292,6 +299,52 @@ private function autowireMethod(\ReflectionMethod $reflectionMethod, array $argu
292299
return $arguments;
293300
}
294301

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

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