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 739697c

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

File tree

3 files changed

+156
-7
lines changed
Filter options

3 files changed

+156
-7
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+60-7Lines 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
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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
+31Lines 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
+65Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.