Skip to content

Commit 973fbb3

Browse files
feature #16325 [VarDumper] Casters for Generator, ReflectionGenerator and ReflectionType (nicolas-grekas)
This PR was merged into the 2.8 branch. Discussion ---------- [VarDumper] Casters for Generator, ReflectionGenerator and ReflectionType | Q | A | ------------- | --- | Bug fix? | yes | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - I had this on a local branch since a few days: - on PHP7, using ReflectionGenerator allows dumping interesting state info about generators (see test case for example output) - caster for ReflectionType added - source code excerpts are now left-trimmed (see adjusted test cases) Commits ------- d6c2d75 [VarDumper] Casters for Generator, ReflectionGenerator and ReflectionType
2 parents 9a1574a + d6c2d75 commit 973fbb3

File tree

10 files changed

+215
-44
lines changed

10 files changed

+215
-44
lines changed

src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php

+28-15
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222
class ExceptionCaster
2323
{
24+
public static $srcContext = 1;
2425
public static $traceArgs = true;
2526
public static $errorTypes = array(
2627
E_DEPRECATED => 'E_DEPRECATED',
@@ -71,7 +72,7 @@ public static function castThrowingCasterException(ThrowingCasterException $e, a
7172
'file' => $b[$prefix.'file'],
7273
'line' => $b[$prefix.'line'],
7374
));
74-
$a[$xPrefix.'trace'] = new TraceStub($b[$xPrefix.'trace'], 1, false, 0, -1 - count($a[$xPrefix.'trace']->value));
75+
$a[$xPrefix.'trace'] = new TraceStub($b[$xPrefix.'trace'], false, 0, -1 - count($a[$xPrefix.'trace']->value));
7576
}
7677

7778
unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']);
@@ -90,15 +91,15 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
9091

9192
$a = array();
9293
$j = count($frames);
93-
if (0 > $i = $trace->offset) {
94+
if (0 > $i = $trace->sliceOffset) {
9495
$i = max(0, $j + $i);
9596
}
9697
if (!isset($trace->value[$i])) {
9798
return array();
9899
}
99100
$lastCall = isset($frames[$i]['function']) ? ' ==> '.(isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '';
100101

101-
for ($j -= $i++; isset($frames[$i]); ++$i, --$j) {
102+
for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) {
102103
$call = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[$i]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '???';
103104

104105
$a[Caster::PREFIX_VIRTUAL.$j.'. '.$call.$lastCall] = new FrameStub(
@@ -108,7 +109,6 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
108109
'type' => isset($frames[$i]['type']) ? $frames[$i]['type'] : null,
109110
'function' => isset($frames[$i]['function']) ? $frames[$i]['function'] : null,
110111
) + $frames[$i - 1],
111-
$trace->srcContext,
112112
$trace->keepArgs,
113113
true
114114
);
@@ -122,12 +122,11 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
122122
'type' => null,
123123
'function' => '{main}',
124124
) + $frames[$i - 1],
125-
$trace->srcContext,
126125
$trace->keepArgs,
127126
true
128127
);
129-
if (null !== $trace->length) {
130-
$a = array_slice($a, 0, $trace->length, true);
128+
if (null !== $trace->sliceLength) {
129+
$a = array_slice($a, 0, $trace->sliceLength, true);
131130
}
132131

133132
return $a;
@@ -146,8 +145,8 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is
146145
$f['file'] = substr($f['file'], 0, -strlen($match[0]));
147146
$f['line'] = (int) $match[1];
148147
}
149-
if (file_exists($f['file']) && 0 <= $frame->srcContext) {
150-
$src[$f['file'].':'.$f['line']] = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], $frame->srcContext);
148+
if (file_exists($f['file']) && 0 <= self::$srcContext) {
149+
$src[$f['file'].':'.$f['line']] = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], self::$srcContext);
151150

152151
if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig_Template') && method_exists($f['class'], 'getDebugInfo')) {
153152
$template = isset($f['object']) ? $f['object'] : new $f['class'](new \Twig_Environment(new \Twig_Loader_Filesystem()));
@@ -157,7 +156,7 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is
157156
$templateSrc = explode("\n", method_exists($template, 'getSource') ? $template->getSource() : $template->getEnvironment()->getLoader()->getSource($templateName));
158157
$templateInfo = $template->getDebugInfo();
159158
if (isset($templateInfo[$f['line']])) {
160-
$src[$templateName.':'.$templateInfo[$f['line']]] = self::extractSource($templateSrc, $templateInfo[$f['line']], $frame->srcContext);
159+
$src[$templateName.':'.$templateInfo[$f['line']]] = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext);
161160
}
162161
} catch (\Twig_Error_Loader $e) {
163162
}
@@ -247,15 +246,29 @@ private static function filterExceptionArray($xClass, array $a, $xPrefix, $filte
247246

248247
private static function extractSource(array $srcArray, $line, $srcContext)
249248
{
250-
$src = '';
249+
$src = array();
251250

252251
for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) {
253-
$src .= (isset($srcArray[$i]) ? $srcArray[$i] : '')."\n";
252+
$src[] = (isset($srcArray[$i]) ? $srcArray[$i] : '')."\n";
254253
}
255-
if (!$srcContext) {
256-
$src = trim($src);
254+
255+
$ltrim = 0;
256+
while (' ' === $src[0][$ltrim] || "\t" === $src[0][$ltrim]) {
257+
$i = $srcContext << 1;
258+
while ($i > 0 && $src[0][$ltrim] === $src[$i][$ltrim]) {
259+
--$i;
260+
}
261+
if ($i) {
262+
break;
263+
}
264+
++$ltrim;
265+
}
266+
if ($ltrim) {
267+
foreach ($src as $i => $line) {
268+
$src[$i] = substr($line, $ltrim);
269+
}
257270
}
258271

259-
return $src;
272+
return implode('', $src);
260273
}
261274
}

src/Symfony/Component/VarDumper/Caster/FrameStub.php

+2-4
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,12 @@
1818
*/
1919
class FrameStub extends EnumStub
2020
{
21-
public $srcContext;
2221
public $keepArgs;
2322
public $inTraceStub;
2423

25-
public function __construct(array $trace, $srcContext = 1, $keepArgs = true, $inTraceStub = false)
24+
public function __construct(array $frame, $keepArgs = true, $inTraceStub = false)
2625
{
27-
$this->value = $trace;
28-
$this->srcContext = $srcContext;
26+
$this->value = $frame;
2927
$this->keepArgs = $keepArgs;
3028
$this->inTraceStub = $inTraceStub;
3129
}

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

+53
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,59 @@ public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested)
7474
return $a;
7575
}
7676

77+
public static function castGenerator(\Generator $c, array $a, Stub $stub, $isNested)
78+
{
79+
return class_exists('ReflectionGenerator', false) ? self::castReflectionGenerator(new \ReflectionGenerator($c), $a, $stub, $isNested) : $a;
80+
}
81+
82+
public static function castType(\ReflectionType $c, array $a, Stub $stub, $isNested)
83+
{
84+
$prefix = Caster::PREFIX_VIRTUAL;
85+
86+
$a += array(
87+
$prefix.'type' => $c->__toString(),
88+
$prefix.'allowsNull' => $c->allowsNull(),
89+
$prefix.'isBuiltin' => $c->isBuiltin(),
90+
);
91+
92+
return $a;
93+
}
94+
95+
public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, $isNested)
96+
{
97+
$prefix = Caster::PREFIX_VIRTUAL;
98+
99+
if ($c->getThis()) {
100+
$a[$prefix.'this'] = new CutStub($c->getThis());
101+
}
102+
$x = $c->getFunction();
103+
$frame = array(
104+
'class' => isset($x->class) ? $x->class : null,
105+
'type' => isset($x->class) ? ($x->isStatic() ? '::' : '->') : null,
106+
'function' => $x->name,
107+
'file' => $c->getExecutingFile(),
108+
'line' => $c->getExecutingLine(),
109+
);
110+
if ($trace = $c->getTrace(DEBUG_BACKTRACE_IGNORE_ARGS)) {
111+
$x = new \ReflectionGenerator($c->getExecutingGenerator());
112+
array_unshift($trace, array(
113+
'function' => 'yield',
114+
'file' => $x->getExecutingFile(),
115+
'line' => $x->getExecutingLine() - 1,
116+
));
117+
$trace[] = $frame;
118+
$a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1);
119+
} else {
120+
$x = new FrameStub($frame, false, true);
121+
$x = ExceptionCaster::castFrameStub($x, array(), $x, true);
122+
$a[$prefix.'executing'] = new EnumStub(array(
123+
$frame['class'].$frame['type'].$frame['function'].'()' => $x[$prefix.'src'],
124+
));
125+
}
126+
127+
return $a;
128+
}
129+
77130
public static function castClass(\ReflectionClass $c, array $a, Stub $stub, $isNested, $filter = 0)
78131
{
79132
$prefix = Caster::PREFIX_VIRTUAL;

src/Symfony/Component/VarDumper/Caster/StubCaster.php

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public static function castEnum(EnumStub $c, array $a, Stub $stub, $isNested)
5454
if ($isNested) {
5555
$stub->class = '';
5656
$stub->handle = 0;
57+
$stub->value = null;
5758

5859
$a = array();
5960

src/Symfony/Component/VarDumper/Caster/TraceStub.php

+7-7
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@
2020
*/
2121
class TraceStub extends Stub
2222
{
23-
public $srcContext;
2423
public $keepArgs;
25-
public $offset;
26-
public $length;
24+
public $sliceOffset;
25+
public $sliceLength;
26+
public $numberingOffset;
2727

28-
public function __construct(array $trace, $srcContext = 1, $keepArgs = true, $offset = 0, $length = null)
28+
public function __construct(array $trace, $keepArgs = true, $sliceOffset = 0, $sliceLength = null, $numberingOffset = 0)
2929
{
3030
$this->value = $trace;
31-
$this->srcContext = $srcContext;
3231
$this->keepArgs = $keepArgs;
33-
$this->offset = $offset;
34-
$this->length = $length;
32+
$this->sliceOffset = $sliceOffset;
33+
$this->sliceLength = $sliceLength;
34+
$this->numberingOffset = $numberingOffset;
3535
}
3636
}

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

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ abstract class AbstractCloner implements ClonerInterface
2828
'Symfony\Component\VarDumper\Caster\EnumStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castEnum',
2929

3030
'Closure' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure',
31+
'Generator' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castGenerator',
32+
'ReflectionType' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castType',
33+
'ReflectionGenerator' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castReflectionGenerator',
3134
'ReflectionClass' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClass',
3235
'ReflectionFunctionAbstract' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castFunctionAbstract',
3336
'ReflectionMethod' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castMethod',

src/Symfony/Component/VarDumper/Cloner/VarCloner.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ protected function doClone($var)
149149
$stub->handle = $h;
150150
$a = $this->castObject($stub, 0 < $i);
151151
if ($v !== $stub->value) {
152-
if (Stub::TYPE_OBJECT !== $stub->type) {
152+
if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) {
153153
break;
154154
}
155155
if ($useExt) {

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

+84-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\VarDumper\Tests\Caster;
1313

1414
use Symfony\Component\VarDumper\Test\VarDumperTestCase;
15+
use Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo;
1516

1617
/**
1718
* @author Nicolas Grekas <p@tchwork.com>
@@ -72,7 +73,7 @@ public function testClosureCaster()
7273
\$b: & 123
7374
}
7475
file: "%sReflectionCasterTest.php"
75-
line: "62 to 62"
76+
line: "63 to 63"
7677
}
7778
EOTXT
7879
, $var
@@ -92,11 +93,92 @@ public function testReturnType()
9293
returnType: "int"
9394
class: "Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest"
9495
this: Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest { …}
95-
file: "%sReflectionCasterTest.php(87) : eval()'d code"
96+
file: "%sReflectionCasterTest.php(88) : eval()'d code"
9697
line: "1 to 1"
9798
}
9899
EOTXT
99100
, $f
100101
);
101102
}
103+
104+
/**
105+
* @requires PHP 7.0
106+
*/
107+
public function testGenerator()
108+
{
109+
$g = new GeneratorDemo();
110+
$g = $g->baz();
111+
$r = new \ReflectionGenerator($g);
112+
113+
$xDump = <<<'EODUMP'
114+
Generator {
115+
this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …}
116+
executing: {
117+
Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz(): {
118+
%sGeneratorDemo.php:14: """
119+
{\n
120+
yield from bar();\n
121+
}\n
122+
"""
123+
}
124+
}
125+
}
126+
EODUMP;
127+
128+
$this->assertDumpMatchesFormat($xDump, $g);
129+
130+
foreach ($g as $v) {
131+
break;
132+
}
133+
134+
$xDump = <<<'EODUMP'
135+
array:2 [
136+
0 => ReflectionGenerator {
137+
this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …}
138+
trace: {
139+
3. Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo() ==> yield(): {
140+
src: {
141+
%sGeneratorDemo.php:9: """
142+
{\n
143+
yield 1;\n
144+
}\n
145+
"""
146+
}
147+
}
148+
2. Symfony\Component\VarDumper\Tests\Fixtures\bar() ==> Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo(): {
149+
src: {
150+
%sGeneratorDemo.php:20: """
151+
{\n
152+
yield from GeneratorDemo::foo();\n
153+
}\n
154+
"""
155+
}
156+
}
157+
1. Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() ==> Symfony\Component\VarDumper\Tests\Fixtures\bar(): {
158+
src: {
159+
%sGeneratorDemo.php:14: """
160+
{\n
161+
yield from bar();\n
162+
}\n
163+
"""
164+
}
165+
}
166+
}
167+
}
168+
1 => Generator {
169+
executing: {
170+
Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo(): {
171+
%sGeneratorDemo.php:10: """
172+
yield 1;\n
173+
}\n
174+
\n
175+
"""
176+
}
177+
}
178+
}
179+
]
180+
EODUMP;
181+
182+
$this->assertDumpMatchesFormat($xDump, array($r, $r->getExecutingGenerator()));
183+
}
102184
}

0 commit comments

Comments
 (0)