Skip to content

Commit 931de58

Browse files
bug #53891 [PropertyAccess] Fixes getValue() on an unitialized object property on a lazy ghost (priyadi)
This PR was squashed before being merged into the 6.4 branch. Discussion ---------- [PropertyAccess] Fixes getValue() on an unitialized object property on a lazy ghost | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | | License | MIT With a lazy ghost that has an uninitialized property that should contain an object, attempting to `$propertyAccessor->getValue()` on it will previously throw `Error`, not the expected `UninitializedPropertyException`. This PR fixes the problem. Commits ------- cae256b [PropertyAccess] Fixes getValue() on an unitialized object property on a lazy ghost
2 parents 9f692c6 + cae256b commit 931de58

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

src/Symfony/Component/PropertyAccess/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 <fabien@symfony.com>
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+
}

src/Symfony/Component/PropertyAccess/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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy