Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit dd4d393

Browse filesBrowse files
committed
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 ------- 2b2fd12 [PropertyAccess] Added an `UninitializedPropertyException`
2 parents e383b41 + 2b2fd12 commit dd4d393
Copy full SHA for dd4d393

File tree

4 files changed

+70
-6
lines changed
Filter options

4 files changed

+70
-6
lines changed

‎src/Symfony/Component/PropertyAccess/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
5.1.0
55
-----
66

7+
* Added an `UninitializedPropertyException`
78
* Linking to PropertyInfo extractor to remove a lot of duplicate code
89

910
4.4.0
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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\Exception;
13+
14+
/**
15+
* Thrown when a property is not initialized.
16+
*
17+
* @author Jules Pietri <jules@heahprod.com>
18+
*/
19+
class UninitializedPropertyException extends AccessException
20+
{
21+
}

‎src/Symfony/Component/PropertyAccess/PropertyAccessor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/PropertyAccessor.php
+26-6Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
2323
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
2424
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
25+
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
2526
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
2627
use Symfony\Component\PropertyInfo\PropertyReadInfo;
2728
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
@@ -389,14 +390,33 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
389390
$name = $access->getName();
390391
$type = $access->getType();
391392

392-
if (PropertyReadInfo::TYPE_METHOD === $type) {
393-
$result[self::VALUE] = $object->$name();
394-
} elseif (PropertyReadInfo::TYPE_PROPERTY === $type) {
395-
$result[self::VALUE] = $object->$name;
393+
try {
394+
if (PropertyReadInfo::TYPE_METHOD === $type) {
395+
try {
396+
$result[self::VALUE] = $object->$name();
397+
} catch (\TypeError $e) {
398+
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)) {
399+
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);
400+
}
396401

397-
if (isset($zval[self::REF]) && $access->canBeReference()) {
398-
$result[self::REF] = &$object->$name;
402+
throw $e;
403+
}
404+
} elseif (PropertyReadInfo::TYPE_PROPERTY === $type) {
405+
$result[self::VALUE] = $object->$name;
406+
407+
if (isset($zval[self::REF]) && $access->canBeReference()) {
408+
$result[self::REF] = &$object->$name;
409+
}
399410
}
411+
} catch (\Error $e) {
412+
// handle uninitialized properties in PHP >= 7.4
413+
if (\PHP_VERSION_ID >= 70400 && preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
414+
$r = new \ReflectionProperty($matches[1], $matches[2]);
415+
416+
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);
417+
}
418+
419+
throw $e;
400420
}
401421
} elseif ($object instanceof \stdClass && property_exists($object, $property)) {
402422
$result[self::VALUE] = $object->$property;

‎src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
+22Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Cache\Adapter\ArrayAdapter;
1616
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
17+
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
1718
use Symfony\Component\PropertyAccess\PropertyAccess;
1819
use Symfony\Component\PropertyAccess\PropertyAccessor;
1920
use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped;
@@ -28,6 +29,8 @@
2829
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestSingularAndPluralProps;
2930
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
3031
use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
32+
use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty;
33+
use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty;
3134

3235
class PropertyAccessorTest extends TestCase
3336
{
@@ -131,6 +134,25 @@ public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnab
131134
$this->propertyAccessor->getValue($objectOrArray, $path);
132135
}
133136

137+
/**
138+
* @requires PHP 7.4
139+
*/
140+
public function testGetValueThrowsExceptionIfUninitializedProperty()
141+
{
142+
$this->expectException(UninitializedPropertyException::class);
143+
$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.');
144+
145+
$this->propertyAccessor->getValue(new UninitializedProperty(), 'uninitialized');
146+
}
147+
148+
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetter()
149+
{
150+
$this->expectException(UninitializedPropertyException::class);
151+
$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?');
152+
153+
$this->propertyAccessor->getValue(new UninitializedPrivateProperty(), 'uninitialized');
154+
}
155+
134156
public function testGetValueThrowsExceptionIfNotArrayAccess()
135157
{
136158
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException');

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.