Skip to content

Commit 0beb598

Browse files
committed
feature symfony#23967 [VarDumper] add force-collapse/expand + use it for traces (nicolas-grekas)
This PR was merged into the 3.4 branch. Discussion ---------- [VarDumper] add force-collapse/expand + use it for traces | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | not yet | Fixed tickets | - | License | MIT | Doc PR | - This improves dumping stack traces by expanding only frames that are outside of vendor dirs. Here the new CLI output (everything was expanded before): ![capture du 2017-08-24 15-24-01](https://user-images.githubusercontent.com/243674/29668407-614dfe32-88e0-11e7-85b3-8b4f774559a5.png) And here is the HTML output, with toggle-able frames: ![capture du 2017-08-24 15-24-40](https://user-images.githubusercontent.com/243674/29668410-65170c16-88e0-11e7-9388-147475a76b9f.png) Todo: - [x] make tests pass :) Commits ------- 0e2d2b8 [VarDumper] add force-collapse/expand + use it for traces
2 parents 4be4070 + 0e2d2b8 commit 0beb598

File tree

10 files changed

+133
-109
lines changed

10 files changed

+133
-109
lines changed

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

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
127127
}
128128
$lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '';
129129
$frames[] = array('function' => '');
130+
$collapse = false;
130131

131132
for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) {
132133
$f = $frames[$i];
@@ -145,6 +146,13 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
145146
$f = self::castFrameStub($frame, array(), $frame, true);
146147
if (isset($f[$prefix.'src'])) {
147148
foreach ($f[$prefix.'src']->value as $label => $frame) {
149+
if (0 === strpos($label, "\0~collapse=0")) {
150+
if ($collapse) {
151+
$label = substr_replace($label, '1', 11, 1);
152+
} else {
153+
$collapse = true;
154+
}
155+
}
148156
$label = substr_replace($label, "title=Stack level $j.&", 2, 0);
149157
}
150158
$f = $frames[$i - 1];
@@ -162,7 +170,7 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
162170
} else {
163171
$label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall;
164172
}
165-
$a[$label] = $frame;
173+
$a[substr_replace($label, sprintf('separator=%s&', $frame instanceof EnumStub ? ' ' : ':'), 2, 0)] = $frame;
166174

167175
$lastCall = $call;
168176
}
@@ -197,9 +205,10 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is
197205
$caller = isset($f['function']) ? sprintf('in %s() on line %d', (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'], $f['line']) : null;
198206
$src = $f['line'];
199207
$srcKey = $f['file'];
200-
$ellipsis = (new LinkStub($srcKey, 0))->attr;
201-
$ellipsisTail = isset($ellipsis['ellipsis-tail']) ? $ellipsis['ellipsis-tail'] : 0;
202-
$ellipsis = isset($ellipsis['ellipsis']) ? $ellipsis['ellipsis'] : 0;
208+
$ellipsis = new LinkStub($srcKey, 0);
209+
$srcAttr = 'collapse='.(int) $ellipsis->inVendor;
210+
$ellipsisTail = isset($ellipsis->attr['ellipsis-tail']) ? $ellipsis->attr['ellipsis-tail'] : 0;
211+
$ellipsis = isset($ellipsis->attr['ellipsis']) ? $ellipsis->attr['ellipsis'] : 0;
203212

204213
if (file_exists($f['file']) && 0 <= self::$srcContext) {
205214
if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) {
@@ -225,8 +234,11 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is
225234
$ellipsis += 1 + strlen($f['line']);
226235
}
227236
}
237+
$srcAttr .= '&separator= ';
238+
} else {
239+
$srcAttr .= '&separator=:';
228240
}
229-
$srcAttr = $ellipsis ? 'ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : '';
241+
$srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : '';
230242
self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(array("\0~$srcAttr\0$srcKey" => $src));
231243
}
232244
}
@@ -329,7 +341,7 @@ private static function extractSource($srcLines, $line, $srcContext, $title, $la
329341
}
330342
}
331343
$c->attr['lang'] = $lang;
332-
$srcLines[sprintf("\0~%d\0", $i + $line - $srcContext)] = $c;
344+
$srcLines[sprintf("\0~separator=› &%d\0", $i + $line - $srcContext)] = $c;
333345
}
334346

335347
return new EnumStub($srcLines);

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*/
1919
class LinkStub extends ConstStub
2020
{
21+
public $inVendor = false;
22+
2123
private static $vendorRoots;
2224
private static $composerRoots;
2325

@@ -50,10 +52,10 @@ public function __construct($label, $line = 0, $href = null)
5052
if ($label !== $this->attr['file'] = realpath($href) ?: $href) {
5153
return;
5254
}
53-
if ($composerRoot = $this->getComposerRoot($href, $inVendor)) {
55+
if ($composerRoot = $this->getComposerRoot($href, $this->inVendor)) {
5456
$this->attr['ellipsis'] = strlen($href) - strlen($composerRoot) + 1;
5557
$this->attr['ellipsis-type'] = 'path';
56-
$this->attr['ellipsis-tail'] = 1 + ($inVendor ? 2 + strlen(implode(array_slice(explode(DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0);
58+
$this->attr['ellipsis-tail'] = 1 + ($this->inVendor ? 2 + strlen(implode(array_slice(explode(DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0);
5759
} elseif (3 < count($ellipsis = explode(DIRECTORY_SEPARATOR, $href))) {
5860
$this->attr['ellipsis'] = 2 + strlen(implode(array_slice($ellipsis, -2)));
5961
$this->attr['ellipsis-type'] = 'path';

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public static function castReflectionGenerator(\ReflectionGenerator $c, array $a
122122
$function = new FrameStub($frame, false, true);
123123
$function = ExceptionCaster::castFrameStub($function, array(), $function, true);
124124
$a[$prefix.'executing'] = new EnumStub(array(
125-
$frame['class'].$frame['type'].$frame['function'].'()' => $function[$prefix.'src'],
125+
"\0~separator= \0".$frame['class'].$frame['type'].$frame['function'].'()' => $function[$prefix.'src'],
126126
));
127127
}
128128

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ class Cursor
3939
public $hashCut = 0;
4040
public $stop = false;
4141
public $attr = array();
42+
public $skipChildren = false;
4243
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,16 @@ private function dumpItem($dumper, $cursor, &$refs, $item)
355355
$withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth;
356356
$dumper->enterHash($cursor, $item->type, $item->class, $withChildren);
357357
if ($withChildren) {
358-
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class);
358+
if ($cursor->skipChildren) {
359+
$withChildren = false;
360+
$cut = -1;
361+
} else {
362+
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class);
363+
}
359364
} elseif ($children && 0 <= $cut) {
360365
$cut += count($children);
361366
}
367+
$cursor->skipChildren = false;
362368
$dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut);
363369
break;
364370

src/Symfony/Component/VarDumper/Dumper/CliDumper.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ class CliDumper extends AbstractDumper
5151
"\033" => '\e',
5252
);
5353

54+
protected $collapseNextHash = false;
55+
protected $expandNextHash = false;
56+
5457
/**
5558
* {@inheritdoc}
5659
*/
@@ -253,6 +256,11 @@ public function enterHash(Cursor $cursor, $type, $class, $hasChild)
253256
{
254257
$this->dumpKey($cursor);
255258

259+
if ($this->collapseNextHash) {
260+
$cursor->skipChildren = true;
261+
$this->collapseNextHash = $hasChild = false;
262+
}
263+
256264
$class = $this->utf8Encode($class);
257265
if (Cursor::HASH_OBJECT === $type) {
258266
$prefix = $class && 'stdClass' !== $class ? $this->style('note', $class).' {' : '{';
@@ -368,7 +376,15 @@ protected function dumpKey(Cursor $cursor)
368376
break;
369377
}
370378

371-
$this->line .= $bin.$this->style($style, $key[1], $attr).': ';
379+
if (isset($attr['collapse'])) {
380+
if ($attr['collapse']) {
381+
$this->collapseNextHash = true;
382+
} else {
383+
$this->expandNextHash = true;
384+
}
385+
}
386+
387+
$this->line .= $bin.$this->style($style, $key[1], $attr).(isset($attr['separator']) ? $attr['separator'] : ': ');
372388
} else {
373389
// This case should not happen
374390
$this->line .= '-'.$bin.'"'.$this->style('private', $key, array('class' => '')).'": ';
@@ -397,6 +413,21 @@ protected function style($style, $value, $attr = array())
397413
$this->colors = $this->supportsColors();
398414
}
399415

416+
if (isset($attr['ellipsis'], $attr['ellipsis-type'])) {
417+
$prefix = substr($value, 0, -$attr['ellipsis']);
418+
if ('cli' === PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && 0 === strpos($prefix, $_SERVER[$pwd])) {
419+
$prefix = '.'.substr($prefix, strlen($_SERVER[$pwd]));
420+
}
421+
if (!empty($attr['ellipsis-tail'])) {
422+
$prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']);
423+
$value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']);
424+
} else {
425+
$value = substr($value, -$attr['ellipsis']);
426+
}
427+
428+
return $this->style('default', $prefix).$this->style($style, $value);
429+
}
430+
400431
$style = $this->styles[$style];
401432

402433
$map = static::$controlCharsMap;

src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,6 @@ function xpathString(str) {
366366
for (i = 0; i < len; ++i) {
367367
elt = t[i];
368368
if ('SAMP' == elt.tagName) {
369-
elt.className = 'sf-dump-expanded';
370369
a = elt.previousSibling || {};
371370
if ('A' != a.tagName) {
372371
a = doc.createElement('A');
@@ -384,7 +383,8 @@ function xpathString(str) {
384383
x += elt.parentNode.getAttribute('data-depth')/1;
385384
}
386385
elt.setAttribute('data-depth', x);
387-
if (x > options.maxDepth) {
386+
if (elt.className ? 'sf-dump-expanded' !== elt.className : (x > options.maxDepth)) {
387+
elt.className = 'sf-dump-expanded';
388388
toggle(a);
389389
}
390390
} else if (/\bsf-dump-ref\b/.test(elt.className) && (a = elt.getAttribute('href'))) {
@@ -728,15 +728,25 @@ public function enterHash(Cursor $cursor, $type, $class, $hasChild)
728728
{
729729
parent::enterHash($cursor, $type, $class, false);
730730

731+
if ($cursor->skipChildren) {
732+
$cursor->skipChildren = false;
733+
$eol = ' class=sf-dump-compact>';
734+
} elseif ($this->expandNextHash) {
735+
$this->expandNextHash = false;
736+
$eol = ' class=sf-dump-expanded>';
737+
} else {
738+
$eol = '>';
739+
}
740+
731741
if ($hasChild) {
742+
$this->line .= '<samp';
732743
if ($cursor->refIndex) {
733744
$r = Cursor::HASH_OBJECT !== $type ? 1 - (Cursor::HASH_RESOURCE !== $type) : 2;
734745
$r .= $r && 0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->refIndex;
735746

736-
$this->line .= sprintf('<samp id=%s-ref%s>', $this->dumpId, $r);
737-
} else {
738-
$this->line .= '<samp>';
747+
$this->line .= sprintf(' id=%s-ref%s', $this->dumpId, $r);
739748
}
749+
$this->line .= $eol;
740750
$this->dumpLine($cursor->depth);
741751
}
742752
}

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

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,13 @@ public function testDefaultSettings()
4646
#file: "%sExceptionCasterTest.php"
4747
#line: 28
4848
trace: {
49-
%sExceptionCasterTest.php:28: {
50-
: {
51-
: return new \Exception(''.$msg);
52-
: }
53-
}
54-
%sExceptionCasterTest.php:%d: {
55-
: $ref = array('foo');
56-
: $e = $this->getTestException('foo', $ref);
57-
:
58-
arguments: {
59-
$msg: "foo"
60-
&$ref: array:1 [ …1]
61-
}
49+
%s%eTests%eCaster%eExceptionCasterTest.php:28 {
50+
› {
51+
› return new \Exception(''.$msg);
52+
› }
6253
}
54+
%s%eTests%eCaster%eExceptionCasterTest.php:40 { …}
55+
Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->testDefaultSettings() {}
6356
%A
6457
EODUMP;
6558

@@ -73,19 +66,13 @@ public function testSeek()
7366

7467
$expectedDump = <<<'EODUMP'
7568
{
76-
%sExceptionCasterTest.php:28: {
77-
: {
78-
: return new \Exception(''.$msg);
79-
: }
80-
}
81-
%sExceptionCasterTest.php:%d: {
82-
: {
83-
: $e = $this->getTestException(2);
84-
:
85-
arguments: {
86-
$msg: 2
87-
}
69+
%s%eTests%eCaster%eExceptionCasterTest.php:28 {
70+
› {
71+
› return new \Exception(''.$msg);
72+
› }
8873
}
74+
%s%eTests%eCaster%eExceptionCasterTest.php:65 { …}
75+
Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->testSeek() {}
8976
%A
9077
EODUMP;
9178

@@ -104,16 +91,13 @@ public function testNoArgs()
10491
#file: "%sExceptionCasterTest.php"
10592
#line: 28
10693
trace: {
107-
%sExceptionCasterTest.php:28: {
108-
: {
109-
: return new \Exception(''.$msg);
110-
: }
111-
}
112-
%sExceptionCasterTest.php:%d: {
113-
: {
114-
: $e = $this->getTestException(1);
115-
: ExceptionCaster::$traceArgs = false;
94+
%sExceptionCasterTest.php:28 {
95+
› {
96+
› return new \Exception(''.$msg);
97+
› }
11698
}
99+
%s%eTests%eCaster%eExceptionCasterTest.php:84 { …}
100+
Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->testNoArgs() {}
117101
%A
118102
EODUMP;
119103

@@ -132,8 +116,8 @@ public function testNoSrcContext()
132116
#file: "%sExceptionCasterTest.php"
133117
#line: 28
134118
trace: {
135-
%sExceptionCasterTest.php: 28
136-
%sExceptionCasterTest.php: %d
119+
%s%eTests%eCaster%eExceptionCasterTest.php:28
120+
%s%eTests%eCaster%eExceptionCasterTest.php:%d
137121
%A
138122
EODUMP;
139123

@@ -161,7 +145,7 @@ public function testHtmlDump()
161145
#<span class=sf-dump-protected title="Protected property">line</span>: <span class=sf-dump-num>28</span>
162146
<span class=sf-dump-meta>trace</span>: {<samp>
163147
<span class=sf-dump-meta title="%sExceptionCasterTest.php
164-
Stack level %d."><span class="sf-dump-ellipsis sf-dump-ellipsis-path">%s%eVarDumper</span><span class=sf-dump-ellipsis>%e</span>Tests%eCaster%eExceptionCasterTest.php</span>: <span class=sf-dump-num>28</span>
148+
Stack level %d."><span class="sf-dump-ellipsis sf-dump-ellipsis-path">%s%eVarDumper</span><span class=sf-dump-ellipsis>%e</span>Tests%eCaster%eExceptionCasterTest.php</span>:<span class=sf-dump-num>28</span>
165149
&hellip;%d
166150
</samp>}
167151
</samp>}
@@ -197,10 +181,10 @@ public function testFrameWithTwig()
197181
0 => {
198182
class: "__TwigTemplate_VarDumperFixture_u75a09"
199183
src: {
200-
%sTwig.php:1: {
201-
:
202-
: foo bar
203-
: twig source
184+
%sTwig.php:1 {
185+
186+
foo bar
187+
twig source
204188
}
205189
}
206190
}
@@ -210,10 +194,10 @@ class: "__TwigTemplate_VarDumperFixture_u75a09"
210194
%A
211195
}
212196
src: {
213-
%sExceptionCasterTest.php:2: {
214-
: foo bar
215-
: twig source
216-
:
197+
%sExceptionCasterTest.php:2 {
198+
foo bar
199+
twig source
200+
217201
}
218202
}
219203
}

0 commit comments

Comments
 (0)