[VarDumper] Support for ReflectionAttribute.

This commit is contained in:
Alexander M. Turek 2020-09-12 17:20:39 +02:00
parent 163a2ab2b3
commit 34dbf01618
7 changed files with 285 additions and 6 deletions

View File

@ -35,8 +35,11 @@ foreach ($loader->getClassMap() as $class => $file) {
case false !== strpos($file, '/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php80Dummy.php'):
case false !== strpos($file, '/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures'):
case false !== strpos($file, '/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectOuter.php'):
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/LotsOfAttributes.php'):
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/MyAttribute.php'):
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/NotLoadableClass.php'):
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/Php74.php') && \PHP_VERSION_ID < 70400:
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/RepeatableAttribute.php'):
continue 2;
}

View File

@ -105,6 +105,16 @@ class ReflectionCaster
return $a;
}
public static function castAttribute(\ReflectionAttribute $c, array $a, Stub $stub, bool $isNested)
{
self::addMap($a, $c, [
'name' => 'getName',
'arguments' => 'getArguments',
]);
return $a;
}
public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;
@ -151,7 +161,7 @@ class ReflectionCaster
self::addMap($a, $c, [
'extends' => 'getParentClass',
'implements' => 'getInterfaceNames',
'constants' => 'getConstants',
'constants' => 'getReflectionConstants',
]);
foreach ($c->getProperties() as $n) {
@ -162,6 +172,8 @@ class ReflectionCaster
$a[$prefix.'methods'][$n->name] = $n;
}
self::addAttributes($a, $c, $prefix);
if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) {
self::addExtra($a, $c);
}
@ -206,6 +218,8 @@ class ReflectionCaster
$a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']);
}
self::addAttributes($a, $c, $prefix);
if (!($filter & Caster::EXCLUDE_VERBOSE) && $v = $c->getStaticVariables()) {
foreach ($v as $k => &$v) {
if (\is_object($v)) {
@ -225,6 +239,16 @@ class ReflectionCaster
return $a;
}
public static function castClassConstant(\ReflectionClassConstant $c, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
$a[Caster::PREFIX_VIRTUAL.'value'] = $c->getValue();
self::addAttributes($a, $c);
return $a;
}
public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
@ -243,6 +267,8 @@ class ReflectionCaster
'allowsNull' => 'allowsNull',
]);
self::addAttributes($a, $c, $prefix);
if ($v = $c->getType()) {
$a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v;
}
@ -271,6 +297,8 @@ class ReflectionCaster
public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
self::addAttributes($a, $c);
self::addExtra($a, $c);
return $a;
@ -377,7 +405,7 @@ class ReflectionCaster
}
}
private static function addMap(array &$a, \Reflector $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL)
private static function addMap(array &$a, object $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL)
{
foreach ($map as $k => $m) {
if (\PHP_VERSION_ID >= 80000 && 'isDisabled' === $k) {
@ -389,4 +417,13 @@ class ReflectionCaster
}
}
}
private static function addAttributes(array &$a, \Reflector $c, string $prefix = Caster::PREFIX_VIRTUAL): void
{
if (\PHP_VERSION_ID >= 80000) {
foreach ($c->getAttributes() as $n) {
$a[$prefix.'attributes'][] = $n;
}
}
}
}

View File

@ -32,8 +32,10 @@ abstract class AbstractCloner implements ClonerInterface
'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'],
'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'],
'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'],
'ReflectionAttribute' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castAttribute'],
'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'],
'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'],
'ReflectionClassConstant' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClassConstant'],
'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'],
'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'],
'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'],

View File

@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
use Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo;
use Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes;
use Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass;
/**
@ -36,9 +37,24 @@ ReflectionClass {
0 => "Reflector"
%A]
constants: array:3 [
"IS_IMPLICIT_ABSTRACT" => 16
"IS_EXPLICIT_ABSTRACT" => %d
"IS_FINAL" => %d
0 => ReflectionClassConstant {
+name: "IS_IMPLICIT_ABSTRACT"
+class: "ReflectionClass"
modifiers: "public"
value: 16
}
1 => ReflectionClassConstant {
+name: "IS_EXPLICIT_ABSTRACT"
+class: "ReflectionClass"
modifiers: "public"
value: %d
}
2 => ReflectionClassConstant {
+name: "IS_FINAL"
+class: "ReflectionClass"
modifiers: "public"
value: %d
}
]
properties: array:%d [
"name" => ReflectionProperty {
@ -75,7 +91,7 @@ Closure($x) {
$b: & 123
}
file: "%sReflectionCasterTest.php"
line: "68 to 68"
line: "84 to 84"
}
EOTXT
, $var
@ -242,6 +258,135 @@ EODUMP;
$this->assertDumpMatchesFormat($expectedDump, $generator);
}
/**
* @requires PHP 8
*/
public function testReflectionClassWithAttribute()
{
$var = new \ReflectionClass(LotsOfAttributes::class);
$this->assertDumpMatchesFormat(<<< 'EOTXT'
ReflectionClass {
+name: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes"
%A attributes: array:1 [
0 => ReflectionAttribute {
name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute"
arguments: []
}
]
%A
}
EOTXT
, $var);
}
/**
* @requires PHP 8
*/
public function testReflectionMethodWithAttribute()
{
$var = new \ReflectionMethod(LotsOfAttributes::class, 'someMethod');
$this->assertDumpMatchesFormat(<<< 'EOTXT'
ReflectionMethod {
+name: "someMethod"
+class: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes"
%A attributes: array:1 [
0 => ReflectionAttribute {
name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute"
arguments: array:1 [
0 => "two"
]
}
]
%A
}
EOTXT
, $var);
}
/**
* @requires PHP 8
*/
public function testReflectionPropertyWithAttribute()
{
$var = new \ReflectionProperty(LotsOfAttributes::class, 'someProperty');
$this->assertDumpMatchesFormat(<<< 'EOTXT'
ReflectionProperty {
+name: "someProperty"
+class: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes"
%A attributes: array:1 [
0 => ReflectionAttribute {
name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute"
arguments: array:2 [
0 => "one"
"extra" => "hello"
]
}
]
}
EOTXT
, $var);
}
/**
* @requires PHP 8
*/
public function testReflectionClassConstantWithAttribute()
{
$var = new \ReflectionClassConstant(LotsOfAttributes::class, 'SOME_CONSTANT');
$this->assertDumpMatchesFormat(<<< 'EOTXT'
ReflectionClassConstant {
+name: "SOME_CONSTANT"
+class: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes"
modifiers: "public"
value: "some value"
attributes: array:2 [
0 => ReflectionAttribute {
name: "Symfony\Component\VarDumper\Tests\Fixtures\RepeatableAttribute"
arguments: array:1 [
0 => "one"
]
}
1 => ReflectionAttribute {
name: "Symfony\Component\VarDumper\Tests\Fixtures\RepeatableAttribute"
arguments: array:1 [
0 => "two"
]
}
]
}
EOTXT
, $var);
}
/**
* @requires PHP 8
*/
public function testReflectionParameterWithAttribute()
{
$var = new \ReflectionParameter([LotsOfAttributes::class, 'someMethod'], 'someParameter');
$this->assertDumpMatchesFormat(<<< 'EOTXT'
ReflectionParameter {
+name: "someParameter"
position: 0
attributes: array:1 [
0 => ReflectionAttribute {
name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute"
arguments: array:1 [
0 => "three"
]
}
]
%A
}
EOTXT
, $var);
}
public static function stub(): void
{
}

View File

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Tests\Fixtures;
#[MyAttribute]
final class LotsOfAttributes
{
#[RepeatableAttribute('one'), RepeatableAttribute('two')]
public const SOME_CONSTANT = 'some value';
#[MyAttribute('one', extra: 'hello')]
private string $someProperty;
#[MyAttribute('two')]
public function someMethod(
#[MyAttribute('three')] string $someParameter
): void {
}
}

View File

@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Tests\Fixtures;
use Attribute;
#[Attribute]
final class MyAttribute
{
public function __construct(
private string $foo = 'default',
private ?string $extra = null,
) {
}
public function getFoo(): string
{
return $this->foo;
}
public function getExtra(): ?string
{
return $this->extra;
}
}

View File

@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Tests\Fixtures;
use Attribute;
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS_CONST | Attribute::TARGET_PROPERTY)]
final class RepeatableAttribute
{
private string $string;
public function __construct(string $string = 'default')
{
$this->string = $string;
}
public function getString(): string
{
return $this->string;
}
}