PluginProbe ʕ •ᴥ•ʔ
Kubio AI Page Builder / trunk
Kubio AI Page Builder vtrunk
2.9.0 2.8.6 2.8.5 2.8.4 2.8.3 2.8.2 2.8.1 trunk 1.0.0 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.7.3 1.8.0 1.8.1 1.8.2 1.9.0 2.0.0 2.1.1 2.1.2 2.1.3 2.2.0 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.5 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.6.2 2.6.3 2.6.5 2.6.6 2.6.7 2.7.0 2.7.1 2.7.2 2.7.3 2.8.0
kubio / vendor / symfony / var-dumper / Dumper / HtmlDumper.php
kubio / vendor / symfony / var-dumper / Dumper Last commit date
ContextProvider 1 year ago AbstractDumper.php 1 year ago CliDumper.php 1 year ago ContextualizedDumper.php 4 years ago DataDumperInterface.php 4 years ago HtmlDumper.php 1 year ago ServerDumper.php 1 year ago
HtmlDumper.php
987 lines
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\VarDumper\Dumper;
13
14 use Symfony\Component\VarDumper\Cloner\Cursor;
15 use Symfony\Component\VarDumper\Cloner\Data;
16
17 /**
18 * HtmlDumper dumps variables as HTML.
19 *
20 * @author Nicolas Grekas <p@tchwork.com>
21 */
22 class HtmlDumper extends CliDumper
23 {
24 public static $defaultOutput = 'php://output';
25
26 protected static $themes = [
27 'dark' => [
28 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all',
29 'num' => 'font-weight:bold; color:#1299DA',
30 'const' => 'font-weight:bold',
31 'str' => 'font-weight:bold; color:#56DB3A',
32 'note' => 'color:#1299DA',
33 'ref' => 'color:#A0A0A0',
34 'public' => 'color:#FFFFFF',
35 'protected' => 'color:#FFFFFF',
36 'private' => 'color:#FFFFFF',
37 'meta' => 'color:#B729D9',
38 'key' => 'color:#56DB3A',
39 'index' => 'color:#1299DA',
40 'ellipsis' => 'color:#FF8400',
41 'ns' => 'user-select:none;',
42 ],
43 'light' => [
44 'default' => 'background:none; color:#CC7832; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all',
45 'num' => 'font-weight:bold; color:#1299DA',
46 'const' => 'font-weight:bold',
47 'str' => 'font-weight:bold; color:#629755;',
48 'note' => 'color:#6897BB',
49 'ref' => 'color:#6E6E6E',
50 'public' => 'color:#262626',
51 'protected' => 'color:#262626',
52 'private' => 'color:#262626',
53 'meta' => 'color:#B729D9',
54 'key' => 'color:#789339',
55 'index' => 'color:#1299DA',
56 'ellipsis' => 'color:#CC7832',
57 'ns' => 'user-select:none;',
58 ],
59 ];
60
61 protected $dumpHeader;
62 protected $dumpPrefix = '<pre class=sf-dump id=%s data-indent-pad="%s">';
63 protected $dumpSuffix = '</pre><script>Sfdump(%s)</script>';
64 protected $dumpId = 'sf-dump';
65 protected $colors = true;
66 protected $headerIsDumped = false;
67 protected $lastDepth = -1;
68 protected $styles;
69
70 private $displayOptions = [
71 'maxDepth' => 1,
72 'maxStringLength' => 160,
73 'fileLinkFormat' => null,
74 ];
75 private $extraDisplayOptions = [];
76
77 /**
78 * {@inheritdoc}
79 */
80 public function __construct($output = null, ?string $charset = null, int $flags = 0)
81 {
82 AbstractDumper::__construct($output, $charset, $flags);
83 $this->dumpId = 'sf-dump-'.mt_rand();
84 $this->displayOptions['fileLinkFormat'] = \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
85 $this->styles = static::$themes['dark'] ?? self::$themes['dark'];
86 }
87
88 /**
89 * {@inheritdoc}
90 */
91 public function setStyles(array $styles)
92 {
93 $this->headerIsDumped = false;
94 $this->styles = $styles + $this->styles;
95 }
96
97 public function setTheme(string $themeName)
98 {
99 if (!isset(static::$themes[$themeName])) {
100 throw new \InvalidArgumentException(sprintf('Theme "%s" does not exist in class "%s".', $themeName, static::class));
101 }
102
103 $this->setStyles(static::$themes[$themeName]);
104 }
105
106 /**
107 * Configures display options.
108 *
109 * @param array $displayOptions A map of display options to customize the behavior
110 */
111 public function setDisplayOptions(array $displayOptions)
112 {
113 $this->headerIsDumped = false;
114 $this->displayOptions = $displayOptions + $this->displayOptions;
115 }
116
117 /**
118 * Sets an HTML header that will be dumped once in the output stream.
119 */
120 public function setDumpHeader(?string $header)
121 {
122 $this->dumpHeader = $header;
123 }
124
125 /**
126 * Sets an HTML prefix and suffix that will encapse every single dump.
127 */
128 public function setDumpBoundaries(string $prefix, string $suffix)
129 {
130 $this->dumpPrefix = $prefix;
131 $this->dumpSuffix = $suffix;
132 }
133
134 /**
135 * {@inheritdoc}
136 */
137 public function dump(Data $data, $output = null, array $extraDisplayOptions = [])
138 {
139 $this->extraDisplayOptions = $extraDisplayOptions;
140 $result = parent::dump($data, $output);
141 $this->dumpId = 'sf-dump-'.mt_rand();
142
143 return $result;
144 }
145
146 /**
147 * Dumps the HTML header.
148 */
149 protected function getDumpHeader()
150 {
151 $this->headerIsDumped = $this->outputStream ?? $this->lineDumper;
152
153 if (null !== $this->dumpHeader) {
154 return $this->dumpHeader;
155 }
156
157 $line = str_replace('{$options}', json_encode($this->displayOptions, \JSON_FORCE_OBJECT), <<<'EOHTML'
158 <script>
159 Sfdump = window.Sfdump || (function (doc) {
160
161 var refStyle = doc.createElement('style'),
162 rxEsc = /([.*+?^${}()|\[\]\/\\])/g,
163 idRx = /\bsf-dump-\d+-ref[012]\w+\b/,
164 keyHint = 0 <= navigator.platform.toUpperCase().indexOf('MAC') ? 'Cmd' : 'Ctrl',
165 addEventListener = function (e, n, cb) {
166 e.addEventListener(n, cb, false);
167 };
168
169 refStyle.innerHTML = 'pre.sf-dump .sf-dump-compact, .sf-dump-str-collapse .sf-dump-str-collapse, .sf-dump-str-expand .sf-dump-str-expand { display: none; }';
170 doc.head.appendChild(refStyle);
171 refStyle = doc.createElement('style');
172 doc.head.appendChild(refStyle);
173
174 if (!doc.addEventListener) {
175 addEventListener = function (element, eventName, callback) {
176 element.attachEvent('on' + eventName, function (e) {
177 e.preventDefault = function () {e.returnValue = false;};
178 e.target = e.srcElement;
179 callback(e);
180 });
181 };
182 }
183
184 function toggle(a, recursive) {
185 var s = a.nextSibling || {}, oldClass = s.className, arrow, newClass;
186
187 if (/\bsf-dump-compact\b/.test(oldClass)) {
188 arrow = '▼';
189 newClass = 'sf-dump-expanded';
190 } else if (/\bsf-dump-expanded\b/.test(oldClass)) {
191 arrow = '▶';
192 newClass = 'sf-dump-compact';
193 } else {
194 return false;
195 }
196
197 if (doc.createEvent && s.dispatchEvent) {
198 var event = doc.createEvent('Event');
199 event.initEvent('sf-dump-expanded' === newClass ? 'sfbeforedumpexpand' : 'sfbeforedumpcollapse', true, false);
200
201 s.dispatchEvent(event);
202 }
203
204 a.lastChild.innerHTML = arrow;
205 s.className = s.className.replace(/\bsf-dump-(compact|expanded)\b/, newClass);
206
207 if (recursive) {
208 try {
209 a = s.querySelectorAll('.'+oldClass);
210 for (s = 0; s < a.length; ++s) {
211 if (-1 == a[s].className.indexOf(newClass)) {
212 a[s].className = newClass;
213 a[s].previousSibling.lastChild.innerHTML = arrow;
214 }
215 }
216 } catch (e) {
217 }
218 }
219
220 return true;
221 };
222
223 function collapse(a, recursive) {
224 var s = a.nextSibling || {}, oldClass = s.className;
225
226 if (/\bsf-dump-expanded\b/.test(oldClass)) {
227 toggle(a, recursive);
228
229 return true;
230 }
231
232 return false;
233 };
234
235 function expand(a, recursive) {
236 var s = a.nextSibling || {}, oldClass = s.className;
237
238 if (/\bsf-dump-compact\b/.test(oldClass)) {
239 toggle(a, recursive);
240
241 return true;
242 }
243
244 return false;
245 };
246
247 function collapseAll(root) {
248 var a = root.querySelector('a.sf-dump-toggle');
249 if (a) {
250 collapse(a, true);
251 expand(a);
252
253 return true;
254 }
255
256 return false;
257 }
258
259 function reveal(node) {
260 var previous, parents = [];
261
262 while ((node = node.parentNode || {}) && (previous = node.previousSibling) && 'A' === previous.tagName) {
263 parents.push(previous);
264 }
265
266 if (0 !== parents.length) {
267 parents.forEach(function (parent) {
268 expand(parent);
269 });
270
271 return true;
272 }
273
274 return false;
275 }
276
277 function highlight(root, activeNode, nodes) {
278 resetHighlightedNodes(root);
279
280 Array.from(nodes||[]).forEach(function (node) {
281 if (!/\bsf-dump-highlight\b/.test(node.className)) {
282 node.className = node.className + ' sf-dump-highlight';
283 }
284 });
285
286 if (!/\bsf-dump-highlight-active\b/.test(activeNode.className)) {
287 activeNode.className = activeNode.className + ' sf-dump-highlight-active';
288 }
289 }
290
291 function resetHighlightedNodes(root) {
292 Array.from(root.querySelectorAll('.sf-dump-str, .sf-dump-key, .sf-dump-public, .sf-dump-protected, .sf-dump-private')).forEach(function (strNode) {
293 strNode.className = strNode.className.replace(/\bsf-dump-highlight\b/, '');
294 strNode.className = strNode.className.replace(/\bsf-dump-highlight-active\b/, '');
295 });
296 }
297
298 return function (root, x) {
299 root = doc.getElementById(root);
300
301 var indentRx = new RegExp('^('+(root.getAttribute('data-indent-pad') || ' ').replace(rxEsc, '\\$1')+')+', 'm'),
302 options = {$options},
303 elt = root.getElementsByTagName('A'),
304 len = elt.length,
305 i = 0, s, h,
306 t = [];
307
308 while (i < len) t.push(elt[i++]);
309
310 for (i in x) {
311 options[i] = x[i];
312 }
313
314 function a(e, f) {
315 addEventListener(root, e, function (e, n) {
316 if ('A' == e.target.tagName) {
317 f(e.target, e);
318 } else if ('A' == e.target.parentNode.tagName) {
319 f(e.target.parentNode, e);
320 } else {
321 n = /\bsf-dump-ellipsis\b/.test(e.target.className) ? e.target.parentNode : e.target;
322
323 if ((n = n.nextElementSibling) && 'A' == n.tagName) {
324 if (!/\bsf-dump-toggle\b/.test(n.className)) {
325 n = n.nextElementSibling || n;
326 }
327
328 f(n, e, true);
329 }
330 }
331 });
332 };
333 function isCtrlKey(e) {
334 return e.ctrlKey || e.metaKey;
335 }
336 function xpathString(str) {
337 var parts = str.match(/[^'"]+|['"]/g).map(function (part) {
338 if ("'" == part) {
339 return '"\'"';
340 }
341 if ('"' == part) {
342 return "'\"'";
343 }
344
345 return "'" + part + "'";
346 });
347
348 return "concat(" + parts.join(",") + ", '')";
349 }
350 function xpathHasClass(className) {
351 return "contains(concat(' ', normalize-space(@class), ' '), ' " + className +" ')";
352 }
353 addEventListener(root, 'mouseover', function (e) {
354 if ('' != refStyle.innerHTML) {
355 refStyle.innerHTML = '';
356 }
357 });
358 a('mouseover', function (a, e, c) {
359 if (c) {
360 e.target.style.cursor = "pointer";
361 } else if (a = idRx.exec(a.className)) {
362 try {
363 refStyle.innerHTML = 'pre.sf-dump .'+a[0]+'{background-color: #B729D9; color: #FFF !important; border-radius: 2px}';
364 } catch (e) {
365 }
366 }
367 });
368 a('click', function (a, e, c) {
369 if (/\bsf-dump-toggle\b/.test(a.className)) {
370 e.preventDefault();
371 if (!toggle(a, isCtrlKey(e))) {
372 var r = doc.getElementById(a.getAttribute('href').slice(1)),
373 s = r.previousSibling,
374 f = r.parentNode,
375 t = a.parentNode;
376 t.replaceChild(r, a);
377 f.replaceChild(a, s);
378 t.insertBefore(s, r);
379 f = f.firstChild.nodeValue.match(indentRx);
380 t = t.firstChild.nodeValue.match(indentRx);
381 if (f && t && f[0] !== t[0]) {
382 r.innerHTML = r.innerHTML.replace(new RegExp('^'+f[0].replace(rxEsc, '\\$1'), 'mg'), t[0]);
383 }
384 if (/\bsf-dump-compact\b/.test(r.className)) {
385 toggle(s, isCtrlKey(e));
386 }
387 }
388
389 if (c) {
390 } else if (doc.getSelection) {
391 try {
392 doc.getSelection().removeAllRanges();
393 } catch (e) {
394 doc.getSelection().empty();
395 }
396 } else {
397 doc.selection.empty();
398 }
399 } else if (/\bsf-dump-str-toggle\b/.test(a.className)) {
400 e.preventDefault();
401 e = a.parentNode.parentNode;
402 e.className = e.className.replace(/\bsf-dump-str-(expand|collapse)\b/, a.parentNode.className);
403 }
404 });
405
406 elt = root.getElementsByTagName('SAMP');
407 len = elt.length;
408 i = 0;
409
410 while (i < len) t.push(elt[i++]);
411 len = t.length;
412
413 for (i = 0; i < len; ++i) {
414 elt = t[i];
415 if ('SAMP' == elt.tagName) {
416 a = elt.previousSibling || {};
417 if ('A' != a.tagName) {
418 a = doc.createElement('A');
419 a.className = 'sf-dump-ref';
420 elt.parentNode.insertBefore(a, elt);
421 } else {
422 a.innerHTML += ' ';
423 }
424 a.title = (a.title ? a.title+'\n[' : '[')+keyHint+'+click] Expand all children';
425 a.innerHTML += elt.className == 'sf-dump-compact' ? '<span>▶</span>' : '<span>▼</span>';
426 a.className += ' sf-dump-toggle';
427
428 x = 1;
429 if ('sf-dump' != elt.parentNode.className) {
430 x += elt.parentNode.getAttribute('data-depth')/1;
431 }
432 } else if (/\bsf-dump-ref\b/.test(elt.className) && (a = elt.getAttribute('href'))) {
433 a = a.slice(1);
434 elt.className += ' '+a;
435
436 if (/[\[{]$/.test(elt.previousSibling.nodeValue)) {
437 a = a != elt.nextSibling.id && doc.getElementById(a);
438 try {
439 s = a.nextSibling;
440 elt.appendChild(a);
441 s.parentNode.insertBefore(a, s);
442 if (/^[@#]/.test(elt.innerHTML)) {
443 elt.innerHTML += ' <span>▶</span>';
444 } else {
445 elt.innerHTML = '<span>▶</span>';
446 elt.className = 'sf-dump-ref';
447 }
448 elt.className += ' sf-dump-toggle';
449 } catch (e) {
450 if ('&' == elt.innerHTML.charAt(0)) {
451 elt.innerHTML = '…';
452 elt.className = 'sf-dump-ref';
453 }
454 }
455 }
456 }
457 }
458
459 if (doc.evaluate && Array.from && root.children.length > 1) {
460 root.setAttribute('tabindex', 0);
461
462 SearchState = function () {
463 this.nodes = [];
464 this.idx = 0;
465 };
466 SearchState.prototype = {
467 next: function () {
468 if (this.isEmpty()) {
469 return this.current();
470 }
471 this.idx = this.idx < (this.nodes.length - 1) ? this.idx + 1 : 0;
472
473 return this.current();
474 },
475 previous: function () {
476 if (this.isEmpty()) {
477 return this.current();
478 }
479 this.idx = this.idx > 0 ? this.idx - 1 : (this.nodes.length - 1);
480
481 return this.current();
482 },
483 isEmpty: function () {
484 return 0 === this.count();
485 },
486 current: function () {
487 if (this.isEmpty()) {
488 return null;
489 }
490 return this.nodes[this.idx];
491 },
492 reset: function () {
493 this.nodes = [];
494 this.idx = 0;
495 },
496 count: function () {
497 return this.nodes.length;
498 },
499 };
500
501 function showCurrent(state)
502 {
503 var currentNode = state.current(), currentRect, searchRect;
504 if (currentNode) {
505 reveal(currentNode);
506 highlight(root, currentNode, state.nodes);
507 if ('scrollIntoView' in currentNode) {
508 currentNode.scrollIntoView(true);
509 currentRect = currentNode.getBoundingClientRect();
510 searchRect = search.getBoundingClientRect();
511 if (currentRect.top < (searchRect.top + searchRect.height)) {
512 window.scrollBy(0, -(searchRect.top + searchRect.height + 5));
513 }
514 }
515 }
516 counter.textContent = (state.isEmpty() ? 0 : state.idx + 1) + ' of ' + state.count();
517 }
518
519 var search = doc.createElement('div');
520 search.className = 'sf-dump-search-wrapper sf-dump-search-hidden';
521 search.innerHTML = '
522 <input type="text" class="sf-dump-search-input">
523 <span class="sf-dump-search-count">0 of 0<\/span>
524 <button type="button" class="sf-dump-search-input-previous" tabindex="-1">
525 <svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 1331l-166 165q-19 19-45 19t-45-19L896 965l-531 531q-19 19-45 19t-45-19l-166-165q-19-19-19-45.5t19-45.5l742-741q19-19 45-19t45 19l742 741q19 19 19 45.5t-19 45.5z"\/><\/svg>
526 <\/button>
527 <button type="button" class="sf-dump-search-input-next" tabindex="-1">
528 <svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 808l-742 741q-19 19-45 19t-45-19L109 808q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"\/><\/svg>
529 <\/button>
530 ';
531 root.insertBefore(search, root.firstChild);
532
533 var state = new SearchState();
534 var searchInput = search.querySelector('.sf-dump-search-input');
535 var counter = search.querySelector('.sf-dump-search-count');
536 var searchInputTimer = 0;
537 var previousSearchQuery = '';
538
539 addEventListener(searchInput, 'keyup', function (e) {
540 var searchQuery = e.target.value;
541 /* Don't perform anything if the pressed key didn't change the query */
542 if (searchQuery === previousSearchQuery) {
543 return;
544 }
545 previousSearchQuery = searchQuery;
546 clearTimeout(searchInputTimer);
547 searchInputTimer = setTimeout(function () {
548 state.reset();
549 collapseAll(root);
550 resetHighlightedNodes(root);
551 if ('' === searchQuery) {
552 counter.textContent = '0 of 0';
553
554 return;
555 }
556
557 var classMatches = [
558 "sf-dump-str",
559 "sf-dump-key",
560 "sf-dump-public",
561 "sf-dump-protected",
562 "sf-dump-private",
563 ].map(xpathHasClass).join(' or ');
564
565 var xpathResult = doc.evaluate('.//span[' + classMatches + '][contains(translate(child::text(), ' + xpathString(searchQuery.toUpperCase()) + ', ' + xpathString(searchQuery.toLowerCase()) + '), ' + xpathString(searchQuery.toLowerCase()) + ')]', root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
566
567 while (node = xpathResult.iterateNext()) state.nodes.push(node);
568
569 showCurrent(state);
570 }, 400);
571 });
572
573 Array.from(search.querySelectorAll('.sf-dump-search-input-next, .sf-dump-search-input-previous')).forEach(function (btn) {
574 addEventListener(btn, 'click', function (e) {
575 e.preventDefault();
576 -1 !== e.target.className.indexOf('next') ? state.next() : state.previous();
577 searchInput.focus();
578 collapseAll(root);
579 showCurrent(state);
580 })
581 });
582
583 addEventListener(root, 'keydown', function (e) {
584 var isSearchActive = !/\bsf-dump-search-hidden\b/.test(search.className);
585 if ((114 === e.keyCode && !isSearchActive) || (isCtrlKey(e) && 70 === e.keyCode)) {
586 /* F3 or CMD/CTRL + F */
587 if (70 === e.keyCode && document.activeElement === searchInput) {
588 /*
589 * If CMD/CTRL + F is hit while having focus on search input,
590 * the user probably meant to trigger browser search instead.
591 * Let the browser execute its behavior:
592 */
593 return;
594 }
595
596 e.preventDefault();
597 search.className = search.className.replace(/\bsf-dump-search-hidden\b/, '');
598 searchInput.focus();
599 } else if (isSearchActive) {
600 if (27 === e.keyCode) {
601 /* ESC key */
602 search.className += ' sf-dump-search-hidden';
603 e.preventDefault();
604 resetHighlightedNodes(root);
605 searchInput.value = '';
606 } else if (
607 (isCtrlKey(e) && 71 === e.keyCode) /* CMD/CTRL + G */
608 || 13 === e.keyCode /* Enter */
609 || 114 === e.keyCode /* F3 */
610 ) {
611 e.preventDefault();
612 e.shiftKey ? state.previous() : state.next();
613 collapseAll(root);
614 showCurrent(state);
615 }
616 }
617 });
618 }
619
620 if (0 >= options.maxStringLength) {
621 return;
622 }
623 try {
624 elt = root.querySelectorAll('.sf-dump-str');
625 len = elt.length;
626 i = 0;
627 t = [];
628
629 while (i < len) t.push(elt[i++]);
630 len = t.length;
631
632 for (i = 0; i < len; ++i) {
633 elt = t[i];
634 s = elt.innerText || elt.textContent;
635 x = s.length - options.maxStringLength;
636 if (0 < x) {
637 h = elt.innerHTML;
638 elt[elt.innerText ? 'innerText' : 'textContent'] = s.substring(0, options.maxStringLength);
639 elt.className += ' sf-dump-str-collapse';
640 elt.innerHTML = '<span class=sf-dump-str-collapse>'+h+'<a class="sf-dump-ref sf-dump-str-toggle" title="Collapse"> ◀</a></span>'+
641 '<span class=sf-dump-str-expand>'+elt.innerHTML+'<a class="sf-dump-ref sf-dump-str-toggle" title="'+x+' remaining characters"> ▶</a></span>';
642 }
643 }
644 } catch (e) {
645 }
646 };
647
648 })(document);
649 </script><style>
650 pre.sf-dump {
651 display: block;
652 white-space: pre;
653 padding: 5px;
654 overflow: initial !important;
655 }
656 pre.sf-dump:after {
657 content: "";
658 visibility: hidden;
659 display: block;
660 height: 0;
661 clear: both;
662 }
663 pre.sf-dump span {
664 display: inline;
665 }
666 pre.sf-dump a {
667 text-decoration: none;
668 cursor: pointer;
669 border: 0;
670 outline: none;
671 color: inherit;
672 }
673 pre.sf-dump img {
674 max-width: 50em;
675 max-height: 50em;
676 margin: .5em 0 0 0;
677 padding: 0;
678 background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAHUlEQVQY02O8zAABilCaiQEN0EeA8QuUcX9g3QEAAjcC5piyhyEAAAAASUVORK5CYII=) #D3D3D3;
679 }
680 pre.sf-dump .sf-dump-ellipsis {
681 display: inline-block;
682 overflow: visible;
683 text-overflow: ellipsis;
684 max-width: 5em;
685 white-space: nowrap;
686 overflow: hidden;
687 vertical-align: top;
688 }
689 pre.sf-dump .sf-dump-ellipsis+.sf-dump-ellipsis {
690 max-width: none;
691 }
692 pre.sf-dump code {
693 display:inline;
694 padding:0;
695 background:none;
696 }
697 .sf-dump-public.sf-dump-highlight,
698 .sf-dump-protected.sf-dump-highlight,
699 .sf-dump-private.sf-dump-highlight,
700 .sf-dump-str.sf-dump-highlight,
701 .sf-dump-key.sf-dump-highlight {
702 background: rgba(111, 172, 204, 0.3);
703 border: 1px solid #7DA0B1;
704 border-radius: 3px;
705 }
706 .sf-dump-public.sf-dump-highlight-active,
707 .sf-dump-protected.sf-dump-highlight-active,
708 .sf-dump-private.sf-dump-highlight-active,
709 .sf-dump-str.sf-dump-highlight-active,
710 .sf-dump-key.sf-dump-highlight-active {
711 background: rgba(253, 175, 0, 0.4);
712 border: 1px solid #ffa500;
713 border-radius: 3px;
714 }
715 pre.sf-dump .sf-dump-search-hidden {
716 display: none !important;
717 }
718 pre.sf-dump .sf-dump-search-wrapper {
719 font-size: 0;
720 white-space: nowrap;
721 margin-bottom: 5px;
722 display: flex;
723 position: -webkit-sticky;
724 position: sticky;
725 top: 5px;
726 }
727 pre.sf-dump .sf-dump-search-wrapper > * {
728 vertical-align: top;
729 box-sizing: border-box;
730 height: 21px;
731 font-weight: normal;
732 border-radius: 0;
733 background: #FFF;
734 color: #757575;
735 border: 1px solid #BBB;
736 }
737 pre.sf-dump .sf-dump-search-wrapper > input.sf-dump-search-input {
738 padding: 3px;
739 height: 21px;
740 font-size: 12px;
741 border-right: none;
742 border-top-left-radius: 3px;
743 border-bottom-left-radius: 3px;
744 color: #000;
745 min-width: 15px;
746 width: 100%;
747 }
748 pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next,
749 pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous {
750 background: #F2F2F2;
751 outline: none;
752 border-left: none;
753 font-size: 0;
754 line-height: 0;
755 }
756 pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next {
757 border-top-right-radius: 3px;
758 border-bottom-right-radius: 3px;
759 }
760 pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next > svg,
761 pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous > svg {
762 pointer-events: none;
763 width: 12px;
764 height: 12px;
765 }
766 pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-count {
767 display: inline-block;
768 padding: 0 5px;
769 margin: 0;
770 border-left: none;
771 line-height: 21px;
772 font-size: 12px;
773 }
774 EOHTML
775 );
776
777 foreach ($this->styles as $class => $style) {
778 $line .= 'pre.sf-dump'.('default' === $class ? ', pre.sf-dump' : '').' .sf-dump-'.$class.'{'.$style.'}';
779 }
780 $line .= 'pre.sf-dump .sf-dump-ellipsis-note{'.$this->styles['note'].'}';
781
782 return $this->dumpHeader = preg_replace('/\s+/', ' ', $line).'</style>'.$this->dumpHeader;
783 }
784
785 /**
786 * {@inheritdoc}
787 */
788 public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut)
789 {
790 if ('' === $str && isset($cursor->attr['img-data'], $cursor->attr['content-type'])) {
791 $this->dumpKey($cursor);
792 $this->line .= $this->style('default', $cursor->attr['img-size'] ?? '', []);
793 $this->line .= $cursor->depth >= $this->displayOptions['maxDepth'] ? ' <samp class=sf-dump-compact>' : ' <samp class=sf-dump-expanded>';
794 $this->endValue($cursor);
795 $this->line .= $this->indentPad;
796 $this->line .= sprintf('<img src="data:%s;base64,%s" /></samp>', $cursor->attr['content-type'], base64_encode($cursor->attr['img-data']));
797 $this->endValue($cursor);
798 } else {
799 parent::dumpString($cursor, $str, $bin, $cut);
800 }
801 }
802
803 /**
804 * {@inheritdoc}
805 */
806 public function enterHash(Cursor $cursor, int $type, $class, bool $hasChild)
807 {
808 if (Cursor::HASH_OBJECT === $type) {
809 $cursor->attr['depth'] = $cursor->depth;
810 }
811 parent::enterHash($cursor, $type, $class, false);
812
813 if ($cursor->skipChildren || $cursor->depth >= $this->displayOptions['maxDepth']) {
814 $cursor->skipChildren = false;
815 $eol = ' class=sf-dump-compact>';
816 } else {
817 $this->expandNextHash = false;
818 $eol = ' class=sf-dump-expanded>';
819 }
820
821 if ($hasChild) {
822 $this->line .= '<samp data-depth='.($cursor->depth + 1);
823 if ($cursor->refIndex) {
824 $r = Cursor::HASH_OBJECT !== $type ? 1 - (Cursor::HASH_RESOURCE !== $type) : 2;
825 $r .= $r && 0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->refIndex;
826
827 $this->line .= sprintf(' id=%s-ref%s', $this->dumpId, $r);
828 }
829 $this->line .= $eol;
830 $this->dumpLine($cursor->depth);
831 }
832 }
833
834 /**
835 * {@inheritdoc}
836 */
837 public function leaveHash(Cursor $cursor, int $type, $class, bool $hasChild, int $cut)
838 {
839 $this->dumpEllipsis($cursor, $hasChild, $cut);
840 if ($hasChild) {
841 $this->line .= '</samp>';
842 }
843 parent::leaveHash($cursor, $type, $class, $hasChild, 0);
844 }
845
846 /**
847 * {@inheritdoc}
848 */
849 protected function style(string $style, string $value, array $attr = [])
850 {
851 if ('' === $value) {
852 return '';
853 }
854
855 $v = esc($value);
856
857 if ('ref' === $style) {
858 if (empty($attr['count'])) {
859 return sprintf('<a class=sf-dump-ref>%s</a>', $v);
860 }
861 $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1);
862
863 return sprintf('<a class=sf-dump-ref href=#%s-ref%s title="%d occurrences">%s</a>', $this->dumpId, $r, 1 + $attr['count'], $v);
864 }
865
866 if ('const' === $style && isset($attr['value'])) {
867 $style .= sprintf(' title="%s"', esc(\is_scalar($attr['value']) ? $attr['value'] : json_encode($attr['value'])));
868 } elseif ('public' === $style) {
869 $style .= sprintf(' title="%s"', empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property');
870 } elseif ('str' === $style && 1 < $attr['length']) {
871 $style .= sprintf(' title="%d%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : '');
872 } elseif ('note' === $style && 0 < ($attr['depth'] ?? 0) && false !== $c = strrpos($value, '\\')) {
873 $style .= ' title=""';
874 $attr += [
875 'ellipsis' => \strlen($value) - $c,
876 'ellipsis-type' => 'note',
877 'ellipsis-tail' => 1,
878 ];
879 } elseif ('protected' === $style) {
880 $style .= ' title="Protected property"';
881 } elseif ('meta' === $style && isset($attr['title'])) {
882 $style .= sprintf(' title="%s"', esc($this->utf8Encode($attr['title'])));
883 } elseif ('private' === $style) {
884 $style .= sprintf(' title="Private property defined in class:&#10;`%s`"', esc($this->utf8Encode($attr['class'])));
885 }
886 $map = static::$controlCharsMap;
887
888 if (isset($attr['ellipsis'])) {
889 $class = 'sf-dump-ellipsis';
890 if (isset($attr['ellipsis-type'])) {
891 $class = sprintf('"%s sf-dump-ellipsis-%s"', $class, $attr['ellipsis-type']);
892 }
893 $label = esc(substr($value, -$attr['ellipsis']));
894 $style = str_replace(' title="', " title=\"$v\n", $style);
895 $v = sprintf('<span class=%s>%s</span>', $class, substr($v, 0, -\strlen($label)));
896
897 if (!empty($attr['ellipsis-tail'])) {
898 $tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail'])));
899 $v .= sprintf('<span class=%s>%s</span>%s', $class, substr($label, 0, $tail), substr($label, $tail));
900 } else {
901 $v .= $label;
902 }
903 }
904
905 $v = "<span class=sf-dump-{$style}>".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) {
906 $s = $b = '<span class="sf-dump-default';
907 $c = $c[$i = 0];
908 if ($ns = "\r" === $c[$i] || "\n" === $c[$i]) {
909 $s .= ' sf-dump-ns';
910 }
911 $s .= '">';
912 do {
913 if (("\r" === $c[$i] || "\n" === $c[$i]) !== $ns) {
914 $s .= '</span>'.$b;
915 if ($ns = !$ns) {
916 $s .= ' sf-dump-ns';
917 }
918 $s .= '">';
919 }
920
921 $s .= $map[$c[$i]] ?? sprintf('\x%02X', \ord($c[$i]));
922 } while (isset($c[++$i]));
923
924 return $s.'</span>';
925 }, $v).'</span>';
926
927 if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) {
928 $attr['href'] = $href;
929 }
930 if (isset($attr['href'])) {
931 $target = isset($attr['file']) ? '' : ' target="_blank"';
932 $v = sprintf('<a href="%s"%s rel="noopener noreferrer">%s</a>', esc($this->utf8Encode($attr['href'])), $target, $v);
933 }
934 if (isset($attr['lang'])) {
935 $v = sprintf('<code class="%s">%s</code>', esc($attr['lang']), $v);
936 }
937
938 return $v;
939 }
940
941 /**
942 * {@inheritdoc}
943 */
944 protected function dumpLine(int $depth, bool $endOfValue = false)
945 {
946 if (-1 === $this->lastDepth) {
947 $this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line;
948 }
949 if ($this->headerIsDumped !== ($this->outputStream ?? $this->lineDumper)) {
950 $this->line = $this->getDumpHeader().$this->line;
951 }
952
953 if (-1 === $depth) {
954 $args = ['"'.$this->dumpId.'"'];
955 if ($this->extraDisplayOptions) {
956 $args[] = json_encode($this->extraDisplayOptions, \JSON_FORCE_OBJECT);
957 }
958 // Replace is for BC
959 $this->line .= sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args));
960 }
961 $this->lastDepth = $depth;
962
963 $this->line = mb_encode_numericentity($this->line, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8');
964
965 if (-1 === $depth) {
966 AbstractDumper::dumpLine(0);
967 }
968 AbstractDumper::dumpLine($depth);
969 }
970
971 private function getSourceLink(string $file, int $line)
972 {
973 $options = $this->extraDisplayOptions + $this->displayOptions;
974
975 if ($fmt = $options['fileLinkFormat']) {
976 return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line);
977 }
978
979 return false;
980 }
981 }
982
983 function esc(string $str)
984 {
985 return htmlspecialchars($str, \ENT_QUOTES, 'UTF-8');
986 }
987