bug #40699 [PropertyInfo] Make ReflectionExtractor correctly extract nullability (shiftby)
This PR was squashed before being merged into the 4.4 branch.
Discussion
----------
[PropertyInfo] Make ReflectionExtractor correctly extract nullability
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | Fix #40659
| License | MIT
| Doc PR | no
When the property had a default value ReflectionExtractor was always returning isNullable: false. After PHP 7.4 we can get isNullable from the typehint.
Commits
-------
d5fce4c779
[PropertyInfo] Make ReflectionExtractor correctly extract nullability
This commit is contained in:
commit
fab61ee9df
@ -154,20 +154,8 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
|||||||
return $fromConstructor;
|
return $fromConstructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($fromDefaultValue = $this->extractFromDefaultValue($class, $property)) {
|
if ($fromPropertyDeclaration = $this->extractFromPropertyDeclaration($class, $property)) {
|
||||||
return $fromDefaultValue;
|
return $fromPropertyDeclaration;
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -312,10 +300,19 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function extractFromDefaultValue(string $class, string $property): ?array
|
private function extractFromPropertyDeclaration(string $class, string $property): ?array
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$reflectionClass = new \ReflectionClass($class);
|
$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) {
|
} catch (\ReflectionException $e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -328,7 +325,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
|||||||
|
|
||||||
$type = \gettype($defaultValue);
|
$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
|
private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionClass $declaringClass): array
|
||||||
@ -368,6 +365,25 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
|||||||
return $name;
|
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
|
private function isAllowedProperty(string $class, string $property): bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -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_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_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_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'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ class Php74Dummy
|
|||||||
private ?bool $nullableBoolProp;
|
private ?bool $nullableBoolProp;
|
||||||
/** @var string[] */
|
/** @var string[] */
|
||||||
private array $stringCollection;
|
private array $stringCollection;
|
||||||
|
private ?int $nullableWithDefault = 1;
|
||||||
|
|
||||||
public function addStringCollection(string $string): void
|
public function addStringCollection(string $string): void
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user