Skip to content

Commit

Permalink
Fail gracefully when attempting to autowire composite types
Browse files Browse the repository at this point in the history
  • Loading branch information
derrabus committed Jul 18, 2022
1 parent 90ef4f5 commit 9cc5694
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 3 deletions.
16 changes: 16 additions & 0 deletions LazyProxy/ProxyHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public static function getTypeHint(\ReflectionFunctionAbstract $r, \ReflectionPa
return null;
}

return self::getTypeHintForType($type, $r, $noBuiltin);
}

private static function getTypeHintForType(\ReflectionType $type, \ReflectionFunctionAbstract $r, bool $noBuiltin): ?string
{
$types = [];
$glue = '|';
if ($type instanceof \ReflectionUnionType) {
Expand All @@ -46,6 +51,17 @@ public static function getTypeHint(\ReflectionFunctionAbstract $r, \ReflectionPa
}

foreach ($reflectionTypes as $type) {
if ($type instanceof \ReflectionIntersectionType) {
$typeHint = self::getTypeHintForType($type, $r, $noBuiltin);
if (null === $typeHint) {
return null;
}

$types[] = sprintf('(%s)', $typeHint);

continue;
}

if ($type->isBuiltin()) {
if (!$noBuiltin) {
$types[] = $type->getName();
Expand Down
44 changes: 41 additions & 3 deletions Tests/Compiler/AutowirePassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,6 @@ public function testTypeNotGuessableNoServicesFound()
*/
public function testTypeNotGuessableUnionType()
{
$this->expectException(AutowiringFailedException::class);
$this->expectExceptionMessage('Cannot autowire service "a": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\UnionClasses::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionA|Symfony\Component\DependencyInjection\Tests\Compiler\CollisionB" but this class was not found.');
$container = new ContainerBuilder();

$container->register(CollisionA::class);
Expand All @@ -252,6 +250,9 @@ public function testTypeNotGuessableUnionType()
$aDefinition->setAutowired(true);

$pass = new AutowirePass();

$this->expectException(AutowiringFailedException::class);
$this->expectExceptionMessage('Cannot autowire service "a": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\UnionClasses::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionA|Symfony\Component\DependencyInjection\Tests\Compiler\CollisionB" but this class was not found.');
$pass->process($container);
}

Expand All @@ -260,17 +261,38 @@ public function testTypeNotGuessableUnionType()
*/
public function testTypeNotGuessableIntersectionType()
{
$container = new ContainerBuilder();

$container->register(CollisionInterface::class);
$container->register(AnotherInterface::class);

$aDefinition = $container->register('a', IntersectionClasses::class);
$aDefinition->setAutowired(true);

$pass = new AutowirePass();

$this->expectException(AutowiringFailedException::class);
$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.');
$pass->process($container);
}

/**
* @requires PHP 8.2
*/
public function testTypeNotGuessableCompositeType()
{
$container = new ContainerBuilder();

$container->register(CollisionInterface::class);
$container->register(AnotherInterface::class);

$aDefinition = $container->register('a', IntersectionClasses::class);
$aDefinition = $container->register('a', CompositeTypeClasses::class);
$aDefinition->setAutowired(true);

$pass = new AutowirePass();

$this->expectException(AutowiringFailedException::class);
$this->expectExceptionMessage('Cannot autowire service "a": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\CompositeTypeClasses::__construct()" has type "(Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface&Symfony\Component\DependencyInjection\Tests\Compiler\AnotherInterface)|Symfony\Component\DependencyInjection\Tests\Compiler\YetAnotherInterface" but this class was not found.');
$pass->process($container);
}

Expand Down Expand Up @@ -373,6 +395,22 @@ public function testParameterWithNullUnionIsSkipped()
$this->assertNull($definition->getArgument(0));
}

/**
* @requires PHP 8.2
*/
public function testParameterWithNullableIntersectionIsSkipped()
{
$container = new ContainerBuilder();

$optDefinition = $container->register('opt', NullableIntersection::class);
$optDefinition->setAutowired(true);

(new AutowirePass())->process($container);

$definition = $container->getDefinition('opt');
$this->assertNull($definition->getArgument(0));
}

/**
* @requires PHP 8
*/
Expand Down
3 changes: 3 additions & 0 deletions Tests/Fixtures/includes/autowiring_classes.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
if (\PHP_VERSION_ID >= 80100) {
require __DIR__.'/intersectiontype_classes.php';
}
if (\PHP_VERSION_ID >= 80200) {
require __DIR__.'/compositetype_classes.php';
}

class Foo
{
Expand Down
21 changes: 21 additions & 0 deletions Tests/Fixtures/includes/compositetype_classes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Compiler;

interface YetAnotherInterface
{
}

class CompositeTypeClasses
{
public function __construct((CollisionInterface&AnotherInterface)|YetAnotherInterface $collision)
{
}
}

class NullableIntersection
{
public function __construct((CollisionInterface&AnotherInterface)|null $a)
{
}
}

0 comments on commit 9cc5694

Please sign in to comment.