[VarDumper] Fix dump output for better readability

This commit is contained in:
Nicolas Grekas 2015-06-15 14:04:42 +02:00
parent 287db73898
commit e448fadf98
5 changed files with 126 additions and 67 deletions

View File

@ -31,7 +31,6 @@ class CliDumper extends AbstractDumper
'num' => '1;38;5;38',
'const' => '1;38;5;208',
'str' => '1;38;5;113',
'cchr' => '7',
'note' => '38;5;38',
'ref' => '38;5;247',
'public' => '',
@ -42,7 +41,15 @@ class CliDumper extends AbstractDumper
'index' => '38;5;38',
);
protected static $controlCharsRx = '/[\x00-\x1F\x7F]/';
protected static $controlCharsRx = '/[\x00-\x1F\x7F]+/';
protected static $controlCharsMap = array(
"\t" => '\t',
"\n" => '\n',
"\v" => '\v',
"\f" => '\f',
"\r" => '\r',
"\033" => '\e',
);
/**
* {@inheritdoc}
@ -146,7 +153,7 @@ class CliDumper extends AbstractDumper
$this->line .= $this->style($style, $value, $attr);
$this->dumpLine($cursor->depth);
$this->dumpLine($cursor->depth, true);
}
/**
@ -161,13 +168,17 @@ class CliDumper extends AbstractDumper
}
if ('' === $str) {
$this->line .= '""';
$this->dumpLine($cursor->depth);
$this->dumpLine($cursor->depth, true);
} else {
$attr = array(
'length' => function_exists('iconv_strlen') && 0 <= $cut ? iconv_strlen($str, 'UTF-8') + $cut : 0,
'length' => 0 <= $cut && function_exists('iconv_strlen') ? iconv_strlen($str, 'UTF-8') + $cut : 0,
'binary' => $bin,
);
$str = explode("\n", $str);
if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) {
unset($str[1]);
$str[0] .= "\n";
}
$m = count($str) - 1;
$i = $lineCut = 0;
@ -183,20 +194,30 @@ class CliDumper extends AbstractDumper
}
foreach ($str as $str) {
if ($i < $m) {
$str .= "\n";
}
if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = iconv_strlen($str, 'UTF-8')) {
$str = iconv_substr($str, 0, $this->maxStringWidth, 'UTF-8');
$lineCut = $len - $this->maxStringWidth;
}
if ($m) {
if ($m && 0 < $cursor->depth) {
$this->line .= $this->indentPad;
}
$this->line .= $this->style('str', $str, $attr);
if ('' !== $str) {
$this->line .= $this->style('str', $str, $attr);
}
if ($i++ == $m) {
$this->line .= '"';
if ($m) {
$this->line .= '""';
if ('' !== $str) {
$this->dumpLine($cursor->depth);
if (0 < $cursor->depth) {
$this->line .= $this->indentPad;
}
}
$this->line .= '"""';
} else {
$this->line .= '"';
}
if ($cut < 0) {
$this->line .= '…';
@ -210,7 +231,7 @@ class CliDumper extends AbstractDumper
$lineCut = 0;
}
$this->dumpLine($cursor->depth);
$this->dumpLine($cursor->depth, $i > $m);
}
}
}
@ -228,7 +249,7 @@ class CliDumper extends AbstractDumper
if (Cursor::HASH_OBJECT === $type) {
$prefix = 'stdClass' !== $class ? $this->style('note', $class).' {' : '{';
} elseif (Cursor::HASH_RESOURCE === $type) {
$prefix = $this->style('note', ':'.$class).' {';
$prefix = $this->style('note', $class.' resource').($hasChild ? ' {' : ' ');
} else {
$prefix = $class ? $this->style('note', 'array:'.$class).' [' : '[';
}
@ -237,6 +258,8 @@ class CliDumper extends AbstractDumper
$prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), array('count' => $cursor->softRefCount));
} elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) {
$prefix .= $this->style('ref', '&'.$cursor->hardRefTo, array('count' => $cursor->hardRefCount));
} elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) {
$prefix = substr($prefix, 0, -1);
}
$this->line .= $prefix;
@ -252,8 +275,8 @@ class CliDumper extends AbstractDumper
public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut)
{
$this->dumpEllipsis($cursor, $hasChild, $cut);
$this->line .= Cursor::HASH_OBJECT === $type || Cursor::HASH_RESOURCE === $type ? '}' : ']';
$this->dumpLine($cursor->depth);
$this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : ''));
$this->dumpLine($cursor->depth, true);
}
/**
@ -360,12 +383,34 @@ class CliDumper extends AbstractDumper
}
$style = $this->styles[$style];
$cchr = $this->colors ? "\033[m\033[{$style};{$this->styles['cchr']}m%s\033[m\033[{$style}m" : '%s';
$value = preg_replace_callback(self::$controlCharsRx, function ($r) use ($cchr) {
return sprintf($cchr, "\x7F" === $r[0] ? '?' : chr(64 + ord($r[0])));
}, $value);
return $this->colors ? sprintf("\033[%sm%s\033[m\033[%sm", $style, $value, $this->styles['default']) : $value;
$map = static::$controlCharsMap;
$startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : '';
$endCchr = $this->colors ? "\033[m\033[{$style}m" : '';
$value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) {
$s = $startCchr;
$c = $c[$i = 0];
do {
$s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', ord($c[$i]));
} while (isset($c[++$i]));
return $s.$endCchr;
}, $value, -1, $cchrCount);
if ($this->colors) {
if ($cchrCount && "\033" === $value[0]) {
$value = substr($value, strlen($startCchr));
} else {
$value = "\033[{$style}m".$value;
}
if ($cchrCount && $endCchr === substr($value, -strlen($endCchr))) {
$value = substr($value, 0, -strlen($endCchr));
} else {
$value .= "\033[{$this->styles['default']}m";
}
}
return $value;
}
/**
@ -418,7 +463,7 @@ class CliDumper extends AbstractDumper
/**
* {@inheritdoc}
*/
protected function dumpLine($depth)
protected function dumpLine($depth, $endOfValue = false)
{
if ($this->colors) {
$this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line);

View File

@ -35,7 +35,6 @@ class HtmlDumper extends CliDumper
'num' => 'font-weight:bold; color:#1299DA',
'const' => 'font-weight:bold',
'str' => 'font-weight:bold; color:#56DB3A',
'cchr' => 'color:#FF8400',
'note' => 'color:#1299DA',
'ref' => 'color:#A0A0A0',
'public' => 'color:#FFFFFF',
@ -329,10 +328,6 @@ EOHTML;
}
$v = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
$v = preg_replace_callback(self::$controlCharsRx, function ($r) {
// Use Unicode Control Pictures - see http://www.unicode.org/charts/PDF/U2400.pdf
return sprintf('<span class=sf-dump-cchr title=\\x%02X>&#%d;</span>', ord($r[0]), "\x7F" !== $r[0] ? 0x2400 + ord($r[0]) : 0x2421);
}, $v);
if ('ref' === $style) {
if (empty($attr['count'])) {
@ -349,25 +344,44 @@ EOHTML;
$style .= sprintf(' title="%s"', empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property');
} elseif ('str' === $style && 1 < $attr['length']) {
$style .= sprintf(' title="%s%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : '');
} elseif ('note' === $style) {
if (false !== $c = strrpos($v, '\\')) {
return sprintf('<abbr title="%s" class=sf-dump-%s>%s</abbr>', $v, $style, substr($v, $c + 1));
} elseif (':' === $v[0]) {
return sprintf('<abbr title="`%s` resource" class=sf-dump-%s>%s</abbr>', substr($v, 1), $style, $v);
}
} elseif ('note' === $style && false !== $c = strrpos($v, '\\')) {
return sprintf('<abbr title="%s" class=sf-dump-%s>%s</abbr>', $v, $style, substr($v, $c + 1));
} elseif ('protected' === $style) {
$style .= ' title="Protected property"';
} elseif ('private' === $style) {
$style .= sprintf(' title="Private property defined in class:&#10;`%s`"', $attr['class']);
}
return "<span class=sf-dump-$style>$v</span>";
$map = static::$controlCharsMap;
$style = "<span class=sf-dump-{$style}>";
$v = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $style) {
$s = '</span>';
$c = $c[$i = 0];
do {
$s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', ord($c[$i]));
} while (isset($c[++$i]));
return $s.$style;
}, $v, -1, $cchrCount);
if ($cchrCount && '<' === $v[0]) {
$v = substr($v, 7);
} else {
$v = $style.$v;
}
if ($cchrCount && '>' === substr($v, -1)) {
$v = substr($v, 0, -strlen($style));
} else {
$v .= '</span>';
}
return $v;
}
/**
* {@inheritdoc}
*/
protected function dumpLine($depth)
protected function dumpLine($depth, $endOfValue = false)
{
if (-1 === $this->lastDepth) {
$this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line;

View File

@ -55,10 +55,10 @@ array:25 [
4 => INF
5 => -INF
6 => {$intMax}
"str" => "déjà"
7 => b"é@"
"str" => "déjà\\n"
7 => b"é\\x00"
"[]" => []
"res" => :stream {@{$res1}
"res" => stream resource {@{$res1}
wrapper_type: "plainfile"
stream_type: "STDIO"
mode: "r"
@ -69,21 +69,21 @@ array:25 [
eof: false
options: []
}
8 => :Unknown {@{$res2}}
8 => Unknown resource @{$res2}
"obj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d
+foo: "foo"
+"bar": "bar"
}
"closure" => Closure {#%d
reflection: """
Closure [ <user%S> %s Symfony\Component\VarDumper\Tests\Fixture\{closure} ] {
@@ {$var['file']} {$var['line']} - {$var['line']}
- Parameters [2] {
Parameter #0 [ <required> \$a ]
Parameter #1 [ <optional> PDO or NULL &\$b = NULL ]
}
}
Closure [ <user%S> %s Symfony\Component\VarDumper\Tests\Fixture\{closure} ] {\\n
@@ {$var['file']} {$var['line']} - {$var['line']}\\n
\\n
- Parameters [2] {\\n
Parameter #0 [ <required> \$a ]\\n
Parameter #1 [ <optional> PDO or NULL &\$b = NULL ]\\n
}\\n
}\\n
"""
}
"line" => {$var['line']}
@ -130,7 +130,7 @@ EOTXT
$this->assertStringMatchesFormat(
<<<EOTXT
:stream {@{$ref}
stream resource {@{$ref}
wrapper_type: "PHP"
stream_type: "MEMORY"
mode: "w+b"
@ -161,7 +161,7 @@ EOTXT
public function testRefsInProperties()
{
$var = (object) array('foo' => 'foo');
$var->bar =& $var->foo;
$var->bar = &$var->foo;
$dumper = new CliDumper();
$dumper->setColors(false);
@ -325,7 +325,7 @@ EOTXT
$var = function &() {
$var = array();
$var[] =& $var;
$var[] = &$var;
return $var;
};

View File

@ -19,7 +19,7 @@ fclose($h);
$var = array(
'number' => 1, null,
'const' => 1.1, true, false, NAN, INF, -INF, PHP_INT_MAX,
'str' => "déjà", "\xE9\x00",
'str' => "déjà\n", "\xE9\x00",
'[]' => array(),
'res' => $g,
$h,
@ -30,14 +30,14 @@ $var = array(
);
$r = array();
$r[] =& $r;
$r[] = &$r;
$var['recurs'] =& $r;
$var[] =& $var[0];
$var['recurs'] = &$r;
$var[] = &$var[0];
$var['sobj'] = $var['obj'];
$var['snobj'] =& $var['nobj'][0];
$var['snobj'] = &$var['nobj'][0];
$var['snobj2'] = $var['nobj'][0];
$var['file'] = __FILE__;
$var["bin-key-\xE9"] = "";
$var["bin-key-\xE9"] = '';
unset($g, $h, $r);

View File

@ -59,10 +59,10 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
<span class=sf-dump-key>4</span> => <span class=sf-dump-num>INF</span>
<span class=sf-dump-key>5</span> => <span class=sf-dump-num>-INF</span>
<span class=sf-dump-key>6</span> => <span class=sf-dump-num>{$intMax}</span>
"<span class=sf-dump-key>str</span>" => "<span class=sf-dump-str title="4 characters">d&#233;j&#224;</span>"
<span class=sf-dump-key>7</span> => b"<span class=sf-dump-str title="2 binary or non-UTF-8 characters">&#233;<span class=sf-dump-cchr title=\\x00>&#9216;</span></span>"
"<span class=sf-dump-key>str</span>" => "<span class=sf-dump-str title="5 characters">d&#233;j&#224;</span>\\n"
<span class=sf-dump-key>7</span> => b"<span class=sf-dump-str title="2 binary or non-UTF-8 characters">&#233;</span>\\x00"
"<span class=sf-dump-key>[]</span>" => []
"<span class=sf-dump-key>res</span>" => <abbr title="`stream` resource" class=sf-dump-note>:stream</abbr> {<a class=sf-dump-ref>@{$res1}</a><samp>
"<span class=sf-dump-key>res</span>" => <span class=sf-dump-note>stream resource</span> <a class=sf-dump-ref>@{$res1}</a><samp>
<span class=sf-dump-meta>wrapper_type</span>: "<span class=sf-dump-str title="9 characters">plainfile</span>"
<span class=sf-dump-meta>stream_type</span>: "<span class=sf-dump-str title="5 characters">STDIO</span>"
<span class=sf-dump-meta>mode</span>: "<span class=sf-dump-str>r</span>"
@ -73,21 +73,21 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
<span class=sf-dump-meta>eof</span>: <span class=sf-dump-const>false</span>
<span class=sf-dump-meta>options</span>: []
</samp>}
<span class=sf-dump-key>8</span> => <abbr title="`Unknown` resource" class=sf-dump-note>:Unknown</abbr> {<a class=sf-dump-ref>@{$res2}</a>}
<span class=sf-dump-key>8</span> => <span class=sf-dump-note>Unknown resource</span> <a class=sf-dump-ref>@{$res2}</a>
"<span class=sf-dump-key>obj</span>" => <abbr title="Symfony\Component\VarDumper\Tests\Fixture\DumbFoo" class=sf-dump-note>DumbFoo</abbr> {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="2 occurrences">#%d</a><samp id={$dumpId}-ref2%d>
+<span class=sf-dump-public title="Public property">foo</span>: "<span class=sf-dump-str title="3 characters">foo</span>"
+"<span class=sf-dump-public title="Runtime added dynamic property">bar</span>": "<span class=sf-dump-str title="3 characters">bar</span>"
</samp>}
"<span class=sf-dump-key>closure</span>" => <span class=sf-dump-note>Closure</span> {<a class=sf-dump-ref>#%d</a><samp>
<span class=sf-dump-meta>reflection</span>: """
<span class=sf-dump-str title="%d characters">Closure [ &lt;user%S&gt; %s Symfony\Component\VarDumper\Tests\Fixture\{closure} ] {</span>
<span class=sf-dump-str title="%d characters"> @@ {$var['file']} {$var['line']} - {$var['line']}</span>
<span class=sf-dump-str title="%d characters"> - Parameters [2] {</span>
<span class=sf-dump-str title="%d characters"> Parameter #0 [ &lt;required&gt; \$a ]</span>
<span class=sf-dump-str title="%d characters"> Parameter #1 [ &lt;optional&gt; PDO or NULL &amp;\$b = NULL ]</span>
<span class=sf-dump-str title="%d characters"> }</span>
<span class=sf-dump-str title="%d characters">}</span>
<span class=sf-dump-str title="%d characters">Closure [ &lt;user%S&gt; %s Symfony\Component\VarDumper\Tests\Fixture\{closure} ] {</span>\\n
<span class=sf-dump-str title="%d characters"> @@ {$var['file']} {$var['line']} - {$var['line']}</span>\\n
\\n
<span class=sf-dump-str title="%d characters"> - Parameters [2] {</span>\\n
<span class=sf-dump-str title="%d characters"> Parameter #0 [ &lt;required&gt; \$a ]</span>\\n
<span class=sf-dump-str title="%d characters"> Parameter #1 [ &lt;optional&gt; PDO or NULL &amp;\$b = NULL ]</span>\\n
<span class=sf-dump-str title="%d characters"> }</span>\\n
<span class=sf-dump-str title="%d characters">}</span>\\n
"""
</samp>}
"<span class=sf-dump-key>line</span>" => <span class=sf-dump-num>{$var['line']}</span>