diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 3bb5d6dcf7..99aa9a3ebc 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -479,9 +479,15 @@ class PropertyAccessor implements PropertyAccessorInterface try { $result[self::VALUE] = $object->{$access[self::ACCESS_NAME]}(); } catch (\TypeError $e) { + list($trace) = $e->getTrace(); + // handle uninitialized properties in PHP >= 7 - if (preg_match((sprintf('/^Return value of %s::%s\(\) must be of (?:the )?type (\w+), null returned$/', preg_quote(\get_class($object)), $access[self::ACCESS_NAME])), $e->getMessage(), $matches)) { - throw new AccessException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', \get_class($object), $access[self::ACCESS_NAME], $matches[1]), 0, $e); + if (__FILE__ === $trace['file'] + && $access[self::ACCESS_NAME] === $trace['function'] + && $object instanceof $trace['class'] + && preg_match((sprintf('/Return value (?:of .*::\w+\(\) )?must be of (?:the )?type (\w+), null returned$/')), $e->getMessage(), $matches) + ) { + throw new AccessException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', false === strpos(\get_class($object), "@anonymous\0") ? \get_class($object) : (get_parent_class($object) ?: 'class').'@anonymous', $access[self::ACCESS_NAME], $matches[1]), 0, $e); } throw $e; diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index d8331e76ad..e1d87a428c 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -142,6 +142,59 @@ class PropertyAccessorTest extends TestCase $this->propertyAccessor->getValue(new UninitializedPrivateProperty(), 'uninitialized'); } + /** + * @requires PHP 7 + */ + public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousClass() + { + $this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException'); + $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() { + private $uninitialized; + + public function getUninitialized(): array + { + return $this->uninitialized; + } + };'); + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + + /** + * @requires PHP 7 + */ + public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousStdClass() + { + $this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException'); + $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 { + private $uninitialized; + + public function getUninitialized(): array + { + return $this->uninitialized; + } + };'); + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + + /** + * @requires PHP 7 + */ + public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousChildClass() + { + $this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException'); + $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 {};'); + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + public function testGetValueThrowsExceptionIfNotArrayAccess() { $this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException');