Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b50efa5

Browse files
committed
feature #21031 [DI] Getter autowiring (dunglas)
This PR was merged into the 3.3-dev branch. Discussion ---------- [DI] Getter autowiring | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | todo This PR adds support for getter autowiring. #20973 must be merged first. Example: ```yaml # app/config/config.yml services: Foo\Bar: autowire: ['get*'] ``` ```php namespace Foo; class Bar { protected function getBaz(): Baz // this feature only works with PHP 7+ { } } class Baz { } ```` `Baz` will be automatically registered as a service and an instance will be returned when `Bar::getBaz` will be called (and only at this time, lazy loading). This feature requires PHP 7 or superior. Commits ------- c48c36b [DI] Add support for getter autowiring
2 parents 03b7cf7 + c48c36b commit b50efa5

File tree

5 files changed

+160
-9
lines changed

5 files changed

+160
-9
lines changed

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* deprecated `ContainerBuilder::getClassResource()`, use `ContainerBuilder::getReflectionClass()` or `ContainerBuilder::addObjectResource()` instead
99
* added `ContainerBuilder::fileExists()` for checking and tracking file or directory existence
1010
* deprecated autowiring-types, use aliases instead
11+
* [EXPERIMENTAL] added support for getter autowiring
1112
* [EXPERIMENTAL] added support for getter-injection
1213
* added support for omitting the factory class name in a service definition if the definition class is set
1314
* deprecated case insensitivity of service identifiers

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

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public function process(ContainerBuilder $container)
3636
try {
3737
parent::process($container);
3838
} finally {
39-
// Free memory and remove circular reference to container
39+
// Free memory
4040
$this->definedTypes = array();
4141
$this->types = null;
4242
$this->ambiguousServiceTypes = array();
@@ -90,6 +90,7 @@ protected function processValue($value, $isRoot = false)
9090
}
9191

9292
$methodCalls = $this->autowireMethodCalls($reflectionClass, $methodCalls, $autowiredMethods);
93+
$overriddenGetters = $this->autowireOverridenGetters($value->getOverriddenGetters(), $autowiredMethods);
9394

9495
if ($constructor) {
9596
list(, $arguments) = array_shift($methodCalls);
@@ -103,6 +104,10 @@ protected function processValue($value, $isRoot = false)
103104
$value->setMethodCalls($methodCalls);
104105
}
105106

107+
if ($overriddenGetters !== $value->getOverriddenGetters()) {
108+
$value->setOverriddenGetters($overriddenGetters);
109+
}
110+
106111
return parent::processValue($value, $isRoot);
107112
}
108113

@@ -124,7 +129,7 @@ private function getMethodsToAutowire(\ReflectionClass $reflectionClass, array $
124129
$regexList[] = '/^'.str_replace('\*', '.*', preg_quote($pattern, '/')).'$/i';
125130
}
126131

127-
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
132+
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $reflectionMethod) {
128133
if ($reflectionMethod->isStatic()) {
129134
continue;
130135
}
@@ -164,7 +169,7 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
164169
list($method, $arguments) = $call;
165170
$method = $parameterBag->resolveValue($method);
166171

167-
if (isset($autowiredMethods[$lcMethod = strtolower($method)])) {
172+
if (isset($autowiredMethods[$lcMethod = strtolower($method)]) && $autowiredMethods[$lcMethod]->isPublic()) {
168173
$reflectionMethod = $autowiredMethods[$lcMethod];
169174
unset($autowiredMethods[$lcMethod]);
170175
} else {
@@ -177,15 +182,15 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
177182
}
178183
}
179184

180-
$arguments = $this->autowireMethod($reflectionMethod, $arguments, true);
185+
$arguments = $this->autowireMethodCall($reflectionMethod, $arguments, true);
181186

182187
if ($arguments !== $call[1]) {
183188
$methodCalls[$i][1] = $arguments;
184189
}
185190
}
186191

187192
foreach ($autowiredMethods as $reflectionMethod) {
188-
if ($arguments = $this->autowireMethod($reflectionMethod, array(), false)) {
193+
if ($reflectionMethod->isPublic() && $arguments = $this->autowireMethodCall($reflectionMethod, array(), false)) {
189194
$methodCalls[] = array($reflectionMethod->name, $arguments);
190195
}
191196
}
@@ -194,7 +199,7 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
194199
}
195200

196201
/**
197-
* Autowires the constructor or a setter.
202+
* Autowires the constructor or a method.
198203
*
199204
* @param \ReflectionMethod $reflectionMethod
200205
* @param array $arguments
@@ -204,7 +209,7 @@ private function autowireMethodCalls(\ReflectionClass $reflectionClass, array $m
204209
*
205210
* @throws RuntimeException
206211
*/
207-
private function autowireMethod(\ReflectionMethod $reflectionMethod, array $arguments, $mustAutowire)
212+
private function autowireMethodCall(\ReflectionMethod $reflectionMethod, array $arguments, $mustAutowire)
208213
{
209214
$didAutowire = false; // Whether any arguments have been autowired or not
210215
foreach ($reflectionMethod->getParameters() as $index => $parameter) {
@@ -298,6 +303,55 @@ private function autowireMethod(\ReflectionMethod $reflectionMethod, array $argu
298303
return $arguments;
299304
}
300305

306+
/**
307+
* Autowires getters.
308+
*
309+
* @param array $overridenGetters
310+
* @param array $autowiredMethods
311+
*
312+
* @return array
313+
*/
314+
private function autowireOverridenGetters(array $overridenGetters, array $autowiredMethods)
315+
{
316+
foreach ($autowiredMethods as $reflectionMethod) {
317+
if (isset($overridenGetters[strtolower($reflectionMethod->name)])
318+
|| !method_exists($reflectionMethod, 'getReturnType')
319+
|| 0 !== $reflectionMethod->getNumberOfParameters()
320+
|| $reflectionMethod->isFinal()
321+
|| $reflectionMethod->returnsReference()
322+
|| !$returnType = $reflectionMethod->getReturnType()
323+
) {
324+
continue;
325+
}
326+
$typeName = $returnType instanceof \ReflectionNamedType ? $returnType->getName() : $returnType->__toString();
327+
328+
if ($this->container->has($typeName) && !$this->container->findDefinition($typeName)->isAbstract()) {
329+
$overridenGetters[$reflectionMethod->name] = new Reference($typeName);
330+
continue;
331+
}
332+
333+
if (null === $this->types) {
334+
$this->populateAvailableTypes();
335+
}
336+
337+
if (isset($this->types[$typeName])) {
338+
$value = new Reference($this->types[$typeName]);
339+
} elseif ($returnType = $this->container->getReflectionClass($typeName, true)) {
340+
try {
341+
$value = $this->createAutowiredDefinition($returnType);
342+
} catch (RuntimeException $e) {
343+
continue;
344+
}
345+
} else {
346+
continue;
347+
}
348+
349+
$overridenGetters[$reflectionMethod->name] = $value;
350+
}
351+
352+
return $overridenGetters;
353+
}
354+
301355
/**
302356
* Populates the list of available types.
303357
*/

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 <[email protected]>
@@ -516,6 +517,31 @@ public function testExplicitMethodInjection()
516517
);
517518
}
518519

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

859890
class SetterInjectionCollision

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,8 @@ public function testDumpOverridenGettersWithConstructor()
354354

355355
$dump = $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Overriden_Getters_With_Constructor'));
356356
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dump_overriden_getters_with_constructor.php', $dump);
357-
$resources = array_map('strval', $container->getResources());
358-
$this->assertContains(realpath(self::$fixturesPath.'/containers/container_dump_overriden_getters_with_constructor.php'), $resources);
357+
$res = $container->getResources();
358+
$this->assertSame('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\Container34\Foo', (string) array_pop($res));
359359

360360
$baz = $container->get('baz');
361361
$r = new \ReflectionMethod($baz, 'getBaz');
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 <[email protected]>
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 <[email protected]>
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)