From 54468db095d2a75fef8e4a4c9121536cbcf771fe Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 27 Jul 2023 14:53:28 +0200 Subject: [PATCH] [VarDumper] Dump uninitialized properties --- src/Symfony/Component/VarDumper/CHANGELOG.md | 5 +++ .../Component/VarDumper/Caster/Caster.php | 40 +++++++++++++++---- .../VarDumper/Caster/UninitializedStub.php | 25 ++++++++++++ .../VarDumper/Caster/XmlReaderCaster.php | 1 + .../Tests/Caster/ExceptionCasterTest.php | 1 + .../Tests/Caster/MysqliCasterTest.php | 1 - src/Symfony/Component/VarDumper/composer.json | 1 + 7 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 src/Symfony/Component/VarDumper/Caster/UninitializedStub.php diff --git a/src/Symfony/Component/VarDumper/CHANGELOG.md b/src/Symfony/Component/VarDumper/CHANGELOG.md index 932987501053..7481cff1a4cd 100644 --- a/src/Symfony/Component/VarDumper/CHANGELOG.md +++ b/src/Symfony/Component/VarDumper/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.4 +--- + + * Dump uninitialized properties + 6.3 --- diff --git a/src/Symfony/Component/VarDumper/Caster/Caster.php b/src/Symfony/Component/VarDumper/Caster/Caster.php index 32c69ee04dc4..e79ee735fb54 100644 --- a/src/Symfony/Component/VarDumper/Caster/Caster.php +++ b/src/Symfony/Component/VarDumper/Caster/Caster.php @@ -32,6 +32,7 @@ class Caster public const EXCLUDE_EMPTY = 128; public const EXCLUDE_NOT_IMPORTANT = 256; public const EXCLUDE_STRICT = 512; + public const EXCLUDE_UNINITIALIZED = 1024; public const PREFIX_VIRTUAL = "\0~\0"; public const PREFIX_DYNAMIC = "\0+\0"; @@ -39,6 +40,8 @@ class Caster // usage: sprintf(Caster::PATTERN_PRIVATE, $class, $property) public const PATTERN_PRIVATE = "\0%s\0%s"; + private static array $classProperties = []; + /** * Casts objects to arrays and adds the dynamic property prefix. * @@ -61,20 +64,17 @@ public static function castObject(object $obj, string $class, bool $hasDebugInfo return $a; } + $classProperties = self::$classProperties[$class] ??= self::getClassProperties(new \ReflectionClass($class)); + $a = array_replace($classProperties, $a); + if ($a) { - static $publicProperties = []; $debugClass ??= get_debug_type($obj); $i = 0; $prefixedKeys = []; foreach ($a as $k => $v) { if ("\0" !== ($k[0] ?? '')) { - if (!isset($publicProperties[$class])) { - foreach ((new \ReflectionClass($class))->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) { - $publicProperties[$class][$prop->name] = true; - } - } - if (!isset($publicProperties[$class][$k])) { + if (!isset($classProperties[$k])) { $prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k; } } elseif ($debugClass !== $class && 1 === strpos($k, $class)) { @@ -131,6 +131,8 @@ public static function filter(array $a, int $filter, array $listedProperties = [ $type |= self::EXCLUDE_EMPTY & $filter; } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || [] === $v) { $type |= self::EXCLUDE_EMPTY & $filter; + } elseif ($v instanceof UninitializedStub) { + $type |= self::EXCLUDE_UNINITIALIZED & $filter; } if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) { $type |= self::EXCLUDE_NOT_IMPORTANT; @@ -169,4 +171,28 @@ public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array return $a; } + + private static function getClassProperties(\ReflectionClass $class): array + { + $classProperties = []; + $className = $class->name; + + foreach ($class->getProperties() as $p) { + if ($p->isStatic()) { + continue; + } + + $classProperties[match (true) { + $p->isPublic() => $p->name, + $p->isProtected() => self::PREFIX_PROTECTED.$p->name, + default => "\0".$className."\0".$p->name, + }] = new UninitializedStub($p); + } + + if ($parent = $class->getParentClass()) { + $classProperties += self::$classProperties[$parent->name] ??= self::getClassProperties($parent); + } + + return $classProperties; + } } diff --git a/src/Symfony/Component/VarDumper/Caster/UninitializedStub.php b/src/Symfony/Component/VarDumper/Caster/UninitializedStub.php new file mode 100644 index 000000000000..a9bdd9b8130b --- /dev/null +++ b/src/Symfony/Component/VarDumper/Caster/UninitializedStub.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +/** + * Represents an uninitialized property. + * + * @author Nicolas Grekas + */ +class UninitializedStub extends ConstStub +{ + public function __construct(\ReflectionProperty $property) + { + parent::__construct('?'.($property->hasType() ? ' '.$property->getType() : ''), 'Uninitialized property'); + } +} diff --git a/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php b/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php index d802bbf2a107..1cfcf4dd8610 100644 --- a/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php @@ -85,6 +85,7 @@ public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, b $info[$props]->cut = $count; } + $a = Caster::filter($a, Caster::EXCLUDE_UNINITIALIZED, [], $count); $info = Caster::filter($info, Caster::EXCLUDE_EMPTY, [], $count); // +2 because hasValue and hasAttributes are always filtered $stub->cut += $count + 2; diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php index 66708944c63d..f8fe43d8ddce 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php @@ -378,6 +378,7 @@ public function testFlattenException() -file: "%sExceptionCasterTest.php" -line: %d -asString: null + -dataRepresentation: ? Symfony\Component\VarDumper\Cloner\Data } ] EODUMP; diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/MysqliCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/MysqliCasterTest.php index 983f541a3f78..4eba406efd32 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/MysqliCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/MysqliCasterTest.php @@ -30,7 +30,6 @@ public function testNotConnected() $xCast = << 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