diff --git a/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php b/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php index 872bd067be2b..1adad95b8095 100644 --- a/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php @@ -50,8 +50,21 @@ public function getPropertyValue($object) { $reflProperty = $this->getReflectionMember($object); - if (\PHP_VERSION_ID >= 70400 && !$reflProperty->isInitialized($object)) { - return null; + if (\PHP_VERSION_ID >= 70400 && $reflProperty->hasType() && !$reflProperty->isInitialized($object)) { + // There is no way to check if a property has been unset or if it is uninitialized. + // When trying to access an uninitialized property, __get method is triggered. + + // If __get method is not present, no fallback is possible + // Otherwise we need to catch an Error in case we are trying to access an uninitialized but set property. + if (!method_exists($object, '__get')) { + return null; + } + + try { + return $reflProperty->getValue($object); + } catch (\Error $e) { + return null; + } } return $reflProperty->getValue($object); diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/Entity_74_Proxy.php b/src/Symfony/Component/Validator/Tests/Fixtures/Entity_74_Proxy.php new file mode 100644 index 000000000000..d74badc7d7f9 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/Entity_74_Proxy.php @@ -0,0 +1,18 @@ +uninitialized); + } + + public function __get($name) + { + return 42; + } +} diff --git a/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php b/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php index 8d9e67881a9d..8868ec64aac9 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php @@ -15,11 +15,13 @@ use Symfony\Component\Validator\Mapping\PropertyMetadata; use Symfony\Component\Validator\Tests\Fixtures\Entity; use Symfony\Component\Validator\Tests\Fixtures\Entity_74; +use Symfony\Component\Validator\Tests\Fixtures\Entity_74_Proxy; class PropertyMetadataTest extends TestCase { const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; const CLASSNAME_74 = 'Symfony\Component\Validator\Tests\Fixtures\Entity_74'; + const CLASSNAME_74_PROXY = 'Symfony\Component\Validator\Tests\Fixtures\Entity_74_Proxy'; const PARENTCLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent'; public function testInvalidPropertyName() @@ -66,4 +68,17 @@ public function testGetPropertyValueFromUninitializedProperty() $this->assertNull($metadata->getPropertyValue($entity)); } + + /** + * @requires PHP 7.4 + */ + public function testGetPropertyValueFromUninitializedPropertyShouldNotReturnNullIfMagicGetIsPresent() + { + $entity = new Entity_74_Proxy(); + $metadata = new PropertyMetadata(self::CLASSNAME_74_PROXY, 'uninitialized'); + $notUnsetMetadata = new PropertyMetadata(self::CLASSNAME_74_PROXY, 'notUnset'); + + $this->assertNull($notUnsetMetadata->getPropertyValue($entity)); + $this->assertEquals(42, $metadata->getPropertyValue($entity)); + } }