From cae256bb11dbfa6808a45b19c5facbc0c26aa27e Mon Sep 17 00:00:00 2001 From: Priyadi Iman Nurcahyo <1102197+priyadi@users.noreply.github.com> Date: Sat, 10 Feb 2024 10:16:25 +0700 Subject: [PATCH] [PropertyAccess] Fixes getValue() on an unitialized object property on a lazy ghost --- .../PropertyAccess/PropertyAccessor.php | 2 +- .../Fixtures/UninitializedObjectProperty.php | 28 ++++++++++ .../Tests/PropertyAccessorTest.php | 55 ++++++++++++++++++- 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Fixtures/UninitializedObjectProperty.php diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 3aabc5db5e349..60f2d32770b0d 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -423,7 +423,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid } } catch (\Error $e) { // handle uninitialized properties in PHP >= 7.4 - if (preg_match('/^Typed property ([\w\\\\@]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) { + 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)) { $r = new \ReflectionProperty(str_contains($matches[1], '@anonymous') ? $class : $matches[1], $matches[2]); $type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type; diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/UninitializedObjectProperty.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/UninitializedObjectProperty.php new file mode 100644 index 0000000000000..bc05c466e5acb --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/UninitializedObjectProperty.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Tests\Fixtures; + +class UninitializedObjectProperty +{ + public \DateTimeInterface $uninitialized; + private \DateTimeInterface $privateUninitialized; + + public function getPrivateUninitialized(): string + { + return $this->privateUninitialized; + } + + public function setPrivateUninitialized(string $privateUninitialized): void + { + $this->privateUninitialized = $privateUninitialized; + } +} diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index 7b4dfba0759f5..4120b62502c82 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -37,8 +37,10 @@ use Symfony\Component\PropertyAccess\Tests\Fixtures\TestSingularAndPluralProps; use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object; use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted; +use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedObjectProperty; use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty; use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty; +use Symfony\Component\VarExporter\ProxyHelper; class PropertyAccessorTest extends TestCase { @@ -225,7 +227,8 @@ public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAn $this->expectException(UninitializedPropertyException::class); $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"?'); - $object = new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {}; + $object = new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty { + }; $this->propertyAccessor->getValue($object, 'uninitialized'); } @@ -958,4 +961,54 @@ public function testCastDateTimeImmutable() $this->assertInstanceOf(\DateTime::class, $object->getDate()); } + + public function testGetValuePropertyThrowsExceptionIfUninitializedWithoutLazyGhost() + { + $this->expectException(UninitializedPropertyException::class); + $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.'); + + $this->propertyAccessor->getValue(new UninitializedObjectProperty(), 'uninitialized'); + } + + public function testGetValueGetterThrowsExceptionIfUninitializedWithoutLazyGhost() + { + $this->expectException(UninitializedPropertyException::class); + $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.'); + + $this->propertyAccessor->getValue(new UninitializedObjectProperty(), 'privateUninitialized'); + } + + private function createUninitializedObjectPropertyGhost(): UninitializedObjectProperty + { + $class = 'UninitializedObjectPropertyGhost'; + + if (!class_exists($class)) { + eval('class '.$class.ProxyHelper::generateLazyGhost(new \ReflectionClass(UninitializedObjectProperty::class))); + } + + $this->assertTrue(class_exists($class)); + + return $class::createLazyGhost(initializer: function ($instance) { + }); + } + + public function testGetValuePropertyThrowsExceptionIfUninitializedWithLazyGhost() + { + $this->expectException(UninitializedPropertyException::class); + $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.'); + + $lazyGhost = $this->createUninitializedObjectPropertyGhost(); + + $this->propertyAccessor->getValue($lazyGhost, 'uninitialized'); + } + + public function testGetValueGetterThrowsExceptionIfUninitializedWithLazyGhost() + { + $this->expectException(UninitializedPropertyException::class); + $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.'); + + $lazyGhost = $this->createUninitializedObjectPropertyGhost(); + + $this->propertyAccessor->getValue($lazyGhost, 'privateUninitialized'); + } } 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