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