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

Skip to content

Commit 9ed1810

Browse files
committed
[DI] Add support for getter autowiring
1 parent 9e6d6ba commit 9ed1810

File tree

2 files changed

+133
-5
lines changed

2 files changed

+133
-5
lines changed

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

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ private function completeDefinition($id, Definition $definition, array $autowire
108108
$methodsCalled[$methodCall[0]] = true;
109109
}
110110

111+
foreach ($definition->getOverriddenGetters() as $overriddenGetter => $returnValue) {
112+
$methodsCalled[$overriddenGetter] = true;
113+
}
114+
111115
foreach ($this->getMethodsToAutowire($id, $reflectionClass, $autowiredMethods) as $reflectionMethod) {
112116
if (!isset($methodsCalled[$reflectionMethod->name])) {
113117
$this->autowireMethod($id, $definition, $reflectionMethod);
@@ -132,7 +136,7 @@ private function getMethodsToAutowire($id, \ReflectionClass $reflectionClass, ar
132136
$regexList[] = '/^'.str_replace('\*', '.*', preg_quote($pattern, '/')).'$/i';
133137
}
134138

135-
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
139+
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $reflectionMethod) {
136140
if ($reflectionMethod->isStatic()) {
137141
continue;
138142
}
@@ -164,6 +168,19 @@ private function getMethodsToAutowire($id, \ReflectionClass $reflectionClass, ar
164168
*/
165169
private function autowireMethod($id, Definition $definition, \ReflectionMethod $reflectionMethod)
166170
{
171+
if (null === $this->types) {
172+
$this->populateAvailableTypes();
173+
}
174+
175+
if ($this->overrideGetter($id, $definition, $reflectionMethod)) {
176+
return;
177+
}
178+
179+
if ($reflectionMethod->isProtected()) {
180+
// Only getter overriding is supported for protected methods
181+
return;
182+
}
183+
167184
if ($isConstructor = $reflectionMethod->isConstructor()) {
168185
$arguments = $definition->getArguments();
169186
} else {
@@ -193,10 +210,6 @@ private function autowireMethod($id, Definition $definition, \ReflectionMethod $
193210
continue;
194211
}
195212

196-
if (null === $this->types) {
197-
$this->populateAvailableTypes();
198-
}
199-
200213
if (isset($this->types[$typeHint->name])) {
201214
$value = new Reference($this->types[$typeHint->name]);
202215
$addMethodCall = true;
@@ -247,6 +260,43 @@ private function autowireMethod($id, Definition $definition, \ReflectionMethod $
247260
}
248261
}
249262

263+
/**
264+
* Getter injection.
265+
*
266+
* @param string $id
267+
* @param Definition $definition
268+
* @param \ReflectionMethod $reflectionMethod
269+
*
270+
* @return bool
271+
*/
272+
private function overrideGetter($id, Definition $definition, \ReflectionMethod $reflectionMethod)
273+
{
274+
if (!method_exists($reflectionMethod, 'getReturnType')) {
275+
return false;
276+
}
277+
278+
if (0 !== $reflectionMethod->getNumberOfParameters() || $reflectionMethod->isFinal() || $reflectionMethod->returnsReference() || !($returnType = $reflectionMethod->getReturnType())) {
279+
return false;
280+
}
281+
282+
$class = (string) $returnType;
283+
if (isset($this->types[$class])) {
284+
$value = new Reference($this->types[$class]);
285+
} else {
286+
try {
287+
$value = $this->createAutowiredDefinition(new \ReflectionClass($class), $id);
288+
} catch (\ReflectionException $e) {
289+
return false;
290+
} catch (RuntimeException $e) {
291+
return false;
292+
}
293+
}
294+
295+
$definition->setOverriddenGetter($reflectionMethod->name, $value);
296+
297+
return true;
298+
}
299+
250300
/**
251301
* Populates the list of available types.
252302
*/

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

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
1515
use Symfony\Component\DependencyInjection\ContainerBuilder;
1616
use Symfony\Component\DependencyInjection\Reference;
17+
use Symfony\Component\HttpFoundation\RequestStack;
1718

1819
/**
1920
* @author Kévin Dunglas <dunglas@gmail.com>
@@ -497,6 +498,32 @@ public function testExplicitMethodInjection()
497498
);
498499
}
499500

501+
public function testGetterOverriding()
502+
{
503+
if (PHP_VERSION_ID < 70100) {
504+
$this->markTestSkipped('This test requires PHP 7.1 or superior');
505+
}
506+
507+
$container = new ContainerBuilder();
508+
$container->register('b', B::class);
509+
510+
$container
511+
->register('getter_overriding', GetterOverriding::class)
512+
->setOverriddenGetter('getExplicitlyDefined', new Reference('b'))
513+
->setAutowiredMethods(array('get*'))
514+
;
515+
516+
$pass = new AutowirePass();
517+
$pass->process($container);
518+
519+
$overridenGetters = $container->getDefinition('getter_overriding')->getOverriddenGetters();
520+
$this->assertEquals($overridenGetters, array(
521+
'getExplicitlyDefined' => new Reference('b'),
522+
'getFoo' => new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\foo'),
523+
'getBar' => new Reference('autowired.symfony\component\dependencyinjection\tests\compiler\bar'),
524+
));
525+
}
526+
500527
/**
501528
* @dataProvider getCreateResourceTests
502529
*/
@@ -807,6 +834,11 @@ public function notASetter(A $a)
807834
{
808835
// should be called only when explicitly specified
809836
}
837+
838+
protected function setProtectedMethod(A $a)
839+
{
840+
// should not be called
841+
}
810842
}
811843

812844
class SetterInjectionCollision
@@ -818,3 +850,49 @@ public function setMultipleInstancesForOneArg(CollisionInterface $collision)
818850
// should throw an exception
819851
}
820852
}
853+
854+
if (PHP_VERSION_ID >= 70100) {
855+
class GetterOverriding
856+
{
857+
public function getFoo(): ?Foo
858+
{
859+
// should be called
860+
}
861+
862+
protected function getBar(): Bar
863+
{
864+
// should be called
865+
}
866+
867+
public function getNoTypeHint()
868+
{
869+
// should not be called
870+
}
871+
872+
public function getUnknown(): NotExist
873+
{
874+
// should not be called
875+
}
876+
877+
public function getExplicitlyDefined(): B
878+
{
879+
// should be called but not autowired
880+
}
881+
882+
public function getScalar(): string
883+
{
884+
// should not be called
885+
}
886+
887+
final public function getFinal(): A
888+
{
889+
// should not be called
890+
}
891+
892+
public function &getReference(): A
893+
{
894+
// should not be called
895+
}
896+
897+
}
898+
}

0 commit comments

Comments
 (0)
0