Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f549c1b

Browse files
priyadinicolas-grekas
authored andcommitted
[PropertyAccess] Fixes getValue() on an unitialized object property on a lazy ghost
1 parent dd22c92 commit f549c1b

3 files changed

Lines changed: 83 additions & 2 deletions

File tree

PropertyAccessor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
423423
}
424424
} catch (\Error $e) {
425425
// handle uninitialized properties in PHP >= 7.4
426-
if (preg_match('/^Typed property ([\w\\\\@]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
426+
if (preg_match('/^Typed property ([\w\\\\@]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches) || preg_match('/^Cannot access uninitialized non-nullable property ([\w\\\\@]+)::\$(\w+) by reference$/', $e->getMessage(), $matches)) {
427427
$r = new \ReflectionProperty(str_contains($matches[1], '@anonymous') ? $class : $matches[1], $matches[2]);
428428
$type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
429429

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\PropertyAccess\Tests\Fixtures;
13+
14+
class UninitializedObjectProperty
15+
{
16+
public \DateTimeInterface $uninitialized;
17+
private \DateTimeInterface $privateUninitialized;
18+
19+
public function getPrivateUninitialized(): string
20+
{
21+
return $this->privateUninitialized;
22+
}
23+
24+
public function setPrivateUninitialized(string $privateUninitialized): void
25+
{
26+
$this->privateUninitialized = $privateUninitialized;
27+
}
28+
}

Tests/PropertyAccessorTest.php

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@
3737
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestSingularAndPluralProps;
3838
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
3939
use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
40+
use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedObjectProperty;
4041
use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty;
4142
use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty;
43+
use Symfony\Component\VarExporter\ProxyHelper;
4244

4345
class PropertyAccessorTest extends TestCase
4446
{
@@ -225,7 +227,8 @@ public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAn
225227
$this->expectException(UninitializedPropertyException::class);
226228
$this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
227229

228-
$object = new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {};
230+
$object = new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {
231+
};
229232

230233
$this->propertyAccessor->getValue($object, 'uninitialized');
231234
}
@@ -958,4 +961,54 @@ public function testCastDateTimeImmutable()
958961

959962
$this->assertInstanceOf(\DateTime::class, $object->getDate());
960963
}
964+
965+
public function testGetValuePropertyThrowsExceptionIfUninitializedWithoutLazyGhost()
966+
{
967+
$this->expectException(UninitializedPropertyException::class);
968+
$this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedObjectProperty::$uninitialized" is not readable because it is typed "DateTimeInterface". You should initialize it or declare a default value instead.');
969+
970+
$this->propertyAccessor->getValue(new UninitializedObjectProperty(), 'uninitialized');
971+
}
972+
973+
public function testGetValueGetterThrowsExceptionIfUninitializedWithoutLazyGhost()
974+
{
975+
$this->expectException(UninitializedPropertyException::class);
976+
$this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedObjectProperty::$privateUninitialized" is not readable because it is typed "DateTimeInterface". You should initialize it or declare a default value instead.');
977+
978+
$this->propertyAccessor->getValue(new UninitializedObjectProperty(), 'privateUninitialized');
979+
}
980+
981+
private function createUninitializedObjectPropertyGhost(): UninitializedObjectProperty
982+
{
983+
$class = 'UninitializedObjectPropertyGhost';
984+
985+
if (!class_exists($class)) {
986+
eval('class '.$class.ProxyHelper::generateLazyGhost(new \ReflectionClass(UninitializedObjectProperty::class)));
987+
}
988+
989+
$this->assertTrue(class_exists($class));
990+
991+
return $class::createLazyGhost(initializer: function ($instance) {
992+
});
993+
}
994+
995+
public function testGetValuePropertyThrowsExceptionIfUninitializedWithLazyGhost()
996+
{
997+
$this->expectException(UninitializedPropertyException::class);
998+
$this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedObjectProperty::$uninitialized" is not readable because it is typed "DateTimeInterface". You should initialize it or declare a default value instead.');
999+
1000+
$lazyGhost = $this->createUninitializedObjectPropertyGhost();
1001+
1002+
$this->propertyAccessor->getValue($lazyGhost, 'uninitialized');
1003+
}
1004+
1005+
public function testGetValueGetterThrowsExceptionIfUninitializedWithLazyGhost()
1006+
{
1007+
$this->expectException(UninitializedPropertyException::class);
1008+
$this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedObjectProperty::$privateUninitialized" is not readable because it is typed "DateTimeInterface". You should initialize it or declare a default value instead.');
1009+
1010+
$lazyGhost = $this->createUninitializedObjectPropertyGhost();
1011+
1012+
$this->propertyAccessor->getValue($lazyGhost, 'privateUninitialized');
1013+
}
9611014
}

0 commit comments

Comments
 (0)