diff --git a/.github/patch-types.php b/.github/patch-types.php index d9b1ed98f2..f1ec07f725 100644 --- a/.github/patch-types.php +++ b/.github/patch-types.php @@ -23,6 +23,7 @@ foreach ($loader->getClassMap() as $class => $file) { case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php'): case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ParentNotExists.php'): case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/BadClasses/MissingParent.php'): + case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherStaticReturnType.php'): case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/'): case false !== strpos($file, '/src/Symfony/Component/ErrorHandler/Tests/Fixtures/'): case false !== strpos($file, '/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php'): diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 72626da76a..f14c71bfbe 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -20,6 +20,7 @@ CHANGELOG * deprecated `Definition::getDeprecationMessage()`, use `Definition::getDeprecation()` instead * deprecated `Alias::getDeprecationMessage()`, use `Alias::getDeprecation()` instead * deprecated PHP-DSL's `inline()` function, use `service()` instead + * added support of PHP8 static return type for withers 5.0.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php index c46d71f206..2c774f7813 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php @@ -51,7 +51,7 @@ class AutowireRequiredMethodsPass extends AbstractRecursivePass while (true) { if (false !== $doc = $r->getDocComment()) { if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) { - if (preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@return\s++static[\s\*]#i', $doc)) { + if ($this->isWither($reflectionMethod, $doc)) { $withers[] = [$reflectionMethod->name, [], true]; } else { $value->addMethodCall($reflectionMethod->name, []); @@ -81,4 +81,20 @@ class AutowireRequiredMethodsPass extends AbstractRecursivePass return $value; } + + private function isWither(\ReflectionMethod $reflectionMethod, string $doc): bool + { + $match = preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@return\s++(static|\$this)[\s\*]#i', $doc, $matches); + if ($match && 'static' === $matches[1]) { + return true; + } + + if ($match && '$this' === $matches[1]) { + return false; + } + + $reflectionType = $reflectionMethod->hasReturnType() ? $reflectionMethod->getReturnType() : null; + + return $reflectionType instanceof \ReflectionNamedType && 'static' === $reflectionType->getName(); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php index 653e27ea53..742e53b76e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherStaticReturnType; require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; @@ -99,4 +100,28 @@ class AutowireRequiredMethodsPassTest extends TestCase ]; $this->assertSame($expected, $methodCalls); } + + /** + * @requires PHP 8 + */ + public function testWitherWithStaticReturnTypeInjection() + { + $container = new ContainerBuilder(); + $container->register(Foo::class); + + $container + ->register('wither', WitherStaticReturnType::class) + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + + $methodCalls = $container->getDefinition('wither')->getMethodCalls(); + + $expected = [ + ['withFoo', [], true], + ['setFoo', []], + ]; + $this->assertSame($expected, $methodCalls); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 4f935fd219..14cca64920 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -46,6 +46,7 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition; use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument; use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory; use Symfony\Component\DependencyInjection\Tests\Fixtures\SimilarArgumentsDummy; +use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherStaticReturnType; use Symfony\Component\DependencyInjection\TypedReference; use Symfony\Component\ExpressionLanguage\Expression; @@ -1624,6 +1625,25 @@ class ContainerBuilderTest extends TestCase $this->assertInstanceOf(Foo::class, $wither->foo); } + /** + * @requires PHP 8 + */ + public function testWitherWithStaticReturnType() + { + $container = new ContainerBuilder(); + $container->register(Foo::class); + + $container + ->register('wither', WitherStaticReturnType::class) + ->setPublic(true) + ->setAutowired(true); + + $container->compile(); + + $wither = $container->get('wither'); + $this->assertInstanceOf(Foo::class, $wither->foo); + } + public function testAutoAliasing() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 2958a29b2a..108f5ad443 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -42,6 +42,7 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory; use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator; use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1; use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber; +use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherStaticReturnType; use Symfony\Component\DependencyInjection\TypedReference; use Symfony\Component\DependencyInjection\Variable; use Symfony\Component\ExpressionLanguage\Expression; @@ -1362,6 +1363,31 @@ class PhpDumperTest extends TestCase $this->assertInstanceOf(Foo::class, $wither->foo); } + /** + * @requires PHP 8 + */ + public function testWitherWithStaticReturnType() + { + $container = new ContainerBuilder(); + $container->register(Foo::class); + + $container + ->register('wither', WitherStaticReturnType::class) + ->setPublic(true) + ->setAutowired(true); + + $container->compile(); + $dumper = new PhpDumper($container); + $dump = $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Service_WitherStaticReturnType']); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_wither_staticreturntype.php', $dump); + eval('?>'.$dump); + + $container = new \Symfony_DI_PhpDumper_Service_WitherStaticReturnType(); + + $wither = $container->get('wither'); + $this->assertInstanceOf(Foo::class, $wither->foo); + } + /** * @group legacy */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherStaticReturnType.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherStaticReturnType.php new file mode 100644 index 0000000000..5a4d9840d3 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherStaticReturnType.php @@ -0,0 +1,30 @@ +foo = $foo; + + return $new; + } + + /** + * @required + * @return $this + */ + public function setFoo(Foo $foo): static + { + $this->foo = $foo; + + return $this; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_staticreturntype.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_staticreturntype.php new file mode 100644 index 0000000000..85ba3bbb1b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_staticreturntype.php @@ -0,0 +1,64 @@ +services = $this->privates = []; + $this->methodMap = [ + 'wither' => 'getWitherService', + ]; + + $this->aliases = []; + } + + public function compile(): void + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled(): bool + { + return true; + } + + public function getRemovedIds(): array + { + return [ + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo' => true, + ]; + } + + /** + * Gets the public 'wither' shared autowired service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Compiler\WitherStaticReturnType + */ + protected function getWitherService() + { + $instance = new \Symfony\Component\DependencyInjection\Tests\Compiler\WitherStaticReturnType(); + + $a = new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo(); + + $this->services['wither'] = $instance = $instance->withFoo($a); + $instance->setFoo($a); + + return $instance; + } +}