Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 772547e

Browse filesBrowse files
committed
feature #38167 [VarDumper] Support for ReflectionAttribute (derrabus)
This PR was merged into the 5.2-dev branch. Discussion ---------- [VarDumper] Support for ReflectionAttribute | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | N/A | License | MIT | Doc PR | not needed VarDumper currently does not understand that certain reflection objects might have attributes attached to it. Dumping a `ReflectionAttribute` just yields `ReflectionAttribute {#4711}` which is not really helpful. This PR attempts to fix this. ``` ReflectionAttribute {#4711 name: "App\MyAttribute" arguments: array:2 [ 0 => "one" "extra" => "hello" ] } ``` While working on this, I noticed that class constants (which can be reflected on since PHP 7.1) are just dumped as plain values, so I've also added a caster for `ReflectionClasConstant` as bonus. The full output for the `LotsOfAttributes` fixture class that is included with is PR looks like this: <details> ``` ^ ReflectionClass {#7 +name: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes" modifiers: "final" implements: [] constants: array:1 [ 0 => ReflectionClassConstant {#20 +name: "SOME_CONSTANT" +class: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes" modifiers: "public" value: "some value" attributes: array:2 [ 0 => ReflectionAttribute {#33 name: "Symfony\Component\VarDumper\Tests\Fixtures\RepeatableAttribute" arguments: array:1 [ 0 => "one" ] } 1 => ReflectionAttribute {#34 name: "Symfony\Component\VarDumper\Tests\Fixtures\RepeatableAttribute" arguments: array:1 [ 0 => "two" ] } ] } ] properties: array:1 [ "someProperty" => ReflectionProperty {#19 +name: "someProperty" +class: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes" modifiers: "private" attributes: array:1 [ 0 => ReflectionAttribute {#30 name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute" arguments: array:2 [ 0 => "one" "extra" => "hello" ] } ] } ] methods: array:1 [ "someMethod" => ReflectionMethod {#21 +name: "someMethod" +class: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes" returnType: "void" parameters: { $someParameter: ReflectionParameter {#28 +name: "someParameter" position: 0 attributes: array:1 [ 0 => ReflectionAttribute {#42 name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute" arguments: array:1 [ 0 => "three" ] } ] typeHint: "string" } } attributes: array:1 [ 0 => ReflectionAttribute {#27 name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute" arguments: array:1 [ 0 => "two" ] } ] modifiers: "public" } ] attributes: array:1 [ 0 => ReflectionAttribute {#22 name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute" arguments: [] } ] extra: { file: "./src/Symfony/Component/VarDumper/Tests/Fixtures/LotsOfAttributes.php" line: "15 to 28" isUserDefined: true } } ``` </details> Commits ------- 34dbf01 [VarDumper] Support for ReflectionAttribute.
2 parents 163a2ab + 34dbf01 commit 772547e
Copy full SHA for 772547e

File tree

7 files changed

+285
-6
lines changed
Filter options

7 files changed

+285
-6
lines changed

‎.github/patch-types.php

Copy file name to clipboardExpand all lines: .github/patch-types.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@
3535
case false !== strpos($file, '/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php80Dummy.php'):
3636
case false !== strpos($file, '/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures'):
3737
case false !== strpos($file, '/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectOuter.php'):
38+
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/LotsOfAttributes.php'):
39+
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/MyAttribute.php'):
3840
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/NotLoadableClass.php'):
3941
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/Php74.php') && \PHP_VERSION_ID < 70400:
42+
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/RepeatableAttribute.php'):
4043
continue 2;
4144
}
4245

‎src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
+39-2Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,16 @@ public static function castType(\ReflectionType $c, array $a, Stub $stub, bool $
105105
return $a;
106106
}
107107

108+
public static function castAttribute(\ReflectionAttribute $c, array $a, Stub $stub, bool $isNested)
109+
{
110+
self::addMap($a, $c, [
111+
'name' => 'getName',
112+
'arguments' => 'getArguments',
113+
]);
114+
115+
return $a;
116+
}
117+
108118
public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, bool $isNested)
109119
{
110120
$prefix = Caster::PREFIX_VIRTUAL;
@@ -151,7 +161,7 @@ public static function castClass(\ReflectionClass $c, array $a, Stub $stub, bool
151161
self::addMap($a, $c, [
152162
'extends' => 'getParentClass',
153163
'implements' => 'getInterfaceNames',
154-
'constants' => 'getConstants',
164+
'constants' => 'getReflectionConstants',
155165
]);
156166

157167
foreach ($c->getProperties() as $n) {
@@ -162,6 +172,8 @@ public static function castClass(\ReflectionClass $c, array $a, Stub $stub, bool
162172
$a[$prefix.'methods'][$n->name] = $n;
163173
}
164174

175+
self::addAttributes($a, $c, $prefix);
176+
165177
if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) {
166178
self::addExtra($a, $c);
167179
}
@@ -206,6 +218,8 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra
206218
$a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']);
207219
}
208220

221+
self::addAttributes($a, $c, $prefix);
222+
209223
if (!($filter & Caster::EXCLUDE_VERBOSE) && $v = $c->getStaticVariables()) {
210224
foreach ($v as $k => &$v) {
211225
if (\is_object($v)) {
@@ -225,6 +239,16 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra
225239
return $a;
226240
}
227241

242+
public static function castClassConstant(\ReflectionClassConstant $c, array $a, Stub $stub, bool $isNested)
243+
{
244+
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
245+
$a[Caster::PREFIX_VIRTUAL.'value'] = $c->getValue();
246+
247+
self::addAttributes($a, $c);
248+
249+
return $a;
250+
}
251+
228252
public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, bool $isNested)
229253
{
230254
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
@@ -243,6 +267,8 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st
243267
'allowsNull' => 'allowsNull',
244268
]);
245269

270+
self::addAttributes($a, $c, $prefix);
271+
246272
if ($v = $c->getType()) {
247273
$a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v;
248274
}
@@ -271,6 +297,8 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st
271297
public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, bool $isNested)
272298
{
273299
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
300+
301+
self::addAttributes($a, $c);
274302
self::addExtra($a, $c);
275303

276304
return $a;
@@ -377,7 +405,7 @@ private static function addExtra(array &$a, \Reflector $c)
377405
}
378406
}
379407

380-
private static function addMap(array &$a, \Reflector $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL)
408+
private static function addMap(array &$a, object $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL)
381409
{
382410
foreach ($map as $k => $m) {
383411
if (\PHP_VERSION_ID >= 80000 && 'isDisabled' === $k) {
@@ -389,4 +417,13 @@ private static function addMap(array &$a, \Reflector $c, array $map, string $pre
389417
}
390418
}
391419
}
420+
421+
private static function addAttributes(array &$a, \Reflector $c, string $prefix = Caster::PREFIX_VIRTUAL): void
422+
{
423+
if (\PHP_VERSION_ID >= 80000) {
424+
foreach ($c->getAttributes() as $n) {
425+
$a[$prefix.'attributes'][] = $n;
426+
}
427+
}
428+
}
392429
}

‎src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ abstract class AbstractCloner implements ClonerInterface
3232
'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'],
3333
'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'],
3434
'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'],
35+
'ReflectionAttribute' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castAttribute'],
3536
'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'],
3637
'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'],
38+
'ReflectionClassConstant' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClassConstant'],
3739
'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'],
3840
'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'],
3941
'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'],

‎src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php
+149-4Lines changed: 149 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\VarDumper\Caster\Caster;
1616
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
1717
use Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo;
18+
use Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes;
1819
use Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass;
1920

2021
/**
@@ -36,9 +37,24 @@ public function testReflectionCaster()
3637
0 => "Reflector"
3738
%A]
3839
constants: array:3 [
39-
"IS_IMPLICIT_ABSTRACT" => 16
40-
"IS_EXPLICIT_ABSTRACT" => %d
41-
"IS_FINAL" => %d
40+
0 => ReflectionClassConstant {
41+
+name: "IS_IMPLICIT_ABSTRACT"
42+
+class: "ReflectionClass"
43+
modifiers: "public"
44+
value: 16
45+
}
46+
1 => ReflectionClassConstant {
47+
+name: "IS_EXPLICIT_ABSTRACT"
48+
+class: "ReflectionClass"
49+
modifiers: "public"
50+
value: %d
51+
}
52+
2 => ReflectionClassConstant {
53+
+name: "IS_FINAL"
54+
+class: "ReflectionClass"
55+
modifiers: "public"
56+
value: %d
57+
}
4258
]
4359
properties: array:%d [
4460
"name" => ReflectionProperty {
@@ -75,7 +91,7 @@ public function testClosureCaster()
7591
$b: & 123
7692
}
7793
file: "%sReflectionCasterTest.php"
78-
line: "68 to 68"
94+
line: "84 to 84"
7995
}
8096
EOTXT
8197
, $var
@@ -242,6 +258,135 @@ public function testGenerator()
242258
$this->assertDumpMatchesFormat($expectedDump, $generator);
243259
}
244260

261+
/**
262+
* @requires PHP 8
263+
*/
264+
public function testReflectionClassWithAttribute()
265+
{
266+
$var = new \ReflectionClass(LotsOfAttributes::class);
267+
268+
$this->assertDumpMatchesFormat(<<< 'EOTXT'
269+
ReflectionClass {
270+
+name: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes"
271+
%A attributes: array:1 [
272+
0 => ReflectionAttribute {
273+
name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute"
274+
arguments: []
275+
}
276+
]
277+
%A
278+
}
279+
EOTXT
280+
, $var);
281+
}
282+
283+
/**
284+
* @requires PHP 8
285+
*/
286+
public function testReflectionMethodWithAttribute()
287+
{
288+
$var = new \ReflectionMethod(LotsOfAttributes::class, 'someMethod');
289+
290+
$this->assertDumpMatchesFormat(<<< 'EOTXT'
291+
ReflectionMethod {
292+
+name: "someMethod"
293+
+class: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes"
294+
%A attributes: array:1 [
295+
0 => ReflectionAttribute {
296+
name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute"
297+
arguments: array:1 [
298+
0 => "two"
299+
]
300+
}
301+
]
302+
%A
303+
}
304+
EOTXT
305+
, $var);
306+
}
307+
308+
/**
309+
* @requires PHP 8
310+
*/
311+
public function testReflectionPropertyWithAttribute()
312+
{
313+
$var = new \ReflectionProperty(LotsOfAttributes::class, 'someProperty');
314+
315+
$this->assertDumpMatchesFormat(<<< 'EOTXT'
316+
ReflectionProperty {
317+
+name: "someProperty"
318+
+class: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes"
319+
%A attributes: array:1 [
320+
0 => ReflectionAttribute {
321+
name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute"
322+
arguments: array:2 [
323+
0 => "one"
324+
"extra" => "hello"
325+
]
326+
}
327+
]
328+
}
329+
EOTXT
330+
, $var);
331+
}
332+
333+
/**
334+
* @requires PHP 8
335+
*/
336+
public function testReflectionClassConstantWithAttribute()
337+
{
338+
$var = new \ReflectionClassConstant(LotsOfAttributes::class, 'SOME_CONSTANT');
339+
340+
$this->assertDumpMatchesFormat(<<< 'EOTXT'
341+
ReflectionClassConstant {
342+
+name: "SOME_CONSTANT"
343+
+class: "Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes"
344+
modifiers: "public"
345+
value: "some value"
346+
attributes: array:2 [
347+
0 => ReflectionAttribute {
348+
name: "Symfony\Component\VarDumper\Tests\Fixtures\RepeatableAttribute"
349+
arguments: array:1 [
350+
0 => "one"
351+
]
352+
}
353+
1 => ReflectionAttribute {
354+
name: "Symfony\Component\VarDumper\Tests\Fixtures\RepeatableAttribute"
355+
arguments: array:1 [
356+
0 => "two"
357+
]
358+
}
359+
]
360+
}
361+
EOTXT
362+
, $var);
363+
}
364+
365+
/**
366+
* @requires PHP 8
367+
*/
368+
public function testReflectionParameterWithAttribute()
369+
{
370+
$var = new \ReflectionParameter([LotsOfAttributes::class, 'someMethod'], 'someParameter');
371+
372+
$this->assertDumpMatchesFormat(<<< 'EOTXT'
373+
ReflectionParameter {
374+
+name: "someParameter"
375+
position: 0
376+
attributes: array:1 [
377+
0 => ReflectionAttribute {
378+
name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute"
379+
arguments: array:1 [
380+
0 => "three"
381+
]
382+
}
383+
]
384+
%A
385+
}
386+
EOTXT
387+
, $var);
388+
}
389+
245390
public static function stub(): void
246391
{
247392
}
+28Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\VarDumper\Tests\Fixtures;
13+
14+
#[MyAttribute]
15+
final class LotsOfAttributes
16+
{
17+
#[RepeatableAttribute('one'), RepeatableAttribute('two')]
18+
public const SOME_CONSTANT = 'some value';
19+
20+
#[MyAttribute('one', extra: 'hello')]
21+
private string $someProperty;
22+
23+
#[MyAttribute('two')]
24+
public function someMethod(
25+
#[MyAttribute('three')] string $someParameter
26+
): void {
27+
}
28+
}
+34Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\VarDumper\Tests\Fixtures;
13+
14+
use Attribute;
15+
16+
#[Attribute]
17+
final class MyAttribute
18+
{
19+
public function __construct(
20+
private string $foo = 'default',
21+
private ?string $extra = null,
22+
) {
23+
}
24+
25+
public function getFoo(): string
26+
{
27+
return $this->foo;
28+
}
29+
30+
public function getExtra(): ?string
31+
{
32+
return $this->extra;
33+
}
34+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.