bug #40453 [VarDumper] Adds support for ReflectionUnionType to VarDumper (Michael Nelson, michaeldnelson)

This PR was squashed before being merged into the 4.4 branch.

Discussion
----------

[VarDumper] Adds support for ReflectionUnionType to VarDumper

Fixes a bug with VarDumper when dumping a ReflectionUnionType.
> PHP Error:  Call to undefined method ReflectionUnionType::isBuiltin()

Notes:
* One of the existing tests relies on its position in the test file. I had to modify its expected line number.
* There is an existing trailing space around line 367 in an expected value.
  I'm not sure if this was left for BC reasons but it seems like a bug if the dumper is leaving trailing spaces.

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       |
| License       | MIT
| Doc PR        |

This commit fixes a crash when dumping ReflectionUnionType.  The code is minimal but uses a few extra lines to preserve key order for bc and consistency.  Additionally, there is an else condition that is currently unreachable but is defensive should they add additional subtypes of ReflectionType. Tests are included.  Please let me know if you have any questions or suggestions.  Thanks for Symfony it's a wonderful project.

Commits
-------

1a11491f6e [VarDumper] Adds support for ReflectionUnionType to VarDumper
This commit is contained in:
Fabien Potencier 2021-03-17 07:30:13 +01:00
commit 550489aa8e
5 changed files with 154 additions and 7 deletions

View File

@ -96,11 +96,20 @@ class ReflectionCaster
{
$prefix = Caster::PREFIX_VIRTUAL;
$a += [
$prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c,
$prefix.'allowsNull' => $c->allowsNull(),
$prefix.'isBuiltin' => $c->isBuiltin(),
];
if ($c instanceof \ReflectionNamedType || \PHP_VERSION_ID < 80000) {
$a += [
$prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c,
$prefix.'allowsNull' => $c->allowsNull(),
$prefix.'isBuiltin' => $c->isBuiltin(),
];
} elseif ($c instanceof \ReflectionUnionType) {
$a[$prefix.'allowsNull'] = $c->allowsNull();
self::addMap($a, $c, [
'types' => 'getTypes',
]);
} else {
$a[$prefix.'allowsNull'] = $c->allowsNull();
}
return $a;
}
@ -377,7 +386,7 @@ class ReflectionCaster
}
}
private static function addMap(array &$a, \Reflector $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL)
private static function addMap(array &$a, $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL)
{
foreach ($map as $k => $m) {
if (\PHP_VERSION_ID >= 80000 && 'isDisabled' === $k) {

View File

@ -14,8 +14,11 @@ namespace Symfony\Component\VarDumper\Tests\Caster;
use PHPUnit\Framework\TestCase;
use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
use Symfony\Component\VarDumper\Tests\Fixtures\ExtendsReflectionTypeFixture;
use Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo;
use Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass;
use Symfony\Component\VarDumper\Tests\Fixtures\ReflectionNamedTypeFixture;
use Symfony\Component\VarDumper\Tests\Fixtures\ReflectionUnionTypeFixture;
/**
* @author Nicolas Grekas <p@tchwork.com>
@ -75,7 +78,7 @@ Closure($x) {
$b: & 123
}
file: "%sReflectionCasterTest.php"
line: "68 to 68"
line: "71 to 71"
}
EOTXT
, $var
@ -211,6 +214,104 @@ EOTXT
);
}
/**
* @requires PHP 7.4
*/
public function testReflectionPropertyScalar()
{
$var = new \ReflectionProperty(ReflectionNamedTypeFixture::class, 'a');
$this->assertDumpMatchesFormat(
<<<'EOTXT'
ReflectionProperty {
+name: "a"
+class: "Symfony\Component\VarDumper\Tests\Fixtures\ReflectionNamedTypeFixture"
modifiers: "public"
}
EOTXT
, $var
);
}
/**
* @requires PHP 7.4
*/
public function testReflectionNamedType()
{
$var = (new \ReflectionProperty(ReflectionNamedTypeFixture::class, 'a'))->getType();
$this->assertDumpMatchesFormat(
<<<'EOTXT'
ReflectionNamedType {
name: "int"
allowsNull: false
isBuiltin: true
}
EOTXT
, $var
);
}
/**
* @requires PHP 8
*/
public function testReflectionUnionType()
{
$var = (new \ReflectionProperty(ReflectionUnionTypeFixture::class, 'a'))->getType();
$this->assertDumpMatchesFormat(
<<<'EOTXT'
ReflectionUnionType {
allowsNull: false
types: array:2 [
0 => ReflectionNamedType {
name: "string"
allowsNull: false
isBuiltin: true
}
1 => ReflectionNamedType {
name: "int"
allowsNull: false
isBuiltin: true
}
]
}
EOTXT
, $var
);
}
/**
* @requires PHP 8
*/
public function testExtendsReflectionType()
{
$var = new ExtendsReflectionTypeFixture();
$this->assertDumpMatchesFormat(
<<<'EOTXT'
Symfony\Component\VarDumper\Tests\Fixtures\ExtendsReflectionTypeFixture {
allowsNull: false
}
EOTXT
, $var
);
}
/**
* @requires PHP < 8
*/
public function testLegacyExtendsReflectionType()
{
$var = new ExtendsReflectionTypeFixture();
$this->assertDumpMatchesFormat(
<<<'EOTXT'
Symfony\Component\VarDumper\Tests\Fixtures\ExtendsReflectionTypeFixture {
name: "fake"
allowsNull: false
isBuiltin: false
}
EOTXT
, $var
);
}
public function testReturnType()
{
$f = eval('return function ():int {};');

View File

@ -0,0 +1,21 @@
<?php
namespace Symfony\Component\VarDumper\Tests\Fixtures;
class ExtendsReflectionTypeFixture extends \ReflectionType
{
public function allowsNull(): bool
{
return false;
}
public function isBuiltin(): bool
{
return false;
}
public function __toString(): string
{
return 'fake';
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace Symfony\Component\VarDumper\Tests\Fixtures;
class ReflectionNamedTypeFixture
{
public int $a;
}

View File

@ -0,0 +1,8 @@
<?php
namespace Symfony\Component\VarDumper\Tests\Fixtures;
class ReflectionUnionTypeFixture
{
public int|string $a;
}