From b799844c68505611a2bde9625ee41aedb839b923 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 4 Oct 2014 09:40:01 +0200 Subject: [PATCH] [VarDumper] Dynamic HTML dumper --- .../Resources/views/Profiler/toolbar.css.twig | 2 +- .../DataCollector/DumpDataCollectorTest.php | 4 +- .../Component/VarDumper/Dumper/CliDumper.php | 2 +- .../Component/VarDumper/Dumper/HtmlDumper.php | 117 +++++++++++++----- .../VarDumper/Tests/CliDumperTest.php | 4 +- .../VarDumper/Tests/HtmlDumperTest.php | 50 ++++---- 6 files changed, 116 insertions(+), 63 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index 7290750064e20..e040fff53e6f8 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -286,7 +286,7 @@ max-height: 480px; word-wrap: break-word; overflow: hidden; - overflow-y: scroll; + overflow-y: auto; } table.sf-toolbar-ajax-requests { diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 081e3cf151191..5cfdba27d9e0d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -35,12 +35,10 @@ public function testDump() $dump = $collector->getDumps('html'); $this->assertTrue(isset($dump[0]['data'])); $dump[0]['data'] = preg_replace('/^.*?
 "
123\n
\n", + 'data' => "
123\n
\n", 'name' => 'DumpDataCollectorTest.php', 'file' => __FILE__, 'line' => $line, diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 87680de5984c2..483faad1b4ad4 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -224,7 +224,7 @@ public function leaveObject(Cursor $cursor, $class, $hasChild, $cut) */ public function enterResource(Cursor $cursor, $res, $hasChild) { - $this->enterHash($cursor, 'resource:'.$this->style('note', $res).' {', $hasChild); + $this->enterHash($cursor, $this->style('note', ':'.$res).' {', $hasChild); } /** diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index 81b798d01dfef..b8bb8192f9b17 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -24,7 +24,7 @@ class HtmlDumper extends CliDumper public static $defaultOutputStream = 'php://output'; protected $dumpHeader; - protected $dumpPrefix = '
';
+    protected $dumpPrefix = '
';
     protected $dumpSuffix = '
'; protected $dumpId = 'sf-dump'; protected $colors = true; @@ -101,7 +101,7 @@ protected function getDumpHeader() $this->headerIsDumped = true; if (null !== $this->dumpHeader) { - return str_replace('%id%', $this->dumpId, $this->dumpHeader); + return $this->dumpHeader; } $line = <<<'EOHTML' @@ -110,19 +110,81 @@ protected function getDumpHeader() Sfjs.dump = Sfjs.dump || {}; Sfjs.dump.childElts = Sfjs.dump.childElts || document.getElementsByName('sf-dump-child'); Sfjs.dump.childLen = Sfjs.dump.childLen || 0; +Sfjs.dump.refElts = Sfjs.dump.refElts || document.getElementsByName('sf-dump-ref'); +Sfjs.dump.refLen = Sfjs.dump.refLen || 0; +if (!Sfjs.dump.refStyle) { + Sfjs.dump.refStyle = document.createElement('style'); + document.documentElement.firstChild.appendChild(Sfjs.dump.refStyle); +} Sfjs.dump.instrument = Sfjs.dump.instrument || function () { - var elt, - i = this.childLen, - aCompact = '▶', - aExpanded = '▼'; + var elt, i, ref; + i = this.childLen; this.childLen= this.childElts.length; while (i < this.childLen) { elt = this.childElts[i]; if ("" == elt.className) { elt.className = "sf-dump-child"; - elt.innerHTML = ''+('sf-dump-0' == elt.parentNode.className ? aExpanded : aCompact)+elt.innerHTML+''; + elt.innerHTML = ''+elt.innerHTML+''; + if ('sf-dump-expanded' == elt.parentNode.className) { + if (elt.children[1].firstChild.nextSibling.id) { + elt.firstChild.appendChild(elt.children[1].firstChild); + elt.firstChild.appendChild(elt.children[1].firstChild); + } + Sfjs.dump.toggle(elt.firstChild); + } + } + ++i; + } + + i = this.refLen; + this.refLen= this.refElts.length; + + function instrumentRef(elt) { + var ref = elt.id; + + if (elt.href) { + ref = elt.getAttribute('href').substr(1); + if ('@' == elt.innerHTML.charAt(0)) { + elt.onclick = function() { + var r = document.getElementById(ref).parentNode.parentNode, + f = r && r.parentNode, + t = elt.parentNode, + c = elt.cloneNode(true); + if (r && r.className == "sf-dump-child") { + f.insertBefore(c, r); + try { + t.replaceChild(r, elt); + f.replaceChild(elt, c); + Sfjs.dump.refStyle.innerHTML = ''; + r = r.firstChild; + c = r.nextSibling; + if ('sf-dump-compact' == c.className) { + Sfjs.dump.toggle(r); + } + + return false; + } catch (e) { + f.removeChild(c); + } + } + }; + } + } + elt.className += ' '+ref; + elt.onmouseover = function() { + Sfjs.dump.refStyle.innerHTML = 'pre.sf-dump .'+ref+'{background-color: yellow; border-radius: 2px}'; + }; + elt.onmouseout = function() { + Sfjs.dump.refStyle.innerHTML = ''; + }; + } + + while (i < this.refLen) { + elt = this.refElts[i]; + if ("sf-dump-ref" == elt.className) { + instrumentRef(elt); } ++i; } @@ -131,16 +193,16 @@ protected function getDumpHeader() var s = a.nextElementSibling; if ('sf-dump-compact' == s.className) { - a.innerHTML = '▼'; + a.firstChild.innerHTML = '▼'; s.className = 'sf-dump-expanded'; } else { - a.innerHTML = '▶'; + a.firstChild.innerHTML = '▶'; s.className = 'sf-dump-compact'; } }; '.$this->dumpHeader; - - return str_replace('%id%', $this->dumpId, $this->dumpHeader); + return $this->dumpHeader = preg_replace('/\s+/', ' ', $line).''.$this->dumpHeader; } /** @@ -214,9 +272,9 @@ protected function style($style, $val) if ('ref' === $style) { $ref = substr($val, 1); if ('#' === $val[0]) { - return "dumpId}-ref$ref\">$val"; + return "dumpId}-ref$ref\">$val"; } else { - return "dumpId}-ref$ref\">$val"; + return "dumpId}-ref$ref\">$val"; } } @@ -231,7 +289,9 @@ protected function style($style, $val) } } elseif ('note' === $style) { if (false !== $c = strrpos($val, '\\')) { - $val = sprintf('%s', $val, $style, substr($val, $c+1)); + return sprintf('%s', $val, $style, substr($val, $c+1)); + } elseif (':' === $val[0]) { + return sprintf('%s', substr($val, 1), $style, $val); } } @@ -243,20 +303,15 @@ protected function style($style, $val) */ protected function dumpLine($depth) { - switch ($this->lastDepth - $depth) { - case +1: $this->line = ''.$this->line; break; - case -1: $this->line = "$this->line"; break; - } - if (-1 === $this->lastDepth) { - $this->line = str_replace('%id%', $this->dumpId, $this->dumpPrefix).$this->line; + $this->line = $this->dumpPrefix.$this->line; } if (!$this->headerIsDumped) { $this->line = $this->getDumpHeader().$this->line; } if (-1 === $depth) { - $this->line .= str_replace('%id%', $this->dumpId, $this->dumpSuffix); + $this->line .= $this->dumpSuffix; parent::dumpLine(0); } $this->lastDepth = $depth; diff --git a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php index 17d4ef84eb680..ac357d546942e 100644 --- a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php @@ -57,7 +57,7 @@ public function testGet() "str" => "déjà" 7 => b"é" "[]" => [] - "res" => resource:stream { + "res" => :stream { wrapper_type: "plainfile" stream_type: "STDIO" mode: "r" @@ -68,7 +68,7 @@ public function testGet() eof: false options: [] } - 8 => resource:Unknown {} + 8 => :Unknown {} "obj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo { #2 foo: "foo" "bar": "bar" diff --git a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php index 51be849ad6176..e8265599bd21e 100644 --- a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php @@ -44,14 +44,14 @@ public function testGet() $out = preg_replace('/[ \t]+$/m', '', $out); $var['file'] = htmlspecialchars($var['file'], ENT_QUOTES, 'UTF-8'); $intMax = PHP_INT_MAX; - preg_match('/sf-dump-(\\d{2,})/', $out, $matches); - $dumpId = $matches[1]; + preg_match('/sf-dump-\d+/', $out, $dumpId); + $dumpId = $dumpId[0]; $this->assertSame( <<array:25 [ - "number" => 1 - 0 => null #1 +array:25 [ + "number" => 1 + 0 => null #1 "const" => 1.1 1 => true 2 => false @@ -62,8 +62,8 @@ public function testGet() "str" => "déjà" 7 => b"é" "[]" => [] - "res" => resource:stream { - wrapper_type: "plainfile" + "res" => :stream { + wrapper_type: "plainfile" stream_type: "STDIO" mode: "r" unread_bytes: 0 @@ -72,14 +72,14 @@ public function testGet() blocked: true eof: false options: [] - } - 8 => resource:Unknown {} - "obj" => DumbFoo { #2 - foo: "foo" + } + 8 => :Unknown {} + "obj" => DumbFoo { #2 + foo: "foo" "bar": "bar" - } + } "closure" => Closure { - reflection: """ + reflection: """ Closure [ <user> {$closureLabel} Symfony\Component\VarDumper\Tests\Fixture\{closure} ] { @@ {$var['file']} {$var['line']} - {$var['line']} @@ -89,22 +89,22 @@ public function testGet() } } """ - } + } "line" => {$var['line']} "nobj" => array:1 [ - 0 => {} #3 - ] - "recurs" => array:1 [ #4 - 0 => &4 array:1 [@4] - ] - 9 => &1 null - "sobj" => DumbFoo {@2} - "snobj" => &3 {@3} - "snobj2" => {@3} + 0 => {} #3 + ] + "recurs" => array:1 [ #4 + 0 => &4 array:1 [@4] + ] + 9 => &1 null + "sobj" => DumbFoo {@2} + "snobj" => &3 {@3} + "snobj2" => {@3} "file" => "{$var['file']}" b"bin-key-é" => "" -] - +] + EOTXT ,