[TASK] Streamline phpdoc annotations in EXT:extbase
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Utility / DebuggerUtility.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Utility;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * This class is a backport of the corresponding class of TYPO3 Flow.
19 * All credits go to the TYPO3 Flow team.
20 *
21 * A debugging utility class
22 */
23 class DebuggerUtility
24 {
25 const PLAINTEXT_INDENT = ' ';
26 const HTML_INDENT = '&nbsp;&nbsp;&nbsp;';
27
28 /**
29 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
30 */
31 protected static $renderedObjects;
32
33 /**
34 * Hardcoded list of Extbase class names (regex) which should not be displayed during debugging
35 *
36 * @var array
37 */
38 protected static $blacklistedClassNames = [
39 'PHPUnit_Framework_MockObject_InvocationMocker',
40 \TYPO3\CMS\Extbase\Reflection\ReflectionService::class,
41 \TYPO3\CMS\Extbase\Object\ObjectManager::class,
42 \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::class,
43 \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class,
44 \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory::class,
45 \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class
46 ];
47
48 /**
49 * Hardcoded list of property names (regex) which should not be displayed during debugging
50 *
51 * @var array
52 */
53 protected static $blacklistedPropertyNames = ['warning'];
54
55 /**
56 * Is set to TRUE once the CSS file is included in the current page to prevent double inclusions of the CSS file.
57 *
58 * @var bool
59 */
60 protected static $stylesheetEchoed = false;
61
62 /**
63 * Defines the max recursion depth of the dump, set to 8 due to common memory limits
64 *
65 * @var int
66 */
67 protected static $maxDepth = 8;
68
69 /**
70 * Clear the state of the debugger
71 */
72 protected static function clearState()
73 {
74 self::$renderedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
75 }
76
77 /**
78 * Renders a dump of the given value
79 *
80 * @param mixed $value
81 * @param int $level
82 * @param bool $plainText
83 * @param bool $ansiColors
84 * @return string
85 */
86 protected static function renderDump($value, $level, $plainText, $ansiColors)
87 {
88 $dump = '';
89 if (is_string($value)) {
90 $croppedValue = strlen($value) > 2000 ? substr($value, 0, 2000) . '...' : $value;
91 if ($plainText) {
92 $dump = self::ansiEscapeWrap('"' . implode(PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level + 1), str_split($croppedValue, 76)) . '"', '33', $ansiColors) . ' (' . strlen($value) . ' chars)';
93 } else {
94 $lines = str_split($croppedValue, 76);
95 $lines = array_map('htmlspecialchars', $lines);
96 $dump = sprintf('\'<span class="extbase-debug-string">%s</span>\' (%s chars)', implode('<br />' . str_repeat(self::HTML_INDENT, $level + 1), $lines), strlen($value));
97 }
98 } elseif (is_numeric($value)) {
99 $dump = sprintf('%s (%s)', self::ansiEscapeWrap($value, '35', $ansiColors), gettype($value));
100 } elseif (is_bool($value)) {
101 $dump = $value ? self::ansiEscapeWrap('TRUE', '32', $ansiColors) : self::ansiEscapeWrap('FALSE', '32', $ansiColors);
102 } elseif ($value === null || is_resource($value)) {
103 $dump = gettype($value);
104 } elseif (is_array($value)) {
105 $dump = self::renderArray($value, $level + 1, $plainText, $ansiColors);
106 } elseif (is_object($value)) {
107 if ($value instanceof \Closure) {
108 $dump = self::renderClosure($value, $level + 1, $plainText, $ansiColors);
109 } else {
110 $dump = self::renderObject($value, $level + 1, $plainText, $ansiColors);
111 }
112 }
113 return $dump;
114 }
115
116 /**
117 * Renders a dump of the given array
118 *
119 * @param array|\Traversable $array
120 * @param int $level
121 * @param bool $plainText
122 * @param bool $ansiColors
123 * @return string
124 */
125 protected static function renderArray($array, $level, $plainText = false, $ansiColors = false)
126 {
127 $content = '';
128 $count = count($array);
129
130 if ($plainText) {
131 $header = self::ansiEscapeWrap('array', '36', $ansiColors);
132 } else {
133 $header = '<span class="extbase-debug-type">array</span>';
134 }
135 $header .= $count > 0 ? '(' . $count . ' item' . ($count > 1 ? 's' : '') . ')' : '(empty)';
136 if ($level >= self::$maxDepth) {
137 if ($plainText) {
138 $header .= ' ' . self::ansiEscapeWrap('max depth', '47;30', $ansiColors);
139 } else {
140 $header .= '<span class="extbase-debug-filtered">max depth</span>';
141 }
142 } else {
143 $content = self::renderCollection($array, $level, $plainText, $ansiColors);
144 if (!$plainText) {
145 $header = ($level > 1 && $count > 0 ? '<input type="checkbox" /><span class="extbase-debug-header" >' : '<span>') . $header . '</span >';
146 }
147 }
148 if ($level > 1 && $count > 0 && !$plainText) {
149 $dump = '<span class="extbase-debugger-tree">' . $header . '<span class="extbase-debug-content">' . $content . '</span></span>';
150 } else {
151 $dump = $header . $content;
152 }
153 return $dump;
154 }
155
156 /**
157 * Renders a dump of the given object
158 *
159 * @param object $object
160 * @param int $level
161 * @param bool $plainText
162 * @param bool $ansiColors
163 * @return string
164 */
165 protected static function renderObject($object, $level, $plainText = false, $ansiColors = false)
166 {
167 if ($object instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
168 $object = $object->_loadRealInstance();
169 if (!$object) {
170 return gettype($object);
171 }
172 }
173 $header = self::renderHeader($object, $level, $plainText, $ansiColors);
174 if ($level < self::$maxDepth && !self::isBlacklisted($object) && !(self::isAlreadyRendered($object) && $plainText !== true)) {
175 $content = self::renderContent($object, $level, $plainText, $ansiColors);
176 } else {
177 $content = '';
178 }
179 if ($plainText) {
180 return $header . $content;
181 }
182 return '<span class="extbase-debugger-tree">' . $header . '<span class="extbase-debug-content">' . $content . '</span></span>';
183 }
184
185 /**
186 * Renders a dump of the given closure
187 *
188 * @param \Closure $object
189 * @param int $level
190 * @param bool $plainText
191 * @param bool $ansiColors
192 * @return string
193 */
194 protected static function renderClosure($object, $level, $plainText = false, $ansiColors = false)
195 {
196 $header = self::renderHeader($object, $level, $plainText, $ansiColors);
197 if ($level < self::$maxDepth && (!self::isAlreadyRendered($object) || $plainText)) {
198 $content = self::renderContent($object, $level, $plainText, $ansiColors);
199 } else {
200 $content = '';
201 }
202 if ($plainText) {
203 return $header . $content;
204 }
205 return '<span class="extbase-debugger-tree"><input type="checkbox" /><span class="extbase-debug-header">' . $header . '</span><span class="extbase-debug-content">' . $content . '</span></span>';
206 }
207
208 /**
209 * Checks if a given object or property should be excluded/filtered
210 *
211 * @param object $value An ReflectionProperty or other Object
212 * @return bool TRUE if the given object should be filtered
213 */
214 protected static function isBlacklisted($value)
215 {
216 $result = false;
217 if ($value instanceof \ReflectionProperty) {
218 $result = in_array($value->getName(), self::$blacklistedPropertyNames, true);
219 } elseif (is_object($value)) {
220 $result = in_array(get_class($value), self::$blacklistedClassNames, true);
221 }
222 return $result;
223 }
224
225 /**
226 * Checks if a given object was already rendered.
227 *
228 * @param object $object
229 * @return bool TRUE if the given object was already rendered
230 */
231 protected static function isAlreadyRendered($object)
232 {
233 return self::$renderedObjects->contains($object);
234 }
235
236 /**
237 * Renders the header of a given object/collection. It is usually the class name along with some flags.
238 *
239 * @param object $object
240 * @param int $level
241 * @param bool $plainText
242 * @param bool $ansiColors
243 * @return string The rendered header with tags
244 */
245 protected static function renderHeader($object, $level, $plainText, $ansiColors)
246 {
247 $dump = '';
248 $persistenceType = '';
249 $className = get_class($object);
250 $classReflection = new \ReflectionClass($className);
251 if ($plainText) {
252 $dump .= self::ansiEscapeWrap($className, '36', $ansiColors);
253 } else {
254 $dump .= '<span class="extbase-debug-type">' . htmlspecialchars($className) . '</span>';
255 }
256 if (!$object instanceof \Closure) {
257 if ($object instanceof \TYPO3\CMS\Core\SingletonInterface) {
258 $scope = 'singleton';
259 } else {
260 $scope = 'prototype';
261 }
262 if ($plainText) {
263 $dump .= ' ' . self::ansiEscapeWrap($scope, '44;37', $ansiColors);
264 } else {
265 $dump .= $scope ? '<span class="extbase-debug-scope">' . $scope . '</span>' : '';
266 }
267 if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject) {
268 if ($object->_isDirty()) {
269 $persistenceType = 'modified';
270 } elseif ($object->_isNew()) {
271 $persistenceType = 'transient';
272 } else {
273 $persistenceType = 'persistent';
274 }
275 }
276 if ($object instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage && $object->_isDirty()) {
277 $persistenceType = 'modified';
278 }
279 if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractEntity) {
280 $domainObjectType = 'entity';
281 } elseif ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject) {
282 $domainObjectType = 'valueobject';
283 } else {
284 $domainObjectType = 'object';
285 }
286 if ($plainText) {
287 $dump .= ' ' . self::ansiEscapeWrap(($persistenceType ? $persistenceType . ' ' : '') . $domainObjectType, '42;30', $ansiColors);
288 } else {
289 $dump .= '<span class="extbase-debug-ptype">' . ($persistenceType ? $persistenceType . ' ' : '') . $domainObjectType . '</span>';
290 }
291 }
292 if (strpos(implode('|', self::$blacklistedClassNames), get_class($object)) > 0) {
293 if ($plainText) {
294 $dump .= ' ' . self::ansiEscapeWrap('filtered', '47;30', $ansiColors);
295 } else {
296 $dump .= '<span class="extbase-debug-filtered">filtered</span>';
297 }
298 } elseif (self::$renderedObjects->contains($object) && !$plainText) {
299 $dump = '<a href="javascript:;" onclick="document.location.hash=\'#' . spl_object_hash($object) . '\';" class="extbase-debug-seeabove">' . $dump . '<span class="extbase-debug-filtered">see above</span></a>';
300 } elseif ($level >= self::$maxDepth && !$object instanceof \DateTime) {
301 if ($plainText) {
302 $dump .= ' ' . self::ansiEscapeWrap('max depth', '47;30', $ansiColors);
303 } else {
304 $dump .= '<span class="extbase-debug-filtered">max depth</span>';
305 }
306 } elseif ($level > 1 && !$object instanceof \DateTime && !$plainText) {
307 if (($object instanceof \Countable && empty($object)) || empty($classReflection->getProperties())) {
308 $dump = '<span>' . $dump . '</span>';
309 } else {
310 $dump = '<input type="checkbox" id="' . spl_object_hash($object) . '" /><span class="extbase-debug-header">' . $dump . '</span>';
311 }
312 }
313 if ($object instanceof \Countable) {
314 $objectCount = count($object);
315 $dump .= $objectCount > 0 ? ' (' . $objectCount . ' items)' : ' (empty)';
316 }
317 if ($object instanceof \DateTime) {
318 $dump .= ' (' . $object->format(\DateTime::RFC3339) . ', ' . $object->getTimestamp() . ')';
319 }
320 if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface && !$object->_isNew()) {
321 $dump .= ' (uid=' . $object->getUid() . ', pid=' . $object->getPid() . ')';
322 }
323 return $dump;
324 }
325
326 /**
327 * @param object $object
328 * @param int $level
329 * @param bool $plainText
330 * @param bool $ansiColors
331 * @return string The rendered body content of the Object(Storage)
332 */
333 protected static function renderContent($object, $level, $plainText, $ansiColors)
334 {
335 $dump = '';
336 if ($object instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage || $object instanceof \Iterator || $object instanceof \ArrayObject) {
337 $dump .= self::renderCollection($object, $level, $plainText, $ansiColors);
338 } else {
339 self::$renderedObjects->attach($object);
340 if (!$plainText) {
341 $dump .= '<a name="' . spl_object_hash($object) . '" id="' . spl_object_hash($object) . '"></a>';
342 }
343 if ($object instanceof \Closure) {
344 $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level)
345 . ($plainText ? '' : '<span class="extbase-debug-closure">')
346 . self::ansiEscapeWrap('function (', '33', $ansiColors) . ($plainText ? '' : '</span>');
347
348 $reflectionFunction = new \ReflectionFunction($object);
349 $params = [];
350 foreach ($reflectionFunction->getParameters() as $parameter) {
351 $parameterDump = '';
352 if ($parameter->isArray()) {
353 if ($plainText) {
354 $parameterDump .= self::ansiEscapeWrap('array ', '36', $ansiColors);
355 } else {
356 $parameterDump .= '<span class="extbase-debug-type">array </span>';
357 }
358 } elseif ($parameter->getClass()) {
359 if ($plainText) {
360 $parameterDump .= self::ansiEscapeWrap($parameter->getClass()->name . ' ', '36', $ansiColors);
361 } else {
362 $parameterDump .= '<span class="extbase-debug-type">'
363 . htmlspecialchars($parameter->getClass()->name) . '</span>';
364 }
365 }
366 if ($parameter->isPassedByReference()) {
367 $parameterDump .= '&';
368 }
369 if ($parameter->isVariadic()) {
370 $parameterDump .= '...';
371 }
372 if ($plainText) {
373 $parameterDump .= self::ansiEscapeWrap('$' . $parameter->name, '37', $ansiColors);
374 } else {
375 $parameterDump .= '<span class="extbase-debug-property">'
376 . htmlspecialchars('$' . $parameter->name) . '</span>';
377 }
378 if ($parameter->isDefaultValueAvailable()) {
379 $parameterDump .= ' = ';
380 if ($plainText) {
381 $parameterDump .= self::ansiEscapeWrap(var_export($parameter->getDefaultValue(), true), '33', $ansiColors);
382 } else {
383 $parameterDump .= '<span class="extbase-debug-string">'
384 . htmlspecialchars(var_export($parameter->getDefaultValue(), true)) . '</span>';
385 }
386 }
387 $params[] = $parameterDump;
388 }
389 $dump .= implode(', ', $params);
390 if ($plainText) {
391 $dump .= self::ansiEscapeWrap(') {' . PHP_EOL, '33', $ansiColors);
392 } else {
393 $dump .= '<span class="extbase-debug-closure">) {' . PHP_EOL . '</span>';
394 }
395 $lines = file($reflectionFunction->getFileName());
396 for ($l = $reflectionFunction->getStartLine(); $l < $reflectionFunction->getEndLine() - 1; ++$l) {
397 $dump .= $plainText ? $lines[$l] : htmlspecialchars($lines[$l]);
398 }
399 $dump .= str_repeat(self::PLAINTEXT_INDENT, $level);
400 if ($plainText) {
401 $dump .= self::ansiEscapeWrap('}' . PHP_EOL, '33', $ansiColors);
402 } else {
403 $dump .= '<span class="extbase-debug-closure">}</span>';
404 }
405 } else {
406 if (get_class($object) === 'stdClass') {
407 $objReflection = new \ReflectionObject($object);
408 $properties = $objReflection->getProperties();
409 } else {
410 $classReflection = new \ReflectionClass(get_class($object));
411 $properties = $classReflection->getProperties();
412 }
413 foreach ($properties as $property) {
414 if (self::isBlacklisted($property)) {
415 continue;
416 }
417 $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level);
418 if ($plainText) {
419 $dump .= self::ansiEscapeWrap($property->getName(), '37', $ansiColors);
420 } else {
421 $dump .= '<span class="extbase-debug-property">'
422 . htmlspecialchars($property->getName()) . '</span>';
423 }
424 $dump .= ' => ';
425 $property->setAccessible(true);
426 $visibility = ($property->isProtected() ? 'protected' : ($property->isPrivate() ? 'private' : 'public'));
427 if ($plainText) {
428 $dump .= self::ansiEscapeWrap($visibility, '42;30', $ansiColors) . ' ';
429 } else {
430 $dump .= '<span class="extbase-debug-visibility">' . $visibility . '</span>';
431 }
432 $dump .= self::renderDump($property->getValue($object), $level, $plainText, $ansiColors);
433 if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject && !$object->_isNew() && $object->_isDirty($property->getName())) {
434 if ($plainText) {
435 $dump .= ' ' . self::ansiEscapeWrap('modified', '43;30', $ansiColors);
436 } else {
437 $dump .= '<span class="extbase-debug-dirty">modified</span>';
438 }
439 }
440 }
441 }
442 }
443 return $dump;
444 }
445
446 /**
447 * @param mixed $collection
448 * @param int $level
449 * @param bool $plainText
450 * @param bool $ansiColors
451 * @return string
452 */
453 protected static function renderCollection($collection, $level, $plainText, $ansiColors)
454 {
455 $dump = '';
456 foreach ($collection as $key => $value) {
457 $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level);
458 if ($plainText) {
459 $dump .= self::ansiEscapeWrap($key, '37', $ansiColors);
460 } else {
461 $dump .= '<span class="extbase-debug-property">' . htmlspecialchars($key) . '</span>';
462 }
463 $dump .= ' => ';
464 $dump .= self::renderDump($value, $level, $plainText, $ansiColors);
465 }
466 if ($collection instanceof \Iterator) {
467 $collection->rewind();
468 }
469 return $dump;
470 }
471
472 /**
473 * Wrap a string with the ANSI escape sequence for colorful output
474 *
475 * @param string $string The string to wrap
476 * @param string $ansiColors The ansi color sequence (e.g. "1;37")
477 * @param bool $enable If FALSE, the raw string will be returned
478 * @return string The wrapped or raw string
479 */
480 protected static function ansiEscapeWrap($string, $ansiColors, $enable = true)
481 {
482 if ($enable) {
483 return '\e[' . $ansiColors . 'm' . $string . '\e[0m';
484 }
485 return $string;
486 }
487
488 /**
489 * A var_dump function optimized for Extbase's object structures
490 *
491 * @param mixed $variable The value to dump
492 * @param string $title optional custom title for the debug output
493 * @param int $maxDepth Sets the max recursion depth of the dump. De- or increase the number according to your needs and memory limit.
494 * @param bool $plainText If TRUE, the dump is in plain text, if FALSE the debug output is in HTML format.
495 * @param bool $ansiColors If TRUE (default), ANSI color codes is added to the output, if FALSE the debug output not colored.
496 * @param bool $return if TRUE, the dump is returned for custom post-processing (e.g. embed in custom HTML). If FALSE (default), the dump is directly displayed.
497 * @param array $blacklistedClassNames An array of class names (RegEx) to be filtered. Default is an array of some common class names.
498 * @param array $blacklistedPropertyNames An array of property names and/or array keys (RegEx) to be filtered. Default is an array of some common property names.
499 * @return string if $return is TRUE, the dump is returned. By default, the dump is directly displayed, and nothing is returned.
500 */
501 public static function var_dump($variable, $title = null, $maxDepth = 8, $plainText = false, $ansiColors = true, $return = false, $blacklistedClassNames = null, $blacklistedPropertyNames = null)
502 {
503 self::$maxDepth = $maxDepth;
504 if ($title === null) {
505 $title = 'Extbase Variable Dump';
506 }
507 $ansiColors = $plainText && $ansiColors;
508 if ($ansiColors === true) {
509 $title = '\e[1m' . $title . '\e[0m';
510 }
511 $backupBlacklistedClassNames = self::$blacklistedClassNames;
512 if (is_array($blacklistedClassNames)) {
513 self::$blacklistedClassNames = $blacklistedClassNames;
514 }
515 $backupBlacklistedPropertyNames = self::$blacklistedPropertyNames;
516 if (is_array($blacklistedPropertyNames)) {
517 self::$blacklistedPropertyNames = $blacklistedPropertyNames;
518 }
519 self::clearState();
520 $css = '';
521 if (!$plainText && self::$stylesheetEchoed === false) {
522 $css = '
523 <style type=\'text/css\'>
524 .extbase-debugger-tree{position:relative}
525 .extbase-debugger-tree input{position:absolute !important;float: none !important;top:0;left:0;height:14px;width:14px;margin:0 !important;cursor:pointer;opacity:0;z-index:2}
526 .extbase-debugger-tree input~.extbase-debug-content{display:none}
527 .extbase-debugger-tree .extbase-debug-header:before{position:relative;top:3px;content:"";padding:0;line-height:10px;height:12px;width:12px;text-align:center;margin:0 3px 0 0;background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTIgMTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEyIDEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHN0eWxlIHR5cGU9InRleHQvY3NzIj4uc3Qwe2ZpbGw6Izg4ODg4ODt9PC9zdHlsZT48cGF0aCBpZD0iQm9yZGVyIiBjbGFzcz0ic3QwIiBkPSJNMTEsMTFIMFYwaDExVjExeiBNMTAsMUgxdjloOVYxeiIvPjxnIGlkPSJJbm5lciI+PHJlY3QgeD0iMiIgeT0iNSIgY2xhc3M9InN0MCIgd2lkdGg9IjciIGhlaWdodD0iMSIvPjxyZWN0IHg9IjUiIHk9IjIiIGNsYXNzPSJzdDAiIHdpZHRoPSIxIiBoZWlnaHQ9IjciLz48L2c+PC9zdmc+);display:inline-block}
528 .extbase-debugger-tree input:checked~.extbase-debug-content{display:inline}
529 .extbase-debugger-tree input:checked~.extbase-debug-header:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTIgMTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEyIDEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHN0eWxlIHR5cGU9InRleHQvY3NzIj4uc3Qwe2ZpbGw6Izg4ODg4ODt9PC9zdHlsZT48cGF0aCBpZD0iQm9yZGVyIiBjbGFzcz0ic3QwIiBkPSJNMTEsMTFIMFYwaDExVjExeiBNMTAsMUgxdjloOVYxeiIvPjxnIGlkPSJJbm5lciI+PHJlY3QgeD0iMiIgeT0iNSIgY2xhc3M9InN0MCIgd2lkdGg9IjciIGhlaWdodD0iMSIvPjwvZz48L3N2Zz4=)}
530 .extbase-debugger{display:block;text-align:left;background:#2a2a2a;border:1px solid #2a2a2a;box-shadow:0 3px 0 rgba(0,0,0,.5);color:#000;margin:20px;overflow:hidden;border-radius:4px}
531 .extbase-debugger-floating{position:relative;z-index:999}
532 .extbase-debugger-top{background:#444;font-size:12px;font-family:monospace;color:#f1f1f1;padding:6px 15px}
533 .extbase-debugger-center{padding:0 15px;margin:15px 0;background-image:repeating-linear-gradient(to bottom,transparent 0,transparent 20px,#252525 20px,#252525 40px)}
534 .extbase-debugger-center,.extbase-debugger-center .extbase-debug-string,.extbase-debugger-center a,.extbase-debugger-center p,.extbase-debugger-center pre,.extbase-debugger-center strong{font-size:12px;font-weight:400;font-family:monospace;line-height:20px;color:#f1f1f1}
535 .extbase-debugger-center pre{background-color:transparent;margin:0;padding:0;border:0;word-wrap:break-word;color:#999}
536 .extbase-debugger-center .extbase-debug-string{color:#ce9178;white-space:normal}
537 .extbase-debugger-center .extbase-debug-type{color:#569CD6;padding-right:4px}
538 .extbase-debugger-center .extbase-debug-unregistered{background-color:#dce1e8}
539 .extbase-debugger-center .extbase-debug-filtered,.extbase-debugger-center .extbase-debug-proxy,.extbase-debugger-center .extbase-debug-ptype,.extbase-debugger-center .extbase-debug-visibility,.extbase-debugger-center .extbase-debug-scope{color:#fff;font-size:10px;line-height:12px;padding:2px 4px;margin-right:2px;position:relative;top:-1px}
540 .extbase-debugger-center .extbase-debug-scope{background-color:#497AA2}
541 .extbase-debugger-center .extbase-debug-ptype{background-color:#698747}
542 .extbase-debugger-center .extbase-debug-visibility{background-color:#698747}
543 .extbase-debugger-center .extbase-debug-dirty{background-color:#FFFFB6}
544 .extbase-debugger-center .extbase-debug-filtered{background-color:#4F4F4F}
545 .extbase-debugger-center .extbase-debug-seeabove{text-decoration:none;font-style:italic}
546 .extbase-debugger-center .extbase-debug-property{color:#f1f1f1}
547 .extbase-debugger-center .extbase-debug-closure{color:#9BA223;}
548 </style>';
549 self::$stylesheetEchoed = true;
550 }
551 if ($plainText) {
552 $output = $title . PHP_EOL . self::renderDump($variable, 0, true, $ansiColors) . PHP_EOL . PHP_EOL;
553 } else {
554 $output = '
555 <div class="extbase-debugger ' . ($return ? 'extbase-debugger-inline' : 'extbase-debugger-floating') . '">
556 <div class="extbase-debugger-top">' . htmlspecialchars($title) . '</div>
557 <div class="extbase-debugger-center">
558 <pre dir="ltr">' . self::renderDump($variable, 0, false, false) . '</pre>
559 </div>
560 </div>
561 ';
562 }
563 self::$blacklistedClassNames = $backupBlacklistedClassNames;
564 self::$blacklistedPropertyNames = $backupBlacklistedPropertyNames;
565 if ($return === true) {
566 return $css . $output;
567 }
568 echo $css . $output;
569
570 return '';
571 }
572 }