Skip to content

[VarDumper] Handle attributes in Data clones for more semantic dumps #19797

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,11 @@ table.logs .sf-call-stack abbr {
#collector-content .sf-dump .trace li.selected {
background: rgba(255, 255, 153, 0.5);
}
#collector-content .sf-dump-expanded code { color: #222; }
#collector-content .sf-dump-expanded code .sf-dump-const {
background: rgba(255, 255, 153, 0.5);
font-weight: normal;
}

{# Search Results page
========================================================================= #}
Expand Down
50 changes: 32 additions & 18 deletions src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
$stub->handle = 0;
$frames = $trace->value;
$prefix = Caster::PREFIX_VIRTUAL;
$format = "\0~Stack level %s.\0%s";

$a = array();
$j = count($frames);
Expand All @@ -99,14 +98,14 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
if (!isset($trace->value[$i])) {
return array();
}
$lastCall = isset($frames[$i]['function']) ? ' ==> '.(isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '';
$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) {
$f = $frames[$i];
$call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'].'()' : '???';

$label = $call.$lastCall;
$label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall;
$frame = new FrameStub(
array(
'object' => isset($f['object']) ? $f['object'] : null,
Expand All @@ -120,14 +119,15 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is
$f = self::castFrameStub($frame, array(), $frame, true);
if (isset($f[$prefix.'src'])) {
foreach ($f[$prefix.'src']->value as $label => $frame) {
$label = substr_replace($label, "title=Stack level $j.&", 2, 0);
}
if (isset($f[$prefix.'args']) && $frame instanceof EnumStub) {
$frame->value['args'] = $f[$prefix.'args'];
}
}
$a[sprintf($format, $j, $label)] = $frame;
$a[$label] = $frame;

$lastCall = ' ==> '.$call;
$lastCall = $call;
}
if (null !== $trace->sliceLength) {
$a = array_slice($a, 0, $trace->sliceLength, true);
Expand All @@ -149,28 +149,38 @@ 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();
$caller = isset($f['function']) ? sprintf('in %s() on line %d', (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'], $f['line']) : null;
$src = $f['line'];
$srcKey = $f['file'];
$ellipsis = explode(DIRECTORY_SEPARATOR, $srcKey);
$ellipsis = 3 < count($ellipsis) ? 2 + strlen(implode(array_slice($ellipsis, -2))) : 0;

if (file_exists($f['file']) && 0 <= 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()));

try {
$ellipsis = 0;
$templateName = $template->getTemplateName();
$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 = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, $caller, 'twig');
$srcKey = $templateName.':'.$templateInfo[$f['line']];
}
} catch (\Twig_Error_Loader $e) {
}
}
if (!$src) {
$src[$f['file'].':'.$f['line']] = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], self::$srcContext);
if ($srcKey == $f['file']) {
$src = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], self::$srcContext, $caller, 'php', $f['file']);
$srcKey .= ':'.$f['line'];
if ($ellipsis) {
$ellipsis += 1 + strlen($f['line']);
}
}
} else {
$src[$f['file']] = $f['line'];
}
$a[$prefix.'src'] = new EnumStub($src);
$srcAttr = $ellipsis ? 'ellipsis='.$ellipsis : '';
$a[$prefix.'src'] = new EnumStub(array("\0~$srcAttr\0$srcKey" => $src));
}

unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']);
Expand Down Expand Up @@ -214,7 +224,7 @@ private static function filterExceptionArray($xClass, array $a, $xPrefix, $filte
return $a;
}

private static function extractSource(array $srcArray, $line, $srcContext)
private static function extractSource(array $srcArray, $line, $srcContext, $title, $lang, $file = null)
{
$src = array();

Expand All @@ -239,20 +249,24 @@ private static function extractSource(array $srcArray, $line, $srcContext)
} while (0 > $i && null !== $pad);

--$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';
$c = new ConstStub('default', $c);
} else {
$c = new ConstStub($c, $title);
if (null !== $file) {
$c->attr['file'] = $file;
$c->attr['line'] = $line;
}
}
$srcArray[sprintf("% {$pad}d", $i + $line - $srcContext)] = $c;
$c->attr['lang'] = $lang;
$srcArray[sprintf("\0~%d\0", $i + $line - $srcContext)] = $c;
}

return new EnumStub($srcArray);
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/VarDumper/Caster/StubCaster.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public static function castStub(Stub $c, array $a, Stub $stub, $isNested)
$stub->value = $c->value;
$stub->handle = $c->handle;
$stub->cut = $c->cut;
$stub->attr = $c->attr;

return array();
}
Expand Down Expand Up @@ -56,6 +57,7 @@ public static function castEnum(EnumStub $c, array $a, Stub $stub, $isNested)
$stub->handle = 0;
$stub->value = null;
$stub->cut = $c->cut;
$stub->attr = $c->attr;

$a = array();

Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/VarDumper/Cloner/Cursor.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ class Cursor
public $hashLength = 0;
public $hashCut = 0;
public $stop = false;
public $attr = array();
}
3 changes: 3 additions & 0 deletions src/Symfony/Component/VarDumper/Cloner/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ private function dumpItem($dumper, $cursor, &$refs, $item)
$firstSeen = true;

if (!$item instanceof Stub) {
$cursor->attr = array();
$type = gettype($item);
} elseif (Stub::TYPE_REF === $item->type) {
if ($item->handle) {
Expand All @@ -167,6 +168,7 @@ private function dumpItem($dumper, $cursor, &$refs, $item)
$cursor->hardRefHandle = $this->useRefHandles & $item->handle;
$cursor->hardRefCount = $item->refCount;
}
$cursor->attr = $item->attr;
$type = $item->class ?: gettype($item->value);
$item = $item->value;
}
Expand All @@ -181,6 +183,7 @@ private function dumpItem($dumper, $cursor, &$refs, $item)
}
$cursor->softRefHandle = $this->useRefHandles & $item->handle;
$cursor->softRefCount = $item->refCount;
$cursor->attr = $item->attr;
$cut = $item->cut;

if ($item->position && $firstSeen) {
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/VarDumper/Cloner/Stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ class Stub
public $handle = 0;
public $refCount = 0;
public $position = 0;
public $attr = array();
}
10 changes: 6 additions & 4 deletions src/Symfony/Component/VarDumper/Dumper/CliDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function dumpScalar(Cursor $cursor, $type, $value)
$this->dumpKey($cursor);

$style = 'const';
$attr = array();
$attr = $cursor->attr;

switch ($type) {
case 'default':
Expand Down Expand Up @@ -148,7 +148,7 @@ public function dumpScalar(Cursor $cursor, $type, $value)
break;

default:
$attr['value'] = isset($value[0]) && !preg_match('//u', $value) ? $this->utf8Encode($value) : $value;
$attr += array('value' => isset($value[0]) && !preg_match('//u', $value) ? $this->utf8Encode($value) : $value);
$value = isset($type[0]) && !preg_match('//u', $type) ? $this->utf8Encode($type) : $type;
break;
}
Expand All @@ -164,6 +164,7 @@ public function dumpScalar(Cursor $cursor, $type, $value)
public function dumpString(Cursor $cursor, $str, $bin, $cut)
{
$this->dumpKey($cursor);
$attr = $cursor->attr;

if ($bin) {
$str = $this->utf8Encode($str);
Expand All @@ -172,7 +173,7 @@ public function dumpString(Cursor $cursor, $str, $bin, $cut)
$this->line .= '""';
$this->dumpLine($cursor->depth, true);
} else {
$attr = array(
$attr += array(
'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0,
'binary' => $bin,
);
Expand Down Expand Up @@ -350,7 +351,8 @@ protected function dumpKey(Cursor $cursor)
case '~':
$style = 'meta';
if (isset($key[0][1])) {
$attr['title'] = substr($key[0], 1);
parse_str(substr($key[0], 1), $attr);
$attr += array('binary' => $cursor->hashKeyIsBinary);
}
break;
case '*':
Expand Down
63 changes: 50 additions & 13 deletions src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ class HtmlDumper extends CliDumper
'meta' => 'color:#B729D9',
'key' => 'color:#56DB3A',
'index' => 'color:#1299DA',
'expanded code.hljs' => 'display:inline; padding:0; background:none',
);

private $displayOptions = array(
'maxDepth' => 1,
'maxStringLength' => 160,
'fileLinkFormat' => null,
);
private $extraDisplayOptions = array();

Expand Down Expand Up @@ -184,6 +186,19 @@ function toggle(a, recursive) {
return function (root, x) {
root = doc.getElementById(root);

var indentRx = new RegExp('^('+(root.getAttribute('data-indent-pad') || ' ').replace(rxEsc, '\\$1')+')+', 'm'),
options = {$options},
elt = root.getElementsByTagName('A'),
len = elt.length,
i = 0, s, h,
t = [];

while (i < len) t.push(elt[i++]);

for (i in x) {
options[i] = x[i];
}

function a(e, f) {
addEventListener(root, e, function (e) {
if ('A' == e.target.tagName) {
Expand All @@ -201,6 +216,20 @@ function isCtrlKey(e) {
refStyle.innerHTML = '';
}
});
if (options.fileLinkFormat) {
addEventListener(root, 'click', function (e) {
e = e.target;
while (root != e && 'CODE' != e.tagName) {
e = e.parentNode;
}
if ('CODE' == e.tagName) {
var f = e.getAttribute('data-file'), l = e.getAttribute('data-line');
if (f && l) {
location.href = options.fileLinkFormat.replace('%f', f).replace('%l', l);
}
}
});
}
a('mouseover', function (a) {
if (a = idRx.exec(a.className)) {
try {
Expand Down Expand Up @@ -246,19 +275,6 @@ function isCtrlKey(e) {
}
});

var indentRx = new RegExp('^('+(root.getAttribute('data-indent-pad') || ' ').replace(rxEsc, '\\$1')+')+', 'm'),
options = {$options},
elt = root.getElementsByTagName('A'),
len = elt.length,
i = 0, s, h,
t = [];

while (i < len) t.push(elt[i++]);

for (i in x) {
options[i] = x[i];
}

elt = root.getElementsByTagName('SAMP');
len = elt.length;
i = 0;
Expand Down Expand Up @@ -369,6 +385,15 @@ function isCtrlKey(e) {
border: 0;
outline: none;
}
pre.sf-dump .sf-dump-ellipsis {
display: inline-block;
overflow: visible;
text-overflow: ellipsis;
width: 50px;
white-space: nowrap;
overflow: hidden;
vertical-align: top;
}
.sf-dump-str-collapse .sf-dump-str-collapse {
display: none;
}
Expand Down Expand Up @@ -452,6 +477,11 @@ protected function style($style, $value, $attr = array())
} elseif ('private' === $style) {
$style .= sprintf(' title="Private property defined in class:&#10;`%s`"', esc($attr['class']));
}
if (isset($attr['ellipsis'])) {
$label = esc(substr($value, -$attr['ellipsis']));

return sprintf('<span class=sf-dump-%s><abbr title="%s" class=sf-dump-ellipsis>%2$s</abbr>%s</span>', $style, substr($v, 0, -strlen($label)), $label);
}

$map = static::$controlCharsMap;
$style = "<span class=sf-dump-{$style}>";
Expand All @@ -475,6 +505,13 @@ protected function style($style, $value, $attr = array())
} else {
$v .= '</span>';
}
if (isset($attr['lang'])) {
if (isset($attr['file'], $attr['line'])) {
$v = sprintf('<code class="%s" data-file="%s" data-line="%d">%s</code>', esc($attr['lang']), esc($attr['file']), $attr['line'], $v);
} else {
$v = sprintf('<code class="%s">%s</code>', esc($attr['lang']), $v);
}
}

return $v;
}
Expand Down
Loading