From 2b5d642efda3d89172c12ff1846ab80c151bf5da Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 1 Sep 2016 13:15:09 +0200 Subject: [PATCH] [VarDumper] Add LinkStub to create links in HTML dumps --- .../views/Profiler/profiler.css.twig | 5 -- .../Component/VarDumper/Caster/DOMCaster.php | 6 +- .../VarDumper/Caster/ExceptionCaster.php | 2 + .../Component/VarDumper/Caster/LinkStub.php | 49 +++++++++++++++ .../VarDumper/Caster/ReflectionCaster.php | 4 +- .../VarDumper/Caster/ResourceCaster.php | 5 +- .../Component/VarDumper/Caster/SplCaster.php | 4 ++ .../VarDumper/Caster/XmlReaderCaster.php | 2 +- .../Component/VarDumper/Dumper/HtmlDumper.php | 59 +++++++++++-------- .../Tests/Caster/ExceptionCasterTest.php | 2 +- .../VarDumper/Tests/HtmlDumperTest.php | 2 +- 11 files changed, 100 insertions(+), 40 deletions(-) create mode 100644 src/Symfony/Component/VarDumper/Caster/LinkStub.php diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index 697be27ada9e7..62250ba8f320f 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -912,11 +912,6 @@ 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 ========================================================================= #} diff --git a/src/Symfony/Component/VarDumper/Caster/DOMCaster.php b/src/Symfony/Component/VarDumper/Caster/DOMCaster.php index e04cf9534b38c..3a99865370ca2 100644 --- a/src/Symfony/Component/VarDumper/Caster/DOMCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/DOMCaster.php @@ -107,7 +107,7 @@ public static function castNode(\DOMNode $dom, array $a, Stub $stub, $isNested) 'namespaceURI' => $dom->namespaceURI, 'prefix' => $dom->prefix, 'localName' => $dom->localName, - 'baseURI' => $dom->baseURI, + 'baseURI' => $dom->baseURI ? new LinkStub($dom->baseURI) : $dom->baseURI, 'textContent' => new CutStub($dom->textContent), ); @@ -144,7 +144,7 @@ public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, $is 'version' => $dom->version, 'xmlVersion' => $dom->xmlVersion, 'strictErrorChecking' => $dom->strictErrorChecking, - 'documentURI' => $dom->documentURI, + 'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI, 'config' => $dom->config, 'formatOutput' => $dom->formatOutput, 'validateOnParse' => $dom->validateOnParse, @@ -237,7 +237,7 @@ public static function castLocator(\DOMLocator $dom, array $a, Stub $stub, $isNe 'columnNumber' => $dom->columnNumber, 'offset' => $dom->offset, 'relatedNode' => $dom->relatedNode, - 'uri' => $dom->uri, + 'uri' => $dom->uri ? new LinkStub($dom->uri, $dom->lineNumber) : $dom->uri, ); return $a; diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index 4765fbf6db907..484bc60639918 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -221,6 +221,8 @@ private static function filterExceptionArray($xClass, array $a, $xPrefix, $filte } unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message'], $a[Caster::PREFIX_DYNAMIC.'__destructorException']); + $a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); + return $a; } diff --git a/src/Symfony/Component/VarDumper/Caster/LinkStub.php b/src/Symfony/Component/VarDumper/Caster/LinkStub.php new file mode 100644 index 0000000000000..1b1ea1531b6a0 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Caster/LinkStub.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +/** + * Represents a file or a URL. + * + * @author Nicolas Grekas + */ +class LinkStub extends ConstStub +{ + public function __construct($file, $line = 0) + { + $this->value = $file; + + if (is_string($file)) { + $this->type = self::TYPE_STRING; + $this->class = preg_match('//u', $file) ? self::STRING_UTF8 : self::STRING_BINARY; + + if (0 === strpos($file, 'file://')) { + $file = substr($file, 7); + } elseif (false !== strpos($file, '://')) { + $this->attr['href'] = $file; + + return; + } + if (file_exists($file)) { + if ($line) { + $this->attr['line'] = $line; + } + $this->attr['file'] = realpath($file); + + if ($this->attr['file'] === $file) { + $ellipsis = explode(DIRECTORY_SEPARATOR, $file); + $this->attr['ellipsis'] = 3 < count($ellipsis) ? 2 + strlen(implode(array_slice($ellipsis, -2))) : 0; + } + } + } + } +} diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index 76c069dd06be6..fbb633e26010b 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -53,7 +53,7 @@ public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested) } if ($f = $c->getFileName()) { - $a[$prefix.'file'] = $f; + $a[$prefix.'file'] = new LinkStub($f, $c->getStartLine()); $a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine(); } @@ -287,7 +287,7 @@ private static function addExtra(&$a, \Reflector $c) $x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : array(); if (method_exists($c, 'getFileName') && $m = $c->getFileName()) { - $x['file'] = $m; + $x['file'] = new LinkStub($m, $c->getStartLine()); $x['line'] = $c->getStartLine().' to '.$c->getEndLine(); } diff --git a/src/Symfony/Component/VarDumper/Caster/ResourceCaster.php b/src/Symfony/Component/VarDumper/Caster/ResourceCaster.php index 903641f69c636..35d05992e152a 100644 --- a/src/Symfony/Component/VarDumper/Caster/ResourceCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ResourceCaster.php @@ -40,7 +40,10 @@ public static function castProcess($process, array $a, Stub $stub, $isNested) public static function castStream($stream, array $a, Stub $stub, $isNested) { - return stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested); + $a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested); + $a['uri'] = new LinkStub($a['uri']); + + return $a; } public static function castStreamContext($stream, array $a, Stub $stub, $isNested) diff --git a/src/Symfony/Component/VarDumper/Caster/SplCaster.php b/src/Symfony/Component/VarDumper/Caster/SplCaster.php index 97f2146382148..f65dc88e73e32 100644 --- a/src/Symfony/Component/VarDumper/Caster/SplCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/SplCaster.php @@ -115,6 +115,10 @@ public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, $isNe } } + if (isset($a[$prefix.'realPath'])) { + $a[$prefix.'realPath'] = new LinkStub($a[$prefix.'realPath']); + } + if (isset($a[$prefix.'perms'])) { $a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']); } diff --git a/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php b/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php index df23cf2432b41..c371009a17a28 100644 --- a/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/XmlReaderCaster.php @@ -54,7 +54,7 @@ public static function castXmlReader(\XmlReader $reader, array $a, Stub $stub, $ 'attributeCount' => $reader->attributeCount, 'value' => $reader->value, 'namespaceURI' => $reader->namespaceURI, - 'baseURI' => $reader->baseURI, + 'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI, $props => array( 'LOADDTD' => $reader->getParserProperty(\XmlReader::LOADDTD), 'DEFAULTATTRS' => $reader->getParserProperty(\XmlReader::DEFAULTATTRS), diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index c43b208986e29..ee4056ba638d0 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -43,7 +43,6 @@ 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( @@ -190,7 +189,7 @@ function toggle(a, recursive) { options = {$options}, elt = root.getElementsByTagName('A'), len = elt.length, - i = 0, s, h, + i = 0, s, h, fmt, t = []; while (i < len) t.push(elt[i++]); @@ -198,6 +197,10 @@ function toggle(a, recursive) { for (i in x) { options[i] = x[i]; } + fmt = options.fileLinkFormat; + if (fmt && 'string' == typeof fmt) { + fmt = [fmt]; + } function a(e, f) { addEventListener(root, e, function (e) { @@ -218,20 +221,6 @@ 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 { @@ -332,6 +321,16 @@ function isCtrlKey(e) { } } } + } else if (fmt && (a = elt.getAttribute('data-file'))) { + if (fmt[1]) { + for (x in fmt[1]) { + if (0 === a.indexOf(x)) { + a = fmt[1][x] + a.substr(x.length); + break; + } + } + } + elt.href = fmt[0].replace('%l', elt.getAttribute('data-line')).replace('%f', a); } } @@ -387,6 +386,7 @@ function isCtrlKey(e) { cursor: pointer; border: 0; outline: none; + color: inherit; } pre.sf-dump .sf-dump-ellipsis { display: inline-block; @@ -397,6 +397,11 @@ function isCtrlKey(e) { overflow: hidden; vertical-align: top; } +pre.sf-dump code { + display:inline; + padding:0; + background:none; +} .sf-dump-str-collapse .sf-dump-str-collapse { display: none; } @@ -480,14 +485,15 @@ protected function style($style, $value, $attr = array()) } elseif ('private' === $style) { $style .= sprintf(' title="Private property defined in class: `%s`"', esc($attr['class'])); } + $map = static::$controlCharsMap; + $style = ""; + if (isset($attr['ellipsis'])) { $label = esc(substr($value, -$attr['ellipsis'])); - return sprintf('%2$s%s', $style, substr($v, 0, -strlen($label)), $label); + $v = sprintf('%s%2$s%s%1$s', $style, substr($v, 0, -strlen($label)), $label); } - $map = static::$controlCharsMap; - $style = ""; $v = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $style) { $s = ''; $c = $c[$i = 0]; @@ -498,22 +504,23 @@ protected function style($style, $value, $attr = array()) return $s.$style; }, $v, -1, $cchrCount); - if ($cchrCount && '<' === $v[0]) { + if ('<' === $v[0]) { $v = substr($v, 7); } else { $v = $style.$v; } - if ($cchrCount && '>' === substr($v, -1)) { + if ('>' === substr($v, -1)) { $v = substr($v, 0, -strlen($style)); } else { $v .= ''; } + if (isset($attr['file'])) { + $v = sprintf('%s', esc($attr['file']), isset($attr['line']) ? $attr['line'] : 1, $v); + } elseif (isset($attr['href'])) { + $v = sprintf('%s', esc($attr['href']), $v); + } if (isset($attr['lang'])) { - if (isset($attr['file'], $attr['line'])) { - $v = sprintf('%s', esc($attr['lang']), esc($attr['file']), $attr['line'], $v); - } else { - $v = sprintf('%s', esc($attr['lang']), $v); - } + $v = sprintf('%s', esc($attr['lang']), $v); } return $v; diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php index 72f2fa8223a6e..8633b48e29d5c 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php @@ -153,7 +153,7 @@ public function testHtmlDump() Exception { #message: "foo" #code: 0 - #file: "%sExceptionCasterTest.php" + #file: "%sTests%eCaster%eExceptionCasterTest.php" #line: 25 -trace: { %sVarDumper%eTests%eCaster%eExceptionCasterTest.php: 25 diff --git a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php index c4dc4135c65fe..dd9092909a547 100644 --- a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php @@ -84,7 +84,7 @@ public function testGet() default: null } } - file: "{$var['file']}" + file: "%sTests%eFixtures%edumb-var.php" line: "{$var['line']} to {$var['line']}" } "line" => {$var['line']}