From 9ef7b48c2576260fc997883c1053f6b8d734553c Mon Sep 17 00:00:00 2001 From: Benjamin Zikarsky Date: Wed, 18 Mar 2020 14:25:56 +0100 Subject: [PATCH] Add autowiring-support for PHP7.4 typed properties The current implementation prioritizes `@var` and the `name` property of the `@Inject` annotation over the type of the property. This allows to still inject custom-named entries into the properties even in the presence of types. --- .../Source/AnnotationBasedAutowiring.php | 12 +++++- .../Source/AnnotationBasedAutowiringTest.php | 38 ++++++++++++++++++- .../AnnotationFixtureScalarTypedProperty.php | 13 +++++++ .../AnnotationFixtureTypedProperties.php | 26 +++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 tests/UnitTest/Definition/Source/Fixtures/AnnotationFixtureScalarTypedProperty.php create mode 100644 tests/UnitTest/Definition/Source/Fixtures/AnnotationFixtureTypedProperties.php diff --git a/src/Definition/Source/AnnotationBasedAutowiring.php b/src/Definition/Source/AnnotationBasedAutowiring.php index fc0b905cc..e576734c5 100644 --- a/src/Definition/Source/AnnotationBasedAutowiring.php +++ b/src/Definition/Source/AnnotationBasedAutowiring.php @@ -18,6 +18,7 @@ use PhpDocReader\PhpDocReader; use ReflectionClass; use ReflectionMethod; +use ReflectionNamedType; use ReflectionParameter; use ReflectionProperty; use UnexpectedValueException; @@ -125,9 +126,18 @@ private function readProperty(ReflectionProperty $property, ObjectDefinition $de return; } - // @Inject("name") or look for @var content + // Try to @Inject("name") or look for @var content $entryName = $annotation->getName() ?: $this->getPhpDocReader()->getPropertyClass($property); + // Try using PHP7.4 typed properties + if (\PHP_VERSION_ID > 70400 + && $entryName === null + && $property->getType() instanceof ReflectionNamedType + && (class_exists($property->getType()->getName()) || interface_exists($property->getType()->getName())) + ) { + $entryName = $property->getType()->getName(); + } + if ($entryName === null) { throw new InvalidAnnotation(sprintf( '@Inject found on property %s::%s but unable to guess what to inject, use a @var annotation', diff --git a/tests/UnitTest/Definition/Source/AnnotationBasedAutowiringTest.php b/tests/UnitTest/Definition/Source/AnnotationBasedAutowiringTest.php index bf12bc713..00af08b19 100644 --- a/tests/UnitTest/Definition/Source/AnnotationBasedAutowiringTest.php +++ b/tests/UnitTest/Definition/Source/AnnotationBasedAutowiringTest.php @@ -16,6 +16,8 @@ use DI\Test\UnitTest\Definition\Source\Fixtures\AnnotationFixture4; use DI\Test\UnitTest\Definition\Source\Fixtures\AnnotationFixture5; use DI\Test\UnitTest\Definition\Source\Fixtures\AnnotationFixtureChild; +use DI\Test\UnitTest\Definition\Source\Fixtures\AnnotationFixtureScalarTypedProperty; +use DI\Test\UnitTest\Definition\Source\Fixtures\AnnotationFixtureTypedProperties; use DI\Test\UnitTest\Definition\Source\Fixtures\AnnotationInjectableFixture; use PHPUnit\Framework\TestCase; @@ -63,6 +65,30 @@ public function testUnguessableProperty() (new AnnotationBasedAutowiring)->autowire(AnnotationFixture4::class); } + public function testTypedProperty() + { + if (PHP_VERSION_ID < 70400) { + $this->markTestSkipped('Typed properties support requires PHP7.4'); + } + + $definition = (new AnnotationBasedAutowiring)->autowire(AnnotationFixtureTypedProperties::class); + + $this->assertNotHasPropertyInjection($definition, 'typeAndNoInject'); + $this->assertHasPropertyInjection($definition, 'typedAndInject', AnnotationFixture2::class); + $this->assertHasPropertyInjection($definition, 'typedAndVar', AnnotationFixture3::class); + $this->assertHasPropertyInjection($definition, 'typedAndNamed', 'name'); + } + + public function testScalarTypedPropertiesFail() + { + if (PHP_VERSION_ID < 70400) { + $this->markTestSkipped('Typed properties support requires PHP7.4'); + } + + $this->expectException(\DI\Definition\Exception\InvalidAnnotation::class); + (new AnnotationBasedAutowiring)->autowire(AnnotationFixtureScalarTypedProperty::class); + } + public function testConstructor() { $definition = (new AnnotationBasedAutowiring)->autowire(AnnotationFixture::class); @@ -254,11 +280,21 @@ private function getMethodInjection(ObjectDefinition $definition, $name) return null; } - private function assertHasPropertyInjection(ObjectDefinition $definition, $propertyName) + private function assertHasPropertyInjection(ObjectDefinition $definition, $propertyName, ?string $expectedType = null) { $propertyInjections = $definition->getPropertyInjections(); foreach ($propertyInjections as $propertyInjection) { if ($propertyInjection->getPropertyName() === $propertyName) { + + if ($expectedType !== null) { + $this->assertInstanceOf(Reference::class, $propertyInjection->getValue()); + $this->assertEquals( + $expectedType, + $propertyInjection->getValue()->getTargetEntryName(), + 'Property injected with the right type' + ); + } + return; } } diff --git a/tests/UnitTest/Definition/Source/Fixtures/AnnotationFixtureScalarTypedProperty.php b/tests/UnitTest/Definition/Source/Fixtures/AnnotationFixtureScalarTypedProperty.php new file mode 100644 index 000000000..7bf7f9dc8 --- /dev/null +++ b/tests/UnitTest/Definition/Source/Fixtures/AnnotationFixtureScalarTypedProperty.php @@ -0,0 +1,13 @@ +