From a978c75434160ef494408b457c1643775a759b7a Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 25 Jul 2024 17:32:37 +0200 Subject: [PATCH] [VarDumper] Add support for virtual properties --- src/Symfony/Component/VarDumper/CHANGELOG.md | 1 + .../Component/VarDumper/Caster/Caster.php | 2 +- .../VarDumper/Caster/VirtualStub.php | 21 ++++++++++ .../Component/VarDumper/Dumper/CliDumper.php | 17 ++++++--- .../Component/VarDumper/Dumper/HtmlDumper.php | 5 +++ .../VarDumper/Tests/Caster/StubCasterTest.php | 38 ++++++++++++++++++- .../VarDumper/Tests/Dumper/CliDumperTest.php | 16 ++++++++ .../VarDumper/Tests/Dumper/HtmlDumperTest.php | 25 ++++++++++++ .../Tests/Fixtures/VirtualProperty.php | 21 ++++++++++ 9 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 src/Symfony/Component/VarDumper/Caster/VirtualStub.php create mode 100644 src/Symfony/Component/VarDumper/Tests/Fixtures/VirtualProperty.php diff --git a/src/Symfony/Component/VarDumper/CHANGELOG.md b/src/Symfony/Component/VarDumper/CHANGELOG.md index 10f92bde606ec..e47de40cc219f 100644 --- a/src/Symfony/Component/VarDumper/CHANGELOG.md +++ b/src/Symfony/Component/VarDumper/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add support for `FORCE_COLOR` environment variable + * Add support for virtual properties 7.1 --- diff --git a/src/Symfony/Component/VarDumper/Caster/Caster.php b/src/Symfony/Component/VarDumper/Caster/Caster.php index d9577e7ae52fc..cc213ab59197e 100644 --- a/src/Symfony/Component/VarDumper/Caster/Caster.php +++ b/src/Symfony/Component/VarDumper/Caster/Caster.php @@ -190,7 +190,7 @@ private static function getClassProperties(\ReflectionClass $class): array $p->isPublic() => $p->name, $p->isProtected() => self::PREFIX_PROTECTED.$p->name, default => "\0".$className."\0".$p->name, - }] = new UninitializedStub($p); + }] = \PHP_VERSION_ID >= 80400 && $p->isVirtual() ? new VirtualStub($p) : new UninitializedStub($p); } return $classProperties; diff --git a/src/Symfony/Component/VarDumper/Caster/VirtualStub.php b/src/Symfony/Component/VarDumper/Caster/VirtualStub.php new file mode 100644 index 0000000000000..60b58faa1ad2d --- /dev/null +++ b/src/Symfony/Component/VarDumper/Caster/VirtualStub.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +class VirtualStub extends ConstStub +{ + public function __construct(\ReflectionProperty $property) + { + parent::__construct('~'.($property->hasType() ? ' '.$property->getType() : ''), 'Virtual property'); + $this->attr['virtual'] = true; + } +} diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 4b0d6ae8a54f3..c6dd88c4519f4 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -33,12 +33,13 @@ class CliDumper extends AbstractDumper 'default' => '0;38;5;208', 'num' => '1;38;5;38', 'const' => '1;38;5;208', + 'virtual' => '3', 'str' => '1;38;5;113', 'note' => '38;5;38', 'ref' => '38;5;247', - 'public' => '', - 'protected' => '', - 'private' => '', + 'public' => '39', + 'protected' => '39', + 'private' => '39', 'meta' => '38;5;170', 'key' => '38;5;113', 'index' => '38;5;38', @@ -347,7 +348,10 @@ protected function dumpKey(Cursor $cursor): void if ($cursor->hashKeyIsBinary) { $key = $this->utf8Encode($key); } - $attr = ['binary' => $cursor->hashKeyIsBinary]; + $attr = [ + 'binary' => $cursor->hashKeyIsBinary, + 'virtual' => $cursor->attr['virtual'] ?? false, + ]; $bin = $cursor->hashKeyIsBinary ? 'b' : ''; $style = 'key'; switch ($cursor->hashType) { @@ -371,7 +375,7 @@ protected function dumpKey(Cursor $cursor): void // no break case Cursor::HASH_OBJECT: if (!isset($key[0]) || "\0" !== $key[0]) { - $this->line .= '+'.$bin.$this->style('public', $key).': '; + $this->line .= '+'.$bin.$this->style('public', $key, $attr).': '; } elseif (0 < strpos($key, "\0", 1)) { $key = explode("\0", substr($key, 1), 2); @@ -506,6 +510,9 @@ protected function style(string $style, string $value, array $attr = []): string if ('label' === $style && '' !== $value) { $value .= ' '; } + if ($this->colors && ($attr['virtual'] ?? false)) { + $value = "\033[{$this->styles['virtual']}m".$value; + } return $value; } diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index 147556b55fe58..478954ae4a77b 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -29,6 +29,7 @@ class HtmlDumper extends CliDumper 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', 'num' => 'font-weight:bold; color:#1299DA', 'const' => 'font-weight:bold', + 'virtual' => 'font-style:italic', 'str' => 'font-weight:bold; color:#56DB3A', 'note' => 'color:#1299DA', 'ref' => 'color:#A0A0A0', @@ -45,6 +46,7 @@ class HtmlDumper extends CliDumper 'default' => 'background:none; color:#CC7832; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', 'num' => 'font-weight:bold; color:#1299DA', 'const' => 'font-weight:bold', + 'virtual' => 'font-style:italic', 'str' => 'font-weight:bold; color:#629755;', 'note' => 'color:#6897BB', 'ref' => 'color:#6E6E6E', @@ -921,6 +923,9 @@ protected function style(string $style, string $value, array $attr = []): string if ('label' === $style) { $v .= ' '; } + if ($attr['virtual'] ?? false) { + $v = ''.$v.''; + } return $v; } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/StubCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/StubCasterTest.php index 7f6d87b7c1eeb..65603fca13cad 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/StubCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/StubCasterTest.php @@ -16,10 +16,12 @@ use Symfony\Component\VarDumper\Caster\ClassStub; use Symfony\Component\VarDumper\Caster\LinkStub; use Symfony\Component\VarDumper\Caster\ScalarStub; +use Symfony\Component\VarDumper\Caster\VirtualStub; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; use Symfony\Component\VarDumper\Tests\Fixtures\FooInterface; +use Symfony\Component\VarDumper\Tests\Fixtures\VirtualProperty; class StubCasterTest extends TestCase { @@ -101,6 +103,40 @@ public function testEmptyStub() $this->assertDumpMatchesFormat($expectedDump, $args); } + /** + * @requires PHP 8.4 + */ + public function testVirtualPropertyStub() + { + $class = new \ReflectionClass(VirtualProperty::class); + $args = [new VirtualStub($class->getProperty('fullName'))]; + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => ~ string +] +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $args); + } + + /** + * @requires PHP 8.4 + */ + public function testVirtualPropertyWithoutTypeStub() + { + $class = new \ReflectionClass(VirtualProperty::class); + $args = [new VirtualStub($class->getProperty('noType'))]; + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => ~ +] +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $args); + } + public function testLinkStub() { $var = [new LinkStub(__CLASS__, 0, __FILE__)]; @@ -217,7 +253,7 @@ public function testClassStubWithAnonymousClass() $expectedDump = <<<'EODUMP' array:1 [ - 0 => "Exception@anonymous" + 0 => "Exception@anonymous" ] EODUMP; diff --git a/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php index 1764a30c10eec..8a28c5ae7be24 100644 --- a/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php @@ -21,6 +21,7 @@ use Symfony\Component\VarDumper\Dumper\AbstractDumper; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; +use Symfony\Component\VarDumper\Tests\Fixtures\VirtualProperty; use Twig\Environment; use Twig\Loader\FilesystemLoader; @@ -304,6 +305,21 @@ public function testFlags() putenv('DUMP_STRING_LENGTH='); } + /** + * @requires PHP 8.4 + */ + public function testVirtualProperties() + { + $this->assertDumpEquals(<< @@ -117,6 +118,30 @@ public function testGet() ); } + /** + * @requires PHP 8.4 + */ + public function testVirtualProperties() + { + $dumper = new HtmlDumper('php://output'); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $cloner = new VarCloner(); + + $data = $cloner->cloneVar(new VirtualProperty()); + $out = $dumper->dump($data, true); + + $this->assertStringMatchesFormat(<<Symfony\Component\VarDumper\Tests\Fixtures\VirtualProperty {#%i + +firstName: "John" + +lastName: "Doe" + +fullName: ~ string + -noType: ~ + } + + EODUMP, $out); + } + public function testCharset() { $var = mb_convert_encoding('Словарь', 'CP1251', 'UTF-8'); diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/VirtualProperty.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/VirtualProperty.php new file mode 100644 index 0000000000000..83fa74a5e9ac3 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/VirtualProperty.php @@ -0,0 +1,21 @@ +firstName.' '.$this->lastName; + } + } + + private $noType { + get { + return null; + } + } +}