feature #36117 [PropertyAccess][DX] Added an UninitializedPropertyException
(HeahDude)
This PR was merged into the 5.1-dev branch.
Discussion
----------
[PropertyAccess][DX] Added an `UninitializedPropertyException`
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | ~
| License | MIT
| Doc PR | TODO
Feature version of #36073 for master. Again, better be reviewed without whitespace changes, thanks!
Commits
-------
2b2fd12b0d
[PropertyAccess] Added an `UninitializedPropertyException`
This commit is contained in:
commit
dd4d393a3c
@ -4,6 +4,7 @@ CHANGELOG
|
|||||||
5.1.0
|
5.1.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
* Added an `UninitializedPropertyException`
|
||||||
* Linking to PropertyInfo extractor to remove a lot of duplicate code
|
* Linking to PropertyInfo extractor to remove a lot of duplicate code
|
||||||
|
|
||||||
4.4.0
|
4.4.0
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\PropertyAccess\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a property is not initialized.
|
||||||
|
*
|
||||||
|
* @author Jules Pietri <jules@heahprod.com>
|
||||||
|
*/
|
||||||
|
class UninitializedPropertyException extends AccessException
|
||||||
|
{
|
||||||
|
}
|
@ -22,6 +22,7 @@ use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
|
|||||||
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
|
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
|
||||||
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
|
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
|
||||||
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
|
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
|
||||||
|
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
|
||||||
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
|
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
|
||||||
use Symfony\Component\PropertyInfo\PropertyReadInfo;
|
use Symfony\Component\PropertyInfo\PropertyReadInfo;
|
||||||
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
|
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
|
||||||
@ -389,14 +390,33 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
$name = $access->getName();
|
$name = $access->getName();
|
||||||
$type = $access->getType();
|
$type = $access->getType();
|
||||||
|
|
||||||
if (PropertyReadInfo::TYPE_METHOD === $type) {
|
try {
|
||||||
$result[self::VALUE] = $object->$name();
|
if (PropertyReadInfo::TYPE_METHOD === $type) {
|
||||||
} elseif (PropertyReadInfo::TYPE_PROPERTY === $type) {
|
try {
|
||||||
$result[self::VALUE] = $object->$name;
|
$result[self::VALUE] = $object->$name();
|
||||||
|
} catch (\TypeError $e) {
|
||||||
|
if (preg_match((sprintf('/^Return value of %s::%s\(\) must be of the type (\w+), null returned$/', preg_quote(\get_class($object)), $name)), $e->getMessage(), $matches)) {
|
||||||
|
throw new UninitializedPropertyException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Have you forgotten to initialize a property or to make the return type nullable using "?%3$s" instead?', \get_class($object), $name, $matches[1]), 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($zval[self::REF]) && $access->canBeReference()) {
|
throw $e;
|
||||||
$result[self::REF] = &$object->$name;
|
}
|
||||||
|
} elseif (PropertyReadInfo::TYPE_PROPERTY === $type) {
|
||||||
|
$result[self::VALUE] = $object->$name;
|
||||||
|
|
||||||
|
if (isset($zval[self::REF]) && $access->canBeReference()) {
|
||||||
|
$result[self::REF] = &$object->$name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (\Error $e) {
|
||||||
|
// handle uninitialized properties in PHP >= 7.4
|
||||||
|
if (\PHP_VERSION_ID >= 70400 && preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
|
||||||
|
$r = new \ReflectionProperty($matches[1], $matches[2]);
|
||||||
|
|
||||||
|
throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%3$s". You should either initialize it or make it nullable using "?%3$s" instead.', $r->getDeclaringClass()->getName(), $r->getName(), $r->getType()->getName()), 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
} elseif ($object instanceof \stdClass && property_exists($object, $property)) {
|
} elseif ($object instanceof \stdClass && property_exists($object, $property)) {
|
||||||
$result[self::VALUE] = $object->$property;
|
$result[self::VALUE] = $object->$property;
|
||||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\PropertyAccess\Tests;
|
|||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||||
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
|
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
|
||||||
|
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped;
|
use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped;
|
||||||
@ -28,6 +29,8 @@ use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassTypeErrorInsideCall
|
|||||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestSingularAndPluralProps;
|
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestSingularAndPluralProps;
|
||||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
|
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
|
||||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
|
use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
|
||||||
|
use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty;
|
||||||
|
use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty;
|
||||||
|
|
||||||
class PropertyAccessorTest extends TestCase
|
class PropertyAccessorTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -131,6 +134,25 @@ class PropertyAccessorTest extends TestCase
|
|||||||
$this->propertyAccessor->getValue($objectOrArray, $path);
|
$this->propertyAccessor->getValue($objectOrArray, $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires PHP 7.4
|
||||||
|
*/
|
||||||
|
public function testGetValueThrowsExceptionIfUninitializedProperty()
|
||||||
|
{
|
||||||
|
$this->expectException(UninitializedPropertyException::class);
|
||||||
|
$this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty::$uninitialized" is not readable because it is typed "string". You should either initialize it or make it nullable using "?string" instead.');
|
||||||
|
|
||||||
|
$this->propertyAccessor->getValue(new UninitializedProperty(), 'uninitialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetter()
|
||||||
|
{
|
||||||
|
$this->expectException(UninitializedPropertyException::class);
|
||||||
|
$this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty::getUninitialized()" returned "null", but expected type "array". Have you forgotten to initialize a property or to make the return type nullable using "?array" instead?');
|
||||||
|
|
||||||
|
$this->propertyAccessor->getValue(new UninitializedPrivateProperty(), 'uninitialized');
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetValueThrowsExceptionIfNotArrayAccess()
|
public function testGetValueThrowsExceptionIfNotArrayAccess()
|
||||||
{
|
{
|
||||||
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException');
|
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException');
|
||||||
|
Reference in New Issue
Block a user