From d5fce4c77903aa843fb3dac6956853555a979319 Mon Sep 17 00:00:00 2001 From: Maxim Dovydenok Date: Sat, 3 Apr 2021 16:47:18 +0300 Subject: [PATCH] [PropertyInfo] Make ReflectionExtractor correctly extract nullability --- .../Extractor/ReflectionExtractor.php | 48 ++++++++++++------- .../Extractor/ReflectionExtractorTest.php | 1 + .../Tests/Fixtures/Php74Dummy.php | 1 + 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index c0f59cea9a..ebe9d07032 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -154,20 +154,8 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp return $fromConstructor; } - if ($fromDefaultValue = $this->extractFromDefaultValue($class, $property)) { - return $fromDefaultValue; - } - - if (\PHP_VERSION_ID >= 70400) { - try { - $reflectionProperty = new \ReflectionProperty($class, $property); - $type = $reflectionProperty->getType(); - if (null !== $type && $types = $this->extractFromReflectionType($type, $reflectionProperty->getDeclaringClass())) { - return $types; - } - } catch (\ReflectionException $e) { - // noop - } + if ($fromPropertyDeclaration = $this->extractFromPropertyDeclaration($class, $property)) { + return $fromPropertyDeclaration; } return null; @@ -312,10 +300,19 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp return null; } - private function extractFromDefaultValue(string $class, string $property): ?array + private function extractFromPropertyDeclaration(string $class, string $property): ?array { try { $reflectionClass = new \ReflectionClass($class); + + if (\PHP_VERSION_ID >= 70400) { + $reflectionProperty = $reflectionClass->getProperty($property); + $reflectionPropertyType = $reflectionProperty->getType(); + + if (null !== $reflectionPropertyType && $types = $this->extractFromReflectionType($reflectionPropertyType, $reflectionProperty->getDeclaringClass())) { + return $types; + } + } } catch (\ReflectionException $e) { return null; } @@ -328,7 +325,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp $type = \gettype($defaultValue); - return [new Type(static::MAP_TYPES[$type] ?? $type)]; + return [new Type(static::MAP_TYPES[$type] ?? $type, $this->isNullableProperty($class, $property))]; } private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionClass $declaringClass): array @@ -368,6 +365,25 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp return $name; } + private function isNullableProperty(string $class, string $property): bool + { + try { + $reflectionProperty = new \ReflectionProperty($class, $property); + + if (\PHP_VERSION_ID >= 70400) { + $reflectionPropertyType = $reflectionProperty->getType(); + + return null !== $reflectionPropertyType && $reflectionPropertyType->allowsNull(); + } + + return false; + } catch (\ReflectionException $e) { + // Return false if the property doesn't exist + } + + return false; + } + private function isAllowedProperty(string $class, string $property): bool { try { diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index 18f8d4d018..60b4efdbcf 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -414,5 +414,6 @@ class ReflectionExtractorTest extends TestCase $this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)], $this->extractor->getTypes(Php74Dummy::class, 'dummy')); $this->assertEquals([new Type(Type::BUILTIN_TYPE_BOOL, true)], $this->extractor->getTypes(Php74Dummy::class, 'nullableBoolProp')); $this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))], $this->extractor->getTypes(Php74Dummy::class, 'stringCollection')); + $this->assertEquals([new Type(Type::BUILTIN_TYPE_INT, true)], $this->extractor->getTypes(Php74Dummy::class, 'nullableWithDefault')); } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php74Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php74Dummy.php index ffc4f4cd37..405043252a 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php74Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php74Dummy.php @@ -20,6 +20,7 @@ class Php74Dummy private ?bool $nullableBoolProp; /** @var string[] */ private array $stringCollection; + private ?int $nullableWithDefault = 1; public function addStringCollection(string $string): void {