Skip to content

Navigation Menu

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 21528c6

Browse filesBrowse files
committed
feature #43479 [DependencyInjection] autowire union and intersection types (nicolas-grekas)
This PR was merged into the 5.4 branch. Discussion ---------- [DependencyInjection] autowire union and intersection types | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | #43325 | License | MIT | Doc PR | - This PR allows autowiring an argument of type `NormalizerInterface&DenormalizerInterface` if both individual types have a corresponding autowiring alias, and if both aliases point to the very same service. This works the same with union types. Commits ------- aba31f9 [DependencyInjection] autowire union and intersection types
2 parents a61110e + aba31f9 commit 21528c6
Copy full SHA for 21528c6

File tree

4 files changed

+102
-6
lines changed
Filter options

4 files changed

+102
-6
lines changed

‎src/Symfony/Component/DependencyInjection/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Add `service_closure()` to the PHP-DSL
88
* Add support for autoconfigurable attributes on methods, properties and parameters
99
* Make auto-aliases private by default
10+
* Add support for autowiring union and intersection types
1011

1112
5.3
1213
---

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+54-3Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class AutowirePass extends AbstractRecursivePass
4545
private $decoratedMethodIndex;
4646
private $decoratedMethodArgumentIndex;
4747
private $typesClone;
48+
private $combinedAliases;
4849

4950
public function __construct(bool $throwOnAutowireException = true)
5051
{
@@ -60,6 +61,8 @@ public function __construct(bool $throwOnAutowireException = true)
6061
*/
6162
public function process(ContainerBuilder $container)
6263
{
64+
$this->populateCombinedAliases($container);
65+
6366
try {
6467
$this->typesClone = clone $this;
6568
parent::process($container);
@@ -72,6 +75,7 @@ public function process(ContainerBuilder $container)
7275
$this->decoratedMethodIndex = null;
7376
$this->decoratedMethodArgumentIndex = null;
7477
$this->typesClone = null;
78+
$this->combinedAliases = [];
7579
}
7680
}
7781

@@ -223,8 +227,6 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot,
223227
/**
224228
* Autowires the constructor or a method.
225229
*
226-
* @return array
227-
*
228230
* @throws AutowiringFailedException
229231
*/
230232
private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes, int $methodIndex): array
@@ -363,8 +365,12 @@ private function getAutowiredReference(TypedReference $reference): ?TypedReferen
363365
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
364366
}
365367

368+
if (null !== ($alias = $this->combinedAliases[$alias] ?? null) && !$this->container->findDefinition($alias)->isAbstract()) {
369+
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
370+
}
371+
366372
if ($this->container->has($name) && !$this->container->findDefinition($name)->isAbstract()) {
367-
foreach ($this->container->getAliases() as $id => $alias) {
373+
foreach ($this->container->getAliases() + $this->combinedAliases as $id => $alias) {
368374
if ($name === (string) $alias && str_starts_with($id, $type.' $')) {
369375
return new TypedReference($name, $type, $reference->getInvalidBehavior());
370376
}
@@ -376,6 +382,10 @@ private function getAutowiredReference(TypedReference $reference): ?TypedReferen
376382
return new TypedReference($type, $type, $reference->getInvalidBehavior());
377383
}
378384

385+
if (null !== ($alias = $this->combinedAliases[$type] ?? null) && !$this->container->findDefinition($alias)->isAbstract()) {
386+
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
387+
}
388+
379389
return null;
380390
}
381391

@@ -565,4 +575,45 @@ private function populateAutowiringAlias(string $id): void
565575
$this->autowiringAliases[$type][$name] = $name;
566576
}
567577
}
578+
579+
private function populateCombinedAliases(ContainerBuilder $container): void
580+
{
581+
$this->combinedAliases = [];
582+
$reverseAliases = [];
583+
584+
foreach ($container->getAliases() as $id => $alias) {
585+
if (!preg_match('/(?(DEFINE)(?<V>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+))^((?&V)(?:\\\\(?&V))*+)(?: \$((?&V)))?$/', $id, $m)) {
586+
continue;
587+
}
588+
589+
$type = $m[2];
590+
$name = $m[3] ?? '';
591+
$reverseAliases[(string) $alias][$name][] = $type;
592+
}
593+
594+
foreach ($reverseAliases as $alias => $names) {
595+
foreach ($names as $name => $types) {
596+
if (2 > $count = \count($types)) {
597+
continue;
598+
}
599+
sort($types);
600+
$i = 1 << $count;
601+
602+
// compute the powerset of the list of types
603+
while ($i--) {
604+
$set = [];
605+
for ($j = 0; $j < $count; ++$j) {
606+
if ($i & (1 << $j)) {
607+
$set[] = $types[$j];
608+
}
609+
}
610+
611+
if (2 <= \count($set)) {
612+
$this->combinedAliases[implode('&', $set).('' === $name ? '' : ' $'.$name)] = $alias;
613+
$this->combinedAliases[implode('|', $set).('' === $name ? '' : ' $'.$name)] = $alias;
614+
}
615+
}
616+
}
617+
}
618+
}
568619
}

‎src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ public static function getTypeHint(\ReflectionFunctionAbstract $r, \ReflectionPa
7070
}
7171
}
7272

73+
sort($types);
74+
7375
return $types ? implode($glue, $types) : null;
7476
}
7577
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
+45-3Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,31 @@ public function testTypeNotGuessableUnionType()
258258
$pass->process($container);
259259
}
260260

261+
/**
262+
* @requires PHP 8
263+
*/
264+
public function testGuessableUnionType()
265+
{
266+
$container = new ContainerBuilder();
267+
268+
$container->register('b', \stcClass::class);
269+
$container->setAlias(CollisionA::class.' $collision', 'b');
270+
$container->setAlias(CollisionB::class.' $collision', 'b');
271+
272+
$aDefinition = $container->register('a', UnionClasses::class);
273+
$aDefinition->setAutowired(true);
274+
275+
$pass = new AutowirePass();
276+
$pass->process($container);
277+
278+
$this->assertSame('b', (string) $aDefinition->getArgument(0));
279+
}
280+
261281
/**
262282
* @requires PHP 8.1
263283
*/
264284
public function testTypeNotGuessableIntersectionType()
265285
{
266-
$this->expectException(AutowiringFailedException::class);
267-
$this->expectExceptionMessage('Cannot autowire service "a": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\IntersectionClasses::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface&Symfony\Component\DependencyInjection\Tests\Compiler\AnotherInterface" but this class was not found.');
268286
$container = new ContainerBuilder();
269287

270288
$container->register(CollisionInterface::class);
@@ -273,8 +291,32 @@ public function testTypeNotGuessableIntersectionType()
273291
$aDefinition = $container->register('a', IntersectionClasses::class);
274292
$aDefinition->setAutowired(true);
275293

294+
$pass = new AutowirePass();
295+
296+
$this->expectException(AutowiringFailedException::class);
297+
$this->expectExceptionMessage('Cannot autowire service "a": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\IntersectionClasses::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\AnotherInterface&Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" but this class was not found.');
298+
$pass->process($container);
299+
}
300+
301+
/**
302+
* @requires PHP 8.1
303+
*/
304+
public function testGuessableIntersectionType()
305+
{
306+
$container = new ContainerBuilder();
307+
308+
$container->register('b', \stcClass::class);
309+
$container->setAlias(CollisionInterface::class, 'b');
310+
$container->setAlias(AnotherInterface::class, 'b');
311+
$container->setAlias(DummyInterface::class, 'b');
312+
313+
$aDefinition = $container->register('a', IntersectionClasses::class);
314+
$aDefinition->setAutowired(true);
315+
276316
$pass = new AutowirePass();
277317
$pass->process($container);
318+
319+
$this->assertSame('b', (string) $aDefinition->getArgument(0));
278320
}
279321

280322
public function testTypeNotGuessableWithTypeSet()
@@ -516,7 +558,7 @@ public function testScalarArgsCannotBeAutowired()
516558
public function testUnionScalarArgsCannotBeAutowired()
517559
{
518560
$this->expectException(AutowiringFailedException::class);
519-
$this->expectExceptionMessage('Cannot autowire service "union_scalars": argument "$timeout" of method "Symfony\Component\DependencyInjection\Tests\Compiler\UnionScalars::__construct()" is type-hinted "int|float", you should configure its value explicitly.');
561+
$this->expectExceptionMessage('Cannot autowire service "union_scalars": argument "$timeout" of method "Symfony\Component\DependencyInjection\Tests\Compiler\UnionScalars::__construct()" is type-hinted "float|int", you should configure its value explicitly.');
520562
$container = new ContainerBuilder();
521563

522564
$container->register('union_scalars', UnionScalars::class)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.