Skip to content

Commit 902ff16

Browse files
Merge branch '5.4' into 6.0
* 5.4: [HttpClient] fix RetryableHttpClient when a response is canceled Deprecate passing null as $requestIp to IpUtils::checkIp(), checkIp4() and checkIp6() [Uid] fix 4 missing bits of entropy in UUIDv4 Add a warning in WDT if using symfony/symfony [Notifier][Twilio] Ensure from/sender is valid via regex Lower log level in case of retry GuardEvent::getTransition() cannot return null [String] Add `trimSuffix()` and `trimPrefix()` methods [DependencyInjection] autowire union and intersection types [Runtime] Drop class validation of composer "extra.runtime.class"
2 parents f388f1f + 357dd3b commit 902ff16

File tree

4 files changed

+99
-4
lines changed

4 files changed

+99
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ CHANGELOG
1616
* Add `service_closure()` to the PHP-DSL
1717
* Add support for autoconfigurable attributes on methods, properties and parameters
1818
* Make auto-aliases private by default
19+
* Add support for autowiring union and intersection types
1920

2021
5.3
2122
---

Compiler/AutowirePass.php

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class AutowirePass extends AbstractRecursivePass
4545
private ?int $decoratedMethodIndex = null;
4646
private ?int $decoratedMethodArgumentIndex = null;
4747
private ?self $typesClone = null;
48+
private array $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

@@ -358,8 +362,12 @@ private function getAutowiredReference(TypedReference $reference): ?TypedReferen
358362
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
359363
}
360364

365+
if (null !== ($alias = $this->combinedAliases[$alias] ?? null) && !$this->container->findDefinition($alias)->isAbstract()) {
366+
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
367+
}
368+
361369
if ($this->container->has($name) && !$this->container->findDefinition($name)->isAbstract()) {
362-
foreach ($this->container->getAliases() as $id => $alias) {
370+
foreach ($this->container->getAliases() + $this->combinedAliases as $id => $alias) {
363371
if ($name === (string) $alias && str_starts_with($id, $type.' $')) {
364372
return new TypedReference($name, $type, $reference->getInvalidBehavior());
365373
}
@@ -371,6 +379,10 @@ private function getAutowiredReference(TypedReference $reference): ?TypedReferen
371379
return new TypedReference($type, $type, $reference->getInvalidBehavior());
372380
}
373381

382+
if (null !== ($alias = $this->combinedAliases[$type] ?? null) && !$this->container->findDefinition($alias)->isAbstract()) {
383+
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
384+
}
385+
374386
return null;
375387
}
376388

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

LazyProxy/ProxyHelper.php

Lines 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
}

Tests/Compiler/AutowirePassTest.php

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,13 +254,28 @@ public function testTypeNotGuessableUnionType()
254254
$pass->process($container);
255255
}
256256

257+
public function testGuessableUnionType()
258+
{
259+
$container = new ContainerBuilder();
260+
261+
$container->register('b', \stcClass::class);
262+
$container->setAlias(CollisionA::class.' $collision', 'b');
263+
$container->setAlias(CollisionB::class.' $collision', 'b');
264+
265+
$aDefinition = $container->register('a', UnionClasses::class);
266+
$aDefinition->setAutowired(true);
267+
268+
$pass = new AutowirePass();
269+
$pass->process($container);
270+
271+
$this->assertSame('b', (string) $aDefinition->getArgument(0));
272+
}
273+
257274
/**
258275
* @requires PHP 8.1
259276
*/
260277
public function testTypeNotGuessableIntersectionType()
261278
{
262-
$this->expectException(AutowiringFailedException::class);
263-
$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.');
264279
$container = new ContainerBuilder();
265280

266281
$container->register(CollisionInterface::class);
@@ -269,8 +284,32 @@ public function testTypeNotGuessableIntersectionType()
269284
$aDefinition = $container->register('a', IntersectionClasses::class);
270285
$aDefinition->setAutowired(true);
271286

287+
$pass = new AutowirePass();
288+
289+
$this->expectException(AutowiringFailedException::class);
290+
$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.');
291+
$pass->process($container);
292+
}
293+
294+
/**
295+
* @requires PHP 8.1
296+
*/
297+
public function testGuessableIntersectionType()
298+
{
299+
$container = new ContainerBuilder();
300+
301+
$container->register('b', \stcClass::class);
302+
$container->setAlias(CollisionInterface::class, 'b');
303+
$container->setAlias(AnotherInterface::class, 'b');
304+
$container->setAlias(DummyInterface::class, 'b');
305+
306+
$aDefinition = $container->register('a', IntersectionClasses::class);
307+
$aDefinition->setAutowired(true);
308+
272309
$pass = new AutowirePass();
273310
$pass->process($container);
311+
312+
$this->assertSame('b', (string) $aDefinition->getArgument(0));
274313
}
275314

276315
public function testTypeNotGuessableWithTypeSet()
@@ -503,7 +542,7 @@ public function testScalarArgsCannotBeAutowired()
503542
public function testUnionScalarArgsCannotBeAutowired()
504543
{
505544
$this->expectException(AutowiringFailedException::class);
506-
$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.');
545+
$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.');
507546
$container = new ContainerBuilder();
508547

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

0 commit comments

Comments
 (0)