feature #19289 [VarDumper] Dumping exceptions is now more compact (nicolas-grekas)

This PR was merged into the 3.2-dev branch.

Discussion
----------

[VarDumper] Dumping exceptions is now more compact

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | no
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Before:
![before](https://cloud.githubusercontent.com/assets/243674/16578686/2c7285b0-429c-11e6-9139-293e0c899d43.png)

Commits
-------

19e9cbe [VarDumper] Dumping exceptions is now more compact
This commit is contained in:
Nicolas Grekas 2016-07-28 09:26:33 +02:00
commit c7fa99e41f
8 changed files with 207 additions and 111 deletions

View File

@ -20,8 +20,11 @@ use Symfony\Component\VarDumper\Cloner\Stub;
*/
class EnumStub extends Stub
{
public function __construct(array $values)
public $dumpKeys = true;
public function __construct(array $values, $dumpKeys = true)
{
$this->value = $values;
$this->dumpKeys = $dumpKeys;
}
}

View File

@ -88,6 +88,7 @@ class ExceptionCaster
$stub->class = '';
$stub->handle = 0;
$frames = $trace->value;
$prefix = Caster::PREFIX_VIRTUAL;
$a = array();
$j = count($frames);
@ -98,33 +99,35 @@ class ExceptionCaster
return array();
}
$lastCall = isset($frames[$i]['function']) ? ' ==> '.(isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '';
$frames[] = array('function' => '');
for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) {
$call = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[$i]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '???';
$f = $frames[$i];
$call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'].'()' : '???';
$a[Caster::PREFIX_VIRTUAL.$j.'. '.$call.$lastCall] = new FrameStub(
$label = $call.$lastCall;
$frame = new FrameStub(
array(
'object' => isset($frames[$i]['object']) ? $frames[$i]['object'] : null,
'class' => isset($frames[$i]['class']) ? $frames[$i]['class'] : null,
'type' => isset($frames[$i]['type']) ? $frames[$i]['type'] : null,
'function' => isset($frames[$i]['function']) ? $frames[$i]['function'] : null,
'object' => isset($f['object']) ? $f['object'] : null,
'class' => isset($f['class']) ? $f['class'] : null,
'type' => isset($f['type']) ? $f['type'] : null,
'function' => isset($f['function']) ? $f['function'] : null,
) + $frames[$i - 1],
$trace->keepArgs,
true
);
$f = self::castFrameStub($frame, array(), $frame, true);
if (isset($f[$prefix.'src'])) {
foreach ($f[$prefix.'src']->value as $label => $frame) {
}
if (isset($f[$prefix.'args']) && $frame instanceof EnumStub) {
$frame->value['args'] = $f[$prefix.'args'];
}
}
$a[$prefix.$j.'. '.$label] = $frame;
$lastCall = ' ==> '.$call;
}
$a[Caster::PREFIX_VIRTUAL.$j.'. {main}'.$lastCall] = new FrameStub(
array(
'object' => null,
'class' => null,
'type' => null,
'function' => '{main}',
) + $frames[$i - 1],
$trace->keepArgs,
true
);
if (null !== $trace->sliceLength) {
$a = array_slice($a, 0, $trace->sliceLength, true);
}
@ -145,9 +148,8 @@ class ExceptionCaster
$f['file'] = substr($f['file'], 0, -strlen($match[0]));
$f['line'] = (int) $match[1];
}
$src = array();
if (file_exists($f['file']) && 0 <= self::$srcContext) {
$src[$f['file'].':'.$f['line']] = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], self::$srcContext);
if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig_Template') && method_exists($f['class'], 'getDebugInfo')) {
$template = isset($f['object']) ? $f['object'] : new $f['class'](new \Twig_Environment(new \Twig_Loader_Filesystem()));
@ -156,11 +158,14 @@ class ExceptionCaster
$templateSrc = explode("\n", method_exists($template, 'getSource') ? $template->getSource() : $template->getEnvironment()->getLoader()->getSource($templateName));
$templateInfo = $template->getDebugInfo();
if (isset($templateInfo[$f['line']])) {
$src[$templateName.':'.$templateInfo[$f['line']]] = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext);
$src[$templateName] = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext);
}
} catch (\Twig_Error_Loader $e) {
}
}
if (!$src) {
$src[$f['file']] = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], self::$srcContext);
}
} else {
$src[$f['file']] = $f['line'];
}
@ -177,7 +182,7 @@ class ExceptionCaster
}
}
if ($frame->keepArgs && isset($f['args'])) {
$a[$prefix.'args'] = $f['args'];
$a[$prefix.'args'] = new EnumStub($f['args'], false);
}
return $a;
@ -232,12 +237,23 @@ class ExceptionCaster
++$ltrim;
} while (0 > $i && null !== $pad);
if (--$ltrim) {
foreach ($src as $i => $line) {
$src[$i] = isset($line[$ltrim]) && "\r" !== $line[$ltrim] ? substr($line, $ltrim) : ltrim($line, " \t");
--$ltrim;
$pad = strlen($line + $srcContext);
$srcArray = array();
foreach ($src as $i => $c) {
if ($ltrim) {
$c = isset($c[$ltrim]) && "\r" !== $c[$ltrim] ? substr($c, $ltrim) : ltrim($c, " \t");
}
$c = substr($c, 0, -1);
$c = new ConstStub($c, $c);
if ($i !== $srcContext) {
$c->class = 'default';
}
$srcArray[sprintf("% {$pad}d", $i + $line - $srcContext)] = $c;
}
return implode('', $src);
return new EnumStub($srcArray);
}
}

View File

@ -52,7 +52,7 @@ class StubCaster
public static function castEnum(EnumStub $c, array $a, Stub $stub, $isNested)
{
if ($isNested) {
$stub->class = '';
$stub->class = $c->dumpKeys ? '' : null;
$stub->handle = 0;
$stub->value = null;
$stub->cut = $c->cut;

View File

@ -162,7 +162,7 @@ class Data
$withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth;
$dumper->enterHash($cursor, $item->type, $item->class, $withChildren);
if ($withChildren) {
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type);
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class);
} elseif ($children && 0 <= $cut) {
$cut += count($children);
}
@ -191,10 +191,11 @@ class Data
* @param array $children The children to dump
* @param int $hashCut The number of items removed from the original hash
* @param string $hashType A Cursor::HASH_* const
* @param bool $dumpKeys Whether keys should be dumped or not
*
* @return int The final number of removed items
*/
private function dumpChildren($dumper, $parentCursor, &$refs, $children, $hashCut, $hashType)
private function dumpChildren($dumper, $parentCursor, &$refs, $children, $hashCut, $hashType, $dumpKeys)
{
$cursor = clone $parentCursor;
++$cursor->depth;
@ -204,7 +205,7 @@ class Data
$cursor->hashCut = $hashCut;
foreach ($children as $key => $child) {
$cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key);
$cursor->hashKey = $key;
$cursor->hashKey = $dumpKeys ? $key : null;
$this->dumpItem($dumper, $cursor, $refs, $child);
if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) {
$parentCursor->stop = true;

View File

@ -115,6 +115,10 @@ class CliDumper extends AbstractDumper
$attr = array();
switch ($type) {
case 'default':
$style = 'default';
break;
case 'integer':
$style = 'num';
break;

View File

@ -0,0 +1,109 @@
<?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\Caster;
use Symfony\Component\VarDumper\Caster\ExceptionCaster;
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
class ExceptionCasterTest extends \PHPUnit_Framework_TestCase
{
use VarDumperTestTrait;
private function getTestException()
{
return new \Exception('foo');
}
protected function tearDown()
{
ExceptionCaster::$srcContext = 1;
ExceptionCaster::$traceArgs = true;
}
public function testDefaultSettings()
{
$e = $this->getTestException(1);
$expectedDump = <<<'EODUMP'
Exception {
#message: "foo"
#code: 0
#file: "%sExceptionCasterTest.php"
#line: 23
-trace: {
%d. %sExceptionCasterTest.php: {
22: {
23: return new \Exception('foo');
24: }
}
%d. %sExceptionCasterTest.php: {
%d: {
%d: $e = $this->getTestException(1);
%d:
args: {
1
}
}
%A
EODUMP;
$this->assertDumpMatchesFormat($expectedDump, $e);
}
public function testNoArgs()
{
$e = $this->getTestException(1);
ExceptionCaster::$traceArgs = false;
$expectedDump = <<<'EODUMP'
Exception {
#message: "foo"
#code: 0
#file: "%sExceptionCasterTest.php"
#line: 23
-trace: {
%d. %sExceptionCasterTest.php: {
22: {
23: return new \Exception('foo');
24: }
}
%d. %sExceptionCasterTest.php: {
%d: {
%d: $e = $this->getTestException(1);
%d: ExceptionCaster::$traceArgs = false;
}
%A
EODUMP;
$this->assertDumpMatchesFormat($expectedDump, $e);
}
public function testNoSrcContext()
{
$e = $this->getTestException(1);
ExceptionCaster::$srcContext = -1;
$expectedDump = <<<'EODUMP'
Exception {
#message: "foo"
#code: 0
#file: "%sExceptionCasterTest.php"
#line: 23
-trace: {
%d. %sExceptionCasterTest.php: 23
%d. %sExceptionCasterTest.php: %d
%A
EODUMP;
$this->assertDumpMatchesFormat($expectedDump, $e);
}
}

View File

@ -156,11 +156,11 @@ Generator {
this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { }
executing: {
Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz(): {
%sGeneratorDemo.php:14: """
{\n
yield from bar();\n
}\n
"""
%sGeneratorDemo.php: {
13: {
14: yield from bar();
15: }
}
}
}
}
@ -177,43 +177,31 @@ array:2 [
0 => ReflectionGenerator {
this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { }
trace: {
3. Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo() ==> yield(): {
src: {
%sGeneratorDemo.php:9: """
{\n
yield 1;\n
}\n
"""
}
3. %sGeneratorDemo.php: {
8: {
9: yield 1;
10: }
}
2. Symfony\Component\VarDumper\Tests\Fixtures\bar() ==> Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo(): {
src: {
%sGeneratorDemo.php:20: """
{\n
yield from GeneratorDemo::foo();\n
}\n
"""
}
2. %sGeneratorDemo.php: {
19: {
20: yield from GeneratorDemo::foo();
21: }
}
1. Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() ==> Symfony\Component\VarDumper\Tests\Fixtures\bar(): {
src: {
%sGeneratorDemo.php:14: """
{\n
yield from bar();\n
}\n
"""
}
1. %sGeneratorDemo.php: {
13: {
14: yield from bar();
15: }
}
}
}
1 => Generator {
executing: {
Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo(): {
%sGeneratorDemo.php:10: """
yield 1;\n
}\n
\n
"""
%sGeneratorDemo.php: {
9: yield 1;
10: }
11:
}
}
}
}

View File

@ -264,15 +264,14 @@ EOTXT
if (method_exists($twig, 'getSource')) {
$twig = <<<EOTXT
foo.twig:2: """
foo bar\\n
twig source\\n
\\n
"""
foo.twig: {
1: foo bar
2: twig source
3:
}
EOTXT;
} else {
$twig = '';
$twig = '%A';
}
$r = defined('HHVM_VERSION') ? '' : '#%d';
@ -289,50 +288,26 @@ stream resource {@{$ref}
: Symfony\Component\VarDumper\Exception\ThrowingCasterException {{$r}
#message: "Unexpected Exception thrown from a caster: Foobar"
-trace: {
%d. __TwigTemplate_VarDumperFixture_u75a09->doDisplay() ==> new Exception(): {
src: {
%sTwig.php:19: """
// line 2\\n
throw new \Exception('Foobar');\\n
}\\n
"""
{$twig} }
%d. {$twig}
%d. %sTemplate.php: {
%d: try {
%d: \$this->doDisplay(\$context, \$blocks);
%d: } catch (Twig_Error \$e) {
}
%d. Twig_Template->displayWithErrorHandling() ==> __TwigTemplate_VarDumperFixture_u75a09->doDisplay(): {
src: {
%sTemplate.php:%d: """
try {\\n
\$this->doDisplay(\$context, \$blocks);\\n
} catch (Twig_Error \$e) {\\n
"""
}
%d. %sTemplate.php: {
%d: {
%d: \$this->displayWithErrorHandling(\$this->env->mergeGlobals(\$context), array_merge(\$this->blocks, \$blocks));
%d: }
}
%d. Twig_Template->display() ==> Twig_Template->displayWithErrorHandling(): {
src: {
%sTemplate.php:%d: """
{\\n
\$this->displayWithErrorHandling(\$this->env->mergeGlobals(\$context), array_merge(\$this->blocks, \$blocks));\\n
}\\n
"""
}
%d. %sTemplate.php: {
%d: try {
%d: \$this->display(\$context);
%d: } catch (Exception \$e) {
}
%d. Twig_Template->render() ==> Twig_Template->display(): {
src: {
%sTemplate.php:%d: """
try {\\n
\$this->display(\$context);\\n
} catch (Exception \$e) {\\n
"""
}
}
%d. %slosure%s() ==> Twig_Template->render(): {
src: {
%sCliDumperTest.php:{$line}: """
}\\n
};'),\\n
));\\n
"""
}
%d. %sCliDumperTest.php: {
%d: }
{$line}: };'),
%d: ));
}
}
}