Skip to content

Commit

Permalink
bug #35546 [Validator] check for __get method existence if property i…
Browse files Browse the repository at this point in the history
…s uninitialized (alekitto)

This PR was merged into the 3.4 branch.

Discussion
----------

[Validator] check for __get method existence if property is uninitialized

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #35544
| License       | MIT

Resolve bug #35544.

On PHP 7.4, check if object implements `__get` magic method if property is reported as uninitialized before returning null.

Commits
-------

427bc3a [Validator] try to call __get method if property is uninitialized
  • Loading branch information
fabpot committed Feb 3, 2020
2 parents af46fd6 + 427bc3a commit ef4dcdb
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 2 deletions.
17 changes: 15 additions & 2 deletions src/Symfony/Component/Validator/Mapping/PropertyMetadata.php
Expand Up @@ -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);
Expand Down
18 changes: 18 additions & 0 deletions src/Symfony/Component/Validator/Tests/Fixtures/Entity_74_Proxy.php
@@ -0,0 +1,18 @@
<?php

namespace Symfony\Component\Validator\Tests\Fixtures;

class Entity_74_Proxy extends Entity_74
{
public string $notUnset;

public function __construct()
{
unset($this->uninitialized);
}

public function __get($name)
{
return 42;
}
}
Expand Up @@ -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()
Expand Down Expand Up @@ -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));
}
}

0 comments on commit ef4dcdb

Please sign in to comment.