Skip to content

Commit

Permalink
feature #19289 [VarDumper] Dumping exceptions is now more compact (ni…
Browse files Browse the repository at this point in the history
…colas-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
  • Loading branch information
nicolas-grekas committed Jul 28, 2016
2 parents 194dcf3 + 19e9cbe commit c7fa99e
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 111 deletions.
5 changes: 4 additions & 1 deletion src/Symfony/Component/VarDumper/Caster/EnumStub.php
Expand Up @@ -20,8 +20,11 @@
*/
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;
}
}
64 changes: 40 additions & 24 deletions src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php
Expand Up @@ -88,6 +88,7 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
$stub->class = '';
$stub->handle = 0;
$frames = $trace->value;
$prefix = Caster::PREFIX_VIRTUAL;

$a = array();
$j = count($frames);
Expand All @@ -98,33 +99,35 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
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);
}
Expand All @@ -145,9 +148,8 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is
$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()));

Expand All @@ -156,11 +158,14 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is
$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'];
}
Expand All @@ -177,7 +182,7 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is
}
}
if ($frame->keepArgs && isset($f['args'])) {
$a[$prefix.'args'] = $f['args'];
$a[$prefix.'args'] = new EnumStub($f['args'], false);
}

return $a;
Expand Down Expand Up @@ -232,12 +237,23 @@ private static function extractSource(array $srcArray, $line, $srcContext)
++$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);
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Component/VarDumper/Caster/StubCaster.php
Expand Up @@ -52,7 +52,7 @@ public static function cutInternals($obj, array $a, Stub $stub, $isNested)
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;
Expand Down
7 changes: 4 additions & 3 deletions src/Symfony/Component/VarDumper/Cloner/Data.php
Expand Up @@ -162,7 +162,7 @@ private function dumpItem($dumper, $cursor, &$refs, $item)
$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);
}
Expand Down Expand Up @@ -191,10 +191,11 @@ private function dumpItem($dumper, $cursor, &$refs, $item)
* @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;
Expand All @@ -204,7 +205,7 @@ private function dumpChildren($dumper, $parentCursor, &$refs, $children, $hashCu
$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;
Expand Down
4 changes: 4 additions & 0 deletions src/Symfony/Component/VarDumper/Dumper/CliDumper.php
Expand Up @@ -115,6 +115,10 @@ public function dumpScalar(Cursor $cursor, $type, $value)
$attr = array();

switch ($type) {
case 'default':
$style = 'default';
break;

case 'integer':
$style = 'num';
break;
Expand Down
109 changes: 109 additions & 0 deletions src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php
@@ -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);
}
}
Expand Up @@ -156,11 +156,11 @@ public function testGenerator()
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: }
}
}
}
}
Expand All @@ -177,43 +177,31 @@ public function testGenerator()
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:
}
}
}
}
Expand Down

0 comments on commit c7fa99e

Please sign in to comment.