diff --git a/LICENSE b/LICENSE index 9ff2d0d..88bf75b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PropertyAccessor.php b/PropertyAccessor.php index 70f324e..510cfa7 100644 --- a/PropertyAccessor.php +++ b/PropertyAccessor.php @@ -427,11 +427,11 @@ 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)) { - $r = new \ReflectionProperty($matches[1], $matches[2]); + if (preg_match('/^Typed property ('.preg_quote(get_debug_type($object), '/').')::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) { + $r = new \ReflectionProperty($class, $matches[2]); $type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type; - throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $type), 0, $e); + throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $matches[1], $r->getName(), $type), 0, $e); } throw $e; diff --git a/Tests/PropertyAccessorTest.php b/Tests/PropertyAccessorTest.php index 3db072d..cfffdb2 100644 --- a/Tests/PropertyAccessorTest.php +++ b/Tests/PropertyAccessorTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Cache\Adapter\ArrayAdapter; -use Symfony\Component\PropertyAccess\Exception\AccessException; use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; @@ -144,44 +143,73 @@ public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetter() public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousClass() { - $this->expectException(AccessException::class); + $this->expectException(UninitializedPropertyException::class); $this->expectExceptionMessage('The method "class@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 = eval('return new class() { + $object = new class() { private $uninitialized; public function getUninitialized(): array { return $this->uninitialized; } - };'); + }; + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + + public function testGetValueThrowsExceptionIfUninitializedNotNullablePropertyWithGetterOfAnonymousClass() + { + $this->expectException(UninitializedPropertyException::class); + $this->expectExceptionMessage('The property "class@anonymous::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.'); + + $object = new class() { + private string $uninitialized; + + public function getUninitialized(): string + { + return $this->uninitialized; + } + }; + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + + public function testGetValueThrowsExceptionIfUninitializedPropertyOfAnonymousClass() + { + $this->expectException(UninitializedPropertyException::class); + $this->expectExceptionMessage('The property "class@anonymous::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.'); + + $object = new class() { + public string $uninitialized; + }; $this->propertyAccessor->getValue($object, 'uninitialized'); } public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousStdClass() { - $this->expectException(AccessException::class); + $this->expectException(UninitializedPropertyException::class); $this->expectExceptionMessage('The method "stdClass@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 = eval('return new class() extends \stdClass { + $object = new class() extends \stdClass { private $uninitialized; public function getUninitialized(): array { return $this->uninitialized; } - };'); + }; $this->propertyAccessor->getValue($object, 'uninitialized'); } public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousChildClass() { - $this->expectException(AccessException::class); + $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 = eval('return new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {};'); + $object = new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {}; $this->propertyAccessor->getValue($object, 'uninitialized'); } @@ -684,7 +712,7 @@ public function testAnonymousClassWrite() private function generateAnonymousClass($value) { - $obj = eval('return new class($value) + return new class($value) { private $foo; @@ -708,9 +736,7 @@ public function setFoo($foo) { $this->foo = $foo; } - };'); - - return $obj; + }; } public function testThrowTypeErrorInsideSetterCall()