DebuggerUtility::var_dump()   D
last analyzed

Complexity

Conditions 11
Paths 384

Size

Total Lines 78
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 30
nc 384
nop 8
dl 0
loc 78
rs 4.1833
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Extbase\Utility;
19
20
use TYPO3\CMS\Core\SingletonInterface;
21
use TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject;
22
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
23
use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
24
use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
25
use TYPO3\CMS\Extbase\Object\ObjectManager;
26
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;
27
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
28
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
29
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory;
30
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
31
use TYPO3\CMS\Extbase\Reflection\ReflectionService;
32
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
33
34
/**
35
 * This class is a backport of the corresponding class of TYPO3 Flow.
36
 * All credits go to the TYPO3 Flow team.
37
 *
38
 * A debugging utility class
39
 */
40
class DebuggerUtility
41
{
42
    const PLAINTEXT_INDENT = '   ';
43
    const HTML_INDENT = '&nbsp;&nbsp;&nbsp;';
44
45
    /**
46
     * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
47
     */
48
    protected static $renderedObjects;
49
50
    /**
51
     * Hardcoded list of Extbase class names (regex) which should not be displayed during debugging
52
     *
53
     * @var array
54
     */
55
    protected static $blacklistedClassNames = [
56
        'PHPUnit_Framework_MockObject_InvocationMocker',
57
        ReflectionService::class,
58
        // @deprecated since v11, will be removed in v12.
59
        ObjectManager::class,
60
        DataMapper::class,
61
        PersistenceManager::class,
62
        QueryObjectModelFactory::class,
63
        ContentObjectRenderer::class
64
    ];
65
66
    /**
67
     * Hardcoded list of property names (regex) which should not be displayed during debugging
68
     *
69
     * @var array
70
     */
71
    protected static $blacklistedPropertyNames = ['warning'];
72
73
    /**
74
     * Is set to TRUE once the CSS file is included in the current page to prevent double inclusions of the CSS file.
75
     *
76
     * @var bool
77
     */
78
    protected static $stylesheetEchoed = false;
79
80
    /**
81
     * Defines the max recursion depth of the dump, set to 8 due to common memory limits
82
     *
83
     * @var int
84
     */
85
    protected static $maxDepth = 8;
86
87
    /**
88
     * Clear the state of the debugger
89
     */
90
    protected static function clearState(): void
91
    {
92
        self::$renderedObjects = new ObjectStorage();
93
    }
94
95
    /**
96
     * Renders a dump of the given value
97
     *
98
     * @param mixed $value
99
     * @param int $level
100
     * @param bool $plainText
101
     * @param bool $ansiColors
102
     * @return string
103
     */
104
    protected static function renderDump($value, int $level, bool $plainText, bool $ansiColors): string
105
    {
106
        $dump = '';
107
        if (is_string($value)) {
108
            $croppedValue = strlen($value) > 2000 ? substr($value, 0, 2000) . '...' : $value;
109
            if ($plainText) {
110
                $dump = self::ansiEscapeWrap('"' . implode(PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level + 1), str_split($croppedValue, 76)) . '"', '33', $ansiColors) . ' (' . strlen($value) . ' chars)';
0 ignored issues
show
Bug introduced by
It seems like str_split($croppedValue, 76) can also be of type true; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

110
                $dump = self::ansiEscapeWrap('"' . implode(PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level + 1), /** @scrutinizer ignore-type */ str_split($croppedValue, 76)) . '"', '33', $ansiColors) . ' (' . strlen($value) . ' chars)';
Loading history...
111
            } else {
112
                $lines = str_split($croppedValue, 76);
113
                $lines = array_map(static function (string $line): string {
114
                    return htmlspecialchars($line, ENT_COMPAT);
115
                }, $lines);
0 ignored issues
show
Bug introduced by
It seems like $lines can also be of type true; however, parameter $array of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

115
                }, /** @scrutinizer ignore-type */ $lines);
Loading history...
116
                $dump = sprintf('\'<span class="extbase-debug-string">%s</span>\' (%s chars)', implode('<br />' . str_repeat(self::HTML_INDENT, $level + 1), $lines), strlen($value));
117
            }
118
        } elseif (is_numeric($value)) {
119
            $dump = sprintf('%s (%s)', self::ansiEscapeWrap((string)$value, '35', $ansiColors), gettype($value));
120
        } elseif (is_bool($value)) {
121
            $dump = $value ? self::ansiEscapeWrap('TRUE', '32', $ansiColors) : self::ansiEscapeWrap('FALSE', '32', $ansiColors);
122
        } elseif ($value === null || is_resource($value)) {
123
            $dump = gettype($value);
124
        } elseif (is_array($value)) {
125
            $dump = self::renderArray($value, $level + 1, $plainText, $ansiColors);
126
        } elseif (is_object($value)) {
127
            if ($value instanceof \Closure) {
128
                $dump = self::renderClosure($value, $level + 1, $plainText, $ansiColors);
129
            } else {
130
                $dump = self::renderObject($value, $level + 1, $plainText, $ansiColors);
131
            }
132
        }
133
        return $dump;
134
    }
135
136
    /**
137
     * Renders a dump of the given array
138
     *
139
     * @param array $array
140
     * @param int $level
141
     * @param bool $plainText
142
     * @param bool $ansiColors
143
     * @return string
144
     */
145
    protected static function renderArray(array $array, int $level, bool $plainText = false, bool $ansiColors = false): string
146
    {
147
        $content = '';
148
        $count = count($array);
149
150
        if ($plainText) {
151
            $header = self::ansiEscapeWrap('array', '36', $ansiColors);
152
        } else {
153
            $header = '<span class="extbase-debug-type">array</span>';
154
        }
155
        $header .= $count > 0 ? '(' . $count . ' item' . ($count > 1 ? 's' : '') . ')' : '(empty)';
156
        if ($level >= self::$maxDepth) {
157
            if ($plainText) {
158
                $header .= ' ' . self::ansiEscapeWrap('max depth', '47;30', $ansiColors);
159
            } else {
160
                $header .= '<span class="extbase-debug-filtered">max depth</span>';
161
            }
162
        } else {
163
            $content = self::renderCollection($array, $level, $plainText, $ansiColors);
164
            if (!$plainText) {
165
                $header = ($level > 1 && $count > 0 ? '<input type="checkbox" /><span class="extbase-debug-header" >' : '<span>') . $header . '</span >';
166
            }
167
        }
168
        if ($level > 1 && $count > 0 && !$plainText) {
169
            $dump = '<span class="extbase-debugger-tree">' . $header . '<span class="extbase-debug-content">' . $content . '</span></span>';
170
        } else {
171
            $dump = $header . $content;
172
        }
173
        return $dump;
174
    }
175
176
    /**
177
     * Renders a dump of the given object
178
     *
179
     * @param object $object
180
     * @param int $level
181
     * @param bool $plainText
182
     * @param bool $ansiColors
183
     * @return string
184
     */
185
    protected static function renderObject(object $object, int $level, bool $plainText = false, bool $ansiColors = false): string
186
    {
187
        if ($object instanceof LazyLoadingProxy) {
188
            $object = $object->_loadRealInstance();
189
            if (!is_object($object)) {
190
                return gettype($object);
191
            }
192
        }
193
        $header = self::renderHeader($object, $level, $plainText, $ansiColors);
194
        if ($level < self::$maxDepth && !self::isBlacklisted($object) && !(self::isAlreadyRendered($object) && $plainText !== true)) {
195
            $content = self::renderContent($object, $level, $plainText, $ansiColors);
196
        } else {
197
            $content = '';
198
        }
199
        if ($plainText) {
200
            return $header . $content;
201
        }
202
        return '<span class="extbase-debugger-tree">' . $header . '<span class="extbase-debug-content">' . $content . '</span></span>';
203
    }
204
205
    /**
206
     * Renders a dump of the given closure
207
     *
208
     * @param \Closure $object
209
     * @param int $level
210
     * @param bool $plainText
211
     * @param bool $ansiColors
212
     * @return string
213
     */
214
    protected static function renderClosure(\Closure $object, int $level, bool $plainText = false, bool $ansiColors = false): string
215
    {
216
        $header = self::renderHeader($object, $level, $plainText, $ansiColors);
217
        if ($level < self::$maxDepth && (!self::isAlreadyRendered($object) || $plainText)) {
218
            $content = self::renderContent($object, $level, $plainText, $ansiColors);
219
        } else {
220
            $content = '';
221
        }
222
        if ($plainText) {
223
            return $header . $content;
224
        }
225
        return '<span class="extbase-debugger-tree"><input type="checkbox" /><span class="extbase-debug-header">' . $header . '</span><span class="extbase-debug-content">' . $content . '</span></span>';
226
    }
227
228
    /**
229
     * Checks if a given object or property should be excluded/filtered
230
     *
231
     * @param object $value A ReflectionProperty or other Object
232
     * @return bool TRUE if the given object should be filtered
233
     */
234
    protected static function isBlacklisted(object $value): bool
235
    {
236
        if ($value instanceof \ReflectionProperty) {
237
            $result = in_array($value->getName(), self::$blacklistedPropertyNames, true);
238
        } else {
239
            $result = in_array(get_class($value), self::$blacklistedClassNames, true);
240
        }
241
        return $result;
242
    }
243
244
    /**
245
     * Checks if a given object was already rendered.
246
     *
247
     * @param object $object
248
     * @return bool TRUE if the given object was already rendered
249
     */
250
    protected static function isAlreadyRendered(object $object): bool
251
    {
252
        return self::$renderedObjects->contains($object);
253
    }
254
255
    /**
256
     * Renders the header of a given object/collection. It is usually the class name along with some flags.
257
     *
258
     * @param object $object
259
     * @param int $level
260
     * @param bool $plainText
261
     * @param bool $ansiColors
262
     * @return string The rendered header with tags
263
     */
264
    protected static function renderHeader(object $object, int $level, bool $plainText, bool $ansiColors): string
265
    {
266
        $dump = '';
267
        $persistenceType = null;
268
        $className = get_class($object);
269
        $classReflection = new \ReflectionClass($className);
270
        if ($plainText) {
271
            $dump .= self::ansiEscapeWrap($className, '36', $ansiColors);
272
        } else {
273
            $dump .= '<span class="extbase-debug-type">' . htmlspecialchars($className, ENT_COMPAT) . '</span>';
274
        }
275
        if (!$object instanceof \Closure) {
276
            if ($object instanceof SingletonInterface) {
277
                $scope = 'singleton';
278
            } else {
279
                $scope = 'prototype';
280
            }
281
            if ($plainText) {
282
                $dump .= ' ' . self::ansiEscapeWrap($scope, '44;37', $ansiColors);
283
            } else {
284
                $dump .= '<span class="extbase-debug-scope">' . $scope . '</span>';
285
            }
286
            if ($object instanceof AbstractDomainObject) {
287
                if ($object->_isDirty()) {
288
                    $persistenceType = 'modified';
289
                } elseif ($object->_isNew()) {
290
                    $persistenceType = 'transient';
291
                } else {
292
                    $persistenceType = 'persistent';
293
                }
294
            }
295
            if ($object instanceof ObjectStorage && $object->_isDirty()) {
296
                $persistenceType = 'modified';
297
            }
298
            if ($object instanceof AbstractEntity) {
299
                $domainObjectType = 'entity';
300
            } elseif ($object instanceof AbstractValueObject) {
301
                $domainObjectType = 'valueobject';
302
            } else {
303
                $domainObjectType = 'object';
304
            }
305
            $persistenceType = $persistenceType === null ? '' : $persistenceType . ' ';
306
            if ($plainText) {
307
                $dump .= ' ' . self::ansiEscapeWrap($persistenceType . $domainObjectType, '42;30', $ansiColors);
308
            } else {
309
                $dump .= '<span class="extbase-debug-ptype">' . $persistenceType . $domainObjectType . '</span>';
310
            }
311
        }
312
        if (strpos(implode('|', self::$blacklistedClassNames), get_class($object)) > 0) {
313
            if ($plainText) {
314
                $dump .= ' ' . self::ansiEscapeWrap('filtered', '47;30', $ansiColors);
315
            } else {
316
                $dump .= '<span class="extbase-debug-filtered">filtered</span>';
317
            }
318
        } elseif (self::$renderedObjects->contains($object) && !$plainText) {
319
            $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>';
320
        } elseif ($level >= self::$maxDepth && !$object instanceof \DateTimeInterface) {
321
            if ($plainText) {
322
                $dump .= ' ' . self::ansiEscapeWrap('max depth', '47;30', $ansiColors);
323
            } else {
324
                $dump .= '<span class="extbase-debug-filtered">max depth</span>';
325
            }
326
        } elseif ($level > 1 && !$object instanceof \DateTimeInterface && !$plainText) {
327
            if (($object instanceof \Countable && empty($object)) || empty($classReflection->getProperties())) {
328
                $dump = '<span>' . $dump . '</span>';
329
            } else {
330
                $dump = '<input type="checkbox" id="' . spl_object_hash($object) . '" /><span class="extbase-debug-header">' . $dump . '</span>';
331
            }
332
        }
333
        if ($object instanceof \Countable) {
334
            $objectCount = count($object);
335
            $dump .= $objectCount > 0 ? ' (' . $objectCount . ' items)' : ' (empty)';
336
        }
337
        if ($object instanceof \DateTimeInterface) {
338
            $dump .= ' (' . $object->format(\DateTimeInterface::RFC3339) . ', ' . $object->getTimestamp() . ')';
339
        }
340
        if ($object instanceof DomainObjectInterface && !$object->_isNew()) {
341
            $dump .= ' (uid=' . $object->getUid() . ', pid=' . $object->getPid() . ')';
342
        }
343
        return $dump;
344
    }
345
346
    /**
347
     * @param object $object
348
     * @param int $level
349
     * @param bool $plainText
350
     * @param bool $ansiColors
351
     * @return string The rendered body content of the Object(Storage)
352
     */
353
    protected static function renderContent(object $object, int $level, bool $plainText, bool $ansiColors): string
354
    {
355
        $dump = '';
356
        if ($object instanceof \Iterator || $object instanceof \ArrayObject) {
357
            $dump .= self::renderCollection($object, $level, $plainText, $ansiColors);
358
        } else {
359
            self::$renderedObjects->attach($object);
360
            if (!$plainText) {
361
                $dump .= '<a name="' . spl_object_hash($object) . '" id="' . spl_object_hash($object) . '"></a>';
362
            }
363
            if ($object instanceof \Closure) {
364
                $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level)
365
                    . ($plainText ? '' : '<span class="extbase-debug-closure">')
366
                    . self::ansiEscapeWrap('function (', '33', $ansiColors) . ($plainText ? '' : '</span>');
367
368
                $reflectionFunction = new \ReflectionFunction($object);
369
                $params = [];
370
                foreach ($reflectionFunction->getParameters() as $parameter) {
371
                    $parameterDump = '';
372
                    if ($parameter->isArray()) {
373
                        if ($plainText) {
374
                            $parameterDump .= self::ansiEscapeWrap('array ', '36', $ansiColors);
375
                        } else {
376
                            $parameterDump .= '<span class="extbase-debug-type">array </span>';
377
                        }
378
                    } elseif ($parameter->getClass() instanceof \ReflectionClass) {
379
                        if ($plainText) {
380
                            $parameterDump .= self::ansiEscapeWrap($parameter->getClass()->name . ' ', '36', $ansiColors);
381
                        } else {
382
                            $parameterDump .= '<span class="extbase-debug-type">'
383
                                . htmlspecialchars($parameter->getClass()->name, ENT_COMPAT) . '</span>';
384
                        }
385
                    }
386
                    if ($parameter->isPassedByReference()) {
387
                        $parameterDump .= '&';
388
                    }
389
                    if ($parameter->isVariadic()) {
390
                        $parameterDump .= '...';
391
                    }
392
                    if ($plainText) {
393
                        $parameterDump .= self::ansiEscapeWrap('$' . $parameter->name, '37', $ansiColors);
394
                    } else {
395
                        $parameterDump .= '<span class="extbase-debug-property">'
396
                            . htmlspecialchars('$' . $parameter->name, ENT_COMPAT) . '</span>';
397
                    }
398
                    if ($parameter->isDefaultValueAvailable()) {
399
                        $parameterDump .= ' = ';
400
                        if ($plainText) {
401
                            $parameterDump .= self::ansiEscapeWrap(var_export($parameter->getDefaultValue(), true), '33', $ansiColors);
402
                        } else {
403
                            $parameterDump .= '<span class="extbase-debug-string">'
404
                                . htmlspecialchars(var_export($parameter->getDefaultValue(), true), ENT_COMPAT) . '</span>';
405
                        }
406
                    }
407
                    $params[] = $parameterDump;
408
                }
409
                $dump .= implode(', ', $params);
410
                if ($plainText) {
411
                    $dump .= self::ansiEscapeWrap(') {' . PHP_EOL, '33', $ansiColors);
412
                } else {
413
                    $dump .= '<span class="extbase-debug-closure">) {' . PHP_EOL . '</span>';
414
                }
415
                $lines = (array)file((string)$reflectionFunction->getFileName());
416
                for ($l = (int)$reflectionFunction->getStartLine(); $l < (int)$reflectionFunction->getEndLine() - 1; ++$l) {
417
                    $line = (string)($lines[$l] ?? '');
418
                    $dump .= $plainText ? $line : htmlspecialchars($line, ENT_COMPAT);
419
                }
420
                $dump .= str_repeat(self::PLAINTEXT_INDENT, $level);
421
                if ($plainText) {
422
                    $dump .= self::ansiEscapeWrap('}' . PHP_EOL, '33', $ansiColors);
423
                } else {
424
                    $dump .= '<span class="extbase-debug-closure">}</span>';
425
                }
426
            } else {
427
                if (get_class($object) === \stdClass::class) {
428
                    $objReflection = new \ReflectionObject($object);
429
                    $properties = $objReflection->getProperties();
430
                } else {
431
                    $classReflection = new \ReflectionClass(get_class($object));
432
                    $properties = $classReflection->getProperties();
433
                }
434
                foreach ($properties as $property) {
435
                    if (self::isBlacklisted($property)) {
436
                        continue;
437
                    }
438
                    $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level);
439
                    if ($plainText) {
440
                        $dump .= self::ansiEscapeWrap($property->getName(), '37', $ansiColors);
441
                    } else {
442
                        $dump .= '<span class="extbase-debug-property">'
443
                            . htmlspecialchars($property->getName(), ENT_COMPAT) . '</span>';
444
                    }
445
                    $dump .= ' => ';
446
                    $property->setAccessible(true);
447
                    $visibility = ($property->isProtected() ? 'protected' : ($property->isPrivate() ? 'private' : 'public'));
448
                    if ($plainText) {
449
                        $dump .= self::ansiEscapeWrap($visibility, '42;30', $ansiColors) . ' ';
450
                    } else {
451
                        $dump .= '<span class="extbase-debug-visibility">' . $visibility . '</span>';
452
                    }
453
                    $dump .= self::renderDump($property->getValue($object), $level, $plainText, $ansiColors);
454
                    if ($object instanceof AbstractDomainObject && !$object->_isNew() && $object->_isDirty($property->getName())) {
455
                        if ($plainText) {
456
                            $dump .= ' ' . self::ansiEscapeWrap('modified', '43;30', $ansiColors);
457
                        } else {
458
                            $dump .= '<span class="extbase-debug-dirty">modified</span>';
459
                        }
460
                    }
461
                }
462
            }
463
        }
464
        return $dump;
465
    }
466
467
    /**
468
     * @param iterable $collection
469
     * @param int $level
470
     * @param bool $plainText
471
     * @param bool $ansiColors
472
     * @return string
473
     */
474
    protected static function renderCollection(iterable $collection, int $level, bool $plainText, bool $ansiColors): string
475
    {
476
        $dump = '';
477
        foreach ($collection as $key => $value) {
478
            $key = (string)$key;
479
480
            $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level);
481
            if ($plainText) {
482
                $dump .= self::ansiEscapeWrap($key, '37', $ansiColors);
483
            } else {
484
                $dump .= '<span class="extbase-debug-property">' . htmlspecialchars($key, ENT_COMPAT) . '</span>';
485
            }
486
            $dump .= ' => ';
487
            $dump .= self::renderDump($value, $level, $plainText, $ansiColors);
488
        }
489
        if ($collection instanceof \Iterator && !$collection instanceof \Generator) {
490
            $collection->rewind();
491
        }
492
        return $dump;
493
    }
494
495
    /**
496
     * Wrap a string with the ANSI escape sequence for colorful output
497
     *
498
     * @param string $string The string to wrap
499
     * @param string $ansiColors The ansi color sequence (e.g. "1;37")
500
     * @param bool $enable If FALSE, the raw string will be returned
501
     * @return string The wrapped or raw string
502
     */
503
    protected static function ansiEscapeWrap(string $string, string $ansiColors, bool $enable = true): string
504
    {
505
        if ($enable) {
506
            return '[' . $ansiColors . 'm' . $string . '';
507
        }
508
        return $string;
509
    }
510
511
    /**
512
     * A var_dump function optimized for Extbase's object structures
513
     *
514
     * @param mixed $variable The value to dump
515
     * @param string $title optional custom title for the debug output
516
     * @param int $maxDepth Sets the max recursion depth of the dump. De- or increase the number according to your needs and memory limit.
517
     * @param bool $plainText If TRUE, the dump is in plain text, if FALSE the debug output is in HTML format.
518
     * @param bool $ansiColors If TRUE (default), ANSI color codes is added to the output, if FALSE the debug output not colored.
519
     * @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.
520
     * @param array $blacklistedClassNames An array of class names (RegEx) to be filtered. Default is an array of some common class names.
521
     * @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.
522
     * @return string if $return is TRUE, the dump is returned. By default, the dump is directly displayed, and nothing is returned.
523
     */
524
    public static function var_dump(
525
        $variable,
526
        string $title = null,
527
        int $maxDepth = 8,
528
        bool $plainText = false,
529
        bool $ansiColors = true,
530
        bool $return = false,
531
        array $blacklistedClassNames = null,
532
        array $blacklistedPropertyNames = null
533
    ): string {
534
        self::$maxDepth = $maxDepth;
535
        if ($title === null) {
536
            $title = 'Extbase Variable Dump';
537
        }
538
        $ansiColors = $plainText && $ansiColors;
539
        if ($ansiColors === true) {
540
            $title = '' . $title . '';
541
        }
542
        $backupBlacklistedClassNames = self::$blacklistedClassNames;
543
        if (is_array($blacklistedClassNames)) {
544
            self::$blacklistedClassNames = $blacklistedClassNames;
545
        }
546
        $backupBlacklistedPropertyNames = self::$blacklistedPropertyNames;
547
        if (is_array($blacklistedPropertyNames)) {
548
            self::$blacklistedPropertyNames = $blacklistedPropertyNames;
549
        }
550
        self::clearState();
551
        $css = '';
552
        if (!$plainText && self::$stylesheetEchoed === false) {
553
            $css = '
554
				<style>
555
					.extbase-debugger-tree{position:relative}
556
					.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}
557
					.extbase-debugger-tree input~.extbase-debug-content{display:none}
558
					.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}
559
					.extbase-debugger-tree input:checked~.extbase-debug-content{display:inline}
560
					.extbase-debugger-tree input:checked~.extbase-debug-header:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTIgMTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEyIDEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHN0eWxlIHR5cGU9InRleHQvY3NzIj4uc3Qwe2ZpbGw6Izg4ODg4ODt9PC9zdHlsZT48cGF0aCBpZD0iQm9yZGVyIiBjbGFzcz0ic3QwIiBkPSJNMTEsMTFIMFYwaDExVjExeiBNMTAsMUgxdjloOVYxeiIvPjxnIGlkPSJJbm5lciI+PHJlY3QgeD0iMiIgeT0iNSIgY2xhc3M9InN0MCIgd2lkdGg9IjciIGhlaWdodD0iMSIvPjwvZz48L3N2Zz4=)}
561
					.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}
562
					.extbase-debugger-floating{position:relative;z-index:99990}
563
					.extbase-debugger-top{background:#444;font-size:12px;font-family:monospace;color:#f1f1f1;padding:6px 15px}
564
					.extbase-debugger-center{padding:0 15px;margin:15px 0;background-image:repeating-linear-gradient(to bottom,transparent 0,transparent 20px,#252525 20px,#252525 40px)}
565
					.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}
566
					.extbase-debugger-center pre{background-color:transparent;margin:0;padding:0;border:0;word-wrap:break-word;color:#999}
567
					.extbase-debugger-center .extbase-debug-string{color:#ce9178;white-space:normal}
568
					.extbase-debugger-center .extbase-debug-type{color:#569CD6;padding-right:4px}
569
					.extbase-debugger-center .extbase-debug-unregistered{background-color:#dce1e8}
570
					.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}
571
					.extbase-debugger-center .extbase-debug-scope{background-color:#497AA2}
572
					.extbase-debugger-center .extbase-debug-ptype{background-color:#698747}
573
					.extbase-debugger-center .extbase-debug-visibility{background-color:#698747}
574
					.extbase-debugger-center .extbase-debug-dirty{background-color:#FFFFB6}
575
					.extbase-debugger-center .extbase-debug-filtered{background-color:#4F4F4F}
576
					.extbase-debugger-center .extbase-debug-seeabove{text-decoration:none;font-style:italic}
577
					.extbase-debugger-center .extbase-debug-property{color:#f1f1f1}
578
					.extbase-debugger-center .extbase-debug-closure{color:#9BA223;}
579
				</style>';
580
            self::$stylesheetEchoed = true;
581
        }
582
        if ($plainText) {
583
            $output = $title . PHP_EOL . self::renderDump($variable, 0, true, $ansiColors) . PHP_EOL . PHP_EOL;
584
        } else {
585
            $output = '
586
				<div class="extbase-debugger ' . ($return ? 'extbase-debugger-inline' : 'extbase-debugger-floating') . '">
587
				<div class="extbase-debugger-top">' . htmlspecialchars($title, ENT_COMPAT) . '</div>
588
				<div class="extbase-debugger-center">
589
					<pre dir="ltr">' . self::renderDump($variable, 0, false, false) . '</pre>
590
				</div>
591
			</div>
592
			';
593
        }
594
        self::$blacklistedClassNames = $backupBlacklistedClassNames;
595
        self::$blacklistedPropertyNames = $backupBlacklistedPropertyNames;
596
        if ($return === true) {
597
            return $css . $output;
598
        }
599
        echo $css . $output;
600
601
        return '';
602
    }
603
}
604