From ea262441e7a9ab426ed0eaf2213ea5ee57539741 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 9 Jul 2020 22:22:50 +0200 Subject: [PATCH] [DependencyInjection] Add the Required attribute. --- .github/patch-types.php | 1 + .../Compiler/AutowireRequiredMethodsPass.php | 9 ++++ .../AutowireRequiredPropertiesPass.php | 8 ++-- .../Tests/Compiler/AutowirePassTest.php | 27 +++++++++++ .../AutowireRequiredMethodsPassTest.php | 46 +++++++++++++++++++ .../AutowireRequiredPropertiesPassTest.php | 25 ++++++++++ .../Fixtures/includes/autowiring_classes.php | 1 + .../includes/autowiring_classes_80.php | 28 +++++++++++ src/Symfony/Contracts/Cache/composer.json | 2 +- .../Contracts/Deprecation/composer.json | 2 +- .../Contracts/EventDispatcher/composer.json | 2 +- .../Contracts/HttpClient/composer.json | 2 +- .../Contracts/Service/Attribute/Required.php | 27 +++++++++++ src/Symfony/Contracts/Service/composer.json | 2 +- .../Contracts/Translation/composer.json | 2 +- src/Symfony/Contracts/composer.json | 2 +- 16 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php create mode 100644 src/Symfony/Contracts/Service/Attribute/Required.php diff --git a/.github/patch-types.php b/.github/patch-types.php index 70fea35aaae3e..eaf983e08d7f1 100644 --- a/.github/patch-types.php +++ b/.github/patch-types.php @@ -22,6 +22,7 @@ case false !== strpos($file, '/src/Symfony/Component/Debug/Tests/Fixtures/'): case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php'): case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php'): + case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php'): case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/uniontype_classes.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'): diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php index 2c774f781371c..fc1027677d41c 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Contracts\Service\Attribute\Required; /** * Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters. @@ -49,6 +50,14 @@ protected function processValue($value, bool $isRoot = false) } while (true) { + if (\PHP_VERSION_ID >= 80000 && $r->getAttributes(Required::class)) { + if ($this->isWither($r, $r->getDocComment() ?: '')) { + $withers[] = [$r->name, [], true]; + } else { + $value->addMethodCall($r->name, []); + } + break; + } if (false !== $doc = $r->getDocComment()) { if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) { if ($this->isWither($reflectionMethod, $doc)) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php index 24f9c41d2bf50..52024b8074556 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php @@ -14,6 +14,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Contracts\Service\Attribute\Required; /** * Looks for definitions with autowiring enabled and registers their corresponding "@required" properties. @@ -45,10 +46,9 @@ protected function processValue($value, bool $isRoot = false) if (!($type = $reflectionProperty->getType()) instanceof \ReflectionNamedType) { continue; } - if (false === $doc = $reflectionProperty->getDocComment()) { - continue; - } - if (false === stripos($doc, '@required') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) { + if ((\PHP_VERSION_ID < 80000 || !$reflectionProperty->getAttributes(Required::class)) + && ((false === $doc = $reflectionProperty->getDocComment()) || false === stripos($doc, '@required') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) + ) { continue; } if (\array_key_exists($name = $reflectionProperty->getName(), $properties)) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 69a0da2127b7d..bdcbf8d959868 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -28,6 +28,7 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic; use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\MultipleArgumentsOptionalScalarNotReallyOptional; use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Contracts\Service\Attribute\Required; require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; @@ -640,6 +641,32 @@ public function testSetterInjection() ); } + /** + * @requires PHP 8 + */ + public function testSetterInjectionWithAttribute() + { + if (!class_exists(Required::class)) { + $this->markTestSkipped('symfony/service-contracts 2.2 required'); + } + + $container = new ContainerBuilder(); + $container->register(Foo::class); + + $container + ->register('setter_injection', AutowireSetter::class) + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + (new AutowirePass())->process($container); + + $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); + $this->assertCount(1, $methodCalls); + $this->assertSame('setFoo', $methodCalls[0][0]); + $this->assertSame(Foo::class, (string) $methodCalls[0][1][0]); + } + public function testWithNonExistingSetterAndAutowiring() { $this->expectException(RuntimeException::class); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php index 742e53b76e954..4704d1920bbfb 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherStaticReturnType; +use Symfony\Contracts\Service\Attribute\Required; require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; @@ -54,6 +55,29 @@ public function testSetterInjection() $this->assertEquals([], $methodCalls[1][1]); } + /** + * @requires PHP 8 + */ + public function testSetterInjectionWithAttribute() + { + if (!class_exists(Required::class)) { + $this->markTestSkipped('symfony/service-contracts 2.2 required'); + } + + $container = new ContainerBuilder(); + $container->register(Foo::class); + + $container + ->register('setter_injection', AutowireSetter::class) + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + + $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); + $this->assertSame([['setFoo', []]], $methodCalls); + } + public function testExplicitMethodInjection() { $container = new ContainerBuilder(); @@ -124,4 +148,26 @@ public function testWitherWithStaticReturnTypeInjection() ]; $this->assertSame($expected, $methodCalls); } + + /** + * @requires PHP 8 + */ + public function testWitherInjectionWithAttribute() + { + if (!class_exists(Required::class)) { + $this->markTestSkipped('symfony/service-contracts 2.2 required'); + } + + $container = new ContainerBuilder(); + $container->register(Foo::class); + + $container + ->register('wither', AutowireWither::class) + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + + $this->assertSame([['withFoo', [], true]], $container->getDefinition('wither')->getMethodCalls()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php index 241daaaff3358..2de975faac2a2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredPropertiesPass; use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Contracts\Service\Attribute\Required; require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; @@ -43,4 +44,28 @@ public function testInjection() $this->assertArrayHasKey('plop', $properties); $this->assertEquals(Bar::class, (string) $properties['plop']); } + + /** + * @requires PHP 8 + */ + public function testAttribute() + { + if (!class_exists(Required::class)) { + $this->markTestSkipped('symfony/service-contracts 2.2 required'); + } + + $container = new ContainerBuilder(); + $container->register(Foo::class); + + $container->register('property_injection', AutowireProperty::class) + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredPropertiesPass())->process($container); + + $properties = $container->getDefinition('property_injection')->getProperties(); + + $this->assertArrayHasKey('foo', $properties); + $this->assertEquals(Foo::class, (string) $properties['foo']); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php index 2891d4307ca56..33cfdd9d9e403 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php @@ -6,6 +6,7 @@ if (PHP_VERSION_ID >= 80000) { require __DIR__.'/uniontype_classes.php'; + require __DIR__.'/autowiring_classes_80.php'; } class Foo diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php new file mode 100644 index 0000000000000..e22ae85169e5a --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service\Attribute; + +use Attribute; + +/** + * A required dependency. + * + * This attribute indicates that a property holds a required dependency. The annotated property or method should be + * considered during the instantiation process of the containing class. + * + * @author Alexander M. Turek + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY)] +final class Required +{ +} diff --git a/src/Symfony/Contracts/Service/composer.json b/src/Symfony/Contracts/Service/composer.json index bb1824d72d827..47244fbb1034a 100644 --- a/src/Symfony/Contracts/Service/composer.json +++ b/src/Symfony/Contracts/Service/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.2-dev" }, "thanks": { "name": "symfony/contracts", diff --git a/src/Symfony/Contracts/Translation/composer.json b/src/Symfony/Contracts/Translation/composer.json index 0295ce63ce297..2cef00fdd4caf 100644 --- a/src/Symfony/Contracts/Translation/composer.json +++ b/src/Symfony/Contracts/Translation/composer.json @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.2-dev" }, "thanks": { "name": "symfony/contracts", diff --git a/src/Symfony/Contracts/composer.json b/src/Symfony/Contracts/composer.json index 83e5db3996ab4..1ce94b46999c6 100644 --- a/src/Symfony/Contracts/composer.json +++ b/src/Symfony/Contracts/composer.json @@ -49,7 +49,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.2-dev" } } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy