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;
+ }
+ }
+}