TYPO3 CMS  TYPO3_7-6
DebuggerUtility.php
Go to the documentation of this file.
1 <?php
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  * */
34 {
35  const PLAINTEXT_INDENT = ' ';
36  const HTML_INDENT = '&nbsp;&nbsp;&nbsp;';
37 
41  protected static $renderedObjects;
42 
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 
63  protected static $blacklistedPropertyNames = ['warning'];
64 
70  protected static $stylesheetEchoed = false;
71 
77  protected static $maxDepth = 8;
78 
84  protected static function clearState()
85  {
86  self::$renderedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
87  }
88 
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  $lines = str_split($croppedValue, 76);
107  $lines = array_map('htmlspecialchars', $lines);
108  $dump = sprintf('\'<span class="extbase-debug-string">%s</span>\' (%s chars)', implode('<br />' . str_repeat(self::HTML_INDENT, $level + 1), $lines), strlen($value));
109  }
110  } elseif (is_numeric($value)) {
111  $dump = sprintf('%s (%s)', self::ansiEscapeWrap($value, '35', $ansiColors), gettype($value));
112  } elseif (is_bool($value)) {
113  $dump = $value ? self::ansiEscapeWrap('TRUE', '32', $ansiColors) : self::ansiEscapeWrap('FALSE', '32', $ansiColors);
114  } elseif (is_null($value) || is_resource($value)) {
115  $dump = gettype($value);
116  } elseif (is_array($value)) {
117  $dump = self::renderArray($value, $level + 1, $plainText, $ansiColors);
118  } elseif (is_object($value)) {
119  $dump = self::renderObject($value, $level + 1, $plainText, $ansiColors);
120  }
121  return $dump;
122  }
123 
133  protected static function renderArray($array, $level, $plainText = false, $ansiColors = false)
134  {
135  $content = '';
136  $count = count($array);
137 
138  if ($plainText) {
139  $header = self::ansiEscapeWrap('array', '36', $ansiColors);
140  } else {
141  $header = '<span class="extbase-debug-type">array</span>';
142  }
143  $header .= $count > 0 ? '(' . $count . ' item' . ($count > 1 ? 's' : '') . ')' : '(empty)';
144  if ($level >= self::$maxDepth) {
145  if ($plainText) {
146  $header .= ' ' . self::ansiEscapeWrap('max depth', '47;30', $ansiColors);
147  } else {
148  $header .= '<span class="extbase-debug-filtered">max depth</span>';
149  }
150  } else {
151  $content = self::renderCollection($array, $level, $plainText, $ansiColors);
152  if (!$plainText) {
153  $header = ($level > 1 && $count > 0 ? '<input type="checkbox" /><span class="extbase-debug-header" >' : '<span>') . $header . '</span >';
154  }
155  }
156  if ($level > 1 && $count > 0 && !$plainText) {
157  $dump = '<span class="extbase-debugger-tree">' . $header . '<span class="extbase-debug-content">' . $content . '</span></span>';
158  } else {
159  $dump = $header . $content;
160  }
161  return $dump;
162  }
163 
173  protected static function renderObject($object, $level, $plainText = false, $ansiColors = false)
174  {
175  if ($object instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
176  $object = $object->_loadRealInstance();
177  if (!$object) {
178  return gettype($object);
179  }
180  }
181  $header = self::renderHeader($object, $level, $plainText, $ansiColors);
182  if ($level < self::$maxDepth && !self::isBlacklisted($object) && !(self::isAlreadyRendered($object) && $plainText !== true)) {
183  $content = self::renderContent($object, $level, $plainText, $ansiColors);
184  } else {
185  $content = '';
186  }
187  if ($plainText) {
188  return $header . $content;
189  } else {
190  return '<span class="extbase-debugger-tree">' . $header . '<span class="extbase-debug-content">' . $content . '</span></span>';
191  }
192  }
193 
200  protected static function isBlacklisted($value)
201  {
202  $result = false;
203  if ($value instanceof \ReflectionProperty) {
204  $result = in_array($value->getName(), self::$blacklistedPropertyNames, true);
205  } elseif (is_object($value)) {
206  $result = in_array(get_class($value), self::$blacklistedClassNames, true);
207  }
208  return $result;
209  }
210 
217  protected static function isAlreadyRendered($object)
218  {
219  return self::$renderedObjects->contains($object);
220  }
221 
231  protected static function renderHeader($object, $level, $plainText, $ansiColors)
232  {
233  $dump = '';
234  $persistenceType = '';
235  $className = get_class($object);
236  $classReflection = new \ReflectionClass($className);
237  if ($plainText) {
238  $dump .= self::ansiEscapeWrap($className, '36', $ansiColors);
239  } else {
240  $dump .= '<span class="extbase-debug-type">' . $className . '</span>';
241  }
242  if (! $object instanceof \Closure) {
243  if ($object instanceof \TYPO3\CMS\Core\SingletonInterface) {
244  $scope = 'singleton';
245  } else {
246  $scope = 'prototype';
247  }
248  if ($plainText) {
249  $dump .= ' ' . self::ansiEscapeWrap($scope, '44;37', $ansiColors);
250  } else {
251  $dump .= $scope ? '<span class="extbase-debug-scope">' . $scope . '</span>' : '';
252  }
253  if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject) {
254  if ($object->_isDirty()) {
255  $persistenceType = 'modified';
256  } elseif ($object->_isNew()) {
257  $persistenceType = 'transient';
258  } else {
259  $persistenceType = 'persistent';
260  }
261  }
262  if ($object instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage && $object->_isDirty()) {
263  $persistenceType = 'modified';
264  }
265  if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractEntity) {
266  $domainObjectType = 'entity';
267  } elseif ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject) {
268  $domainObjectType = 'valueobject';
269  } else {
270  $domainObjectType = 'object';
271  }
272  if ($plainText) {
273  $dump .= ' ' . self::ansiEscapeWrap(($persistenceType ? $persistenceType . ' ' : '') . $domainObjectType, '42;30', $ansiColors);
274  } else {
275  $dump .= '<span class="extbase-debug-ptype">' . ($persistenceType ? $persistenceType . ' ' : '') . $domainObjectType . '</span>';
276  }
277  }
278  if (strpos(implode('|', self::$blacklistedClassNames), get_class($object)) > 0) {
279  if ($plainText) {
280  $dump .= ' ' . self::ansiEscapeWrap('filtered', '47;30', $ansiColors);
281  } else {
282  $dump .= '<span class="extbase-debug-filtered">filtered</span>';
283  }
284  } elseif (self::$renderedObjects->contains($object) && !$plainText) {
285  $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>';
286  } elseif ($level >= self::$maxDepth && !$object instanceof \DateTime) {
287  if ($plainText) {
288  $dump .= ' ' . self::ansiEscapeWrap('max depth', '47;30', $ansiColors);
289  } else {
290  $dump .= '<span class="extbase-debug-filtered">max depth</span>';
291  }
292  } elseif ($level > 1 && !$object instanceof \DateTime && !$plainText) {
293  if (($object instanceof \Countable && empty($object)) || empty($classReflection->getProperties())) {
294  $dump = '<span>' . $dump . '</span>';
295  } else {
296  $dump = '<input type="checkbox" id="' . spl_object_hash($object) . '" /><span class="extbase-debug-header">' . $dump . '</span>';
297  }
298  }
299  if ($object instanceof \Countable) {
300  $objectCount = count($object);
301  $dump .= $objectCount > 0 ? ' (' . $objectCount . ' items)' : ' (empty)';
302  }
303  if ($object instanceof \DateTime) {
304  $dump .= ' (' . $object->format(\DateTime::RFC3339) . ', ' . $object->getTimestamp() . ')';
305  }
306  if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface && !$object->_isNew()) {
307  $dump .= ' (uid=' . $object->getUid() . ', pid=' . $object->getPid() . ')';
308  }
309  return $dump;
310  }
311 
319  protected static function renderContent($object, $level, $plainText, $ansiColors)
320  {
321  $dump = '';
322  if ($object instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage || $object instanceof \Iterator || $object instanceof \ArrayObject) {
323  $dump .= self::renderCollection($object, $level, $plainText, $ansiColors);
324  } else {
325  self::$renderedObjects->attach($object);
326  if (!$plainText) {
327  $dump .= '<a name="' . spl_object_hash($object) . '" id="' . spl_object_hash($object) . '"></a>';
328  }
329  if (get_class($object) === 'stdClass') {
330  $objReflection = new \ReflectionObject($object);
331  $properties = $objReflection->getProperties();
332  } else {
333  $classReflection = new \ReflectionClass(get_class($object));
334  $properties = $classReflection->getProperties();
335  }
336  foreach ($properties as $property) {
337  if (self::isBlacklisted($property)) {
338  continue;
339  }
340  $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level);
341  if ($plainText) {
342  $dump .= self::ansiEscapeWrap($property->getName(), '37', $ansiColors);
343  } else {
344  $dump .= '<span class="extbase-debug-property">' .
345  htmlspecialchars($property->getName()) . '</span>';
346  }
347  $dump .= ' => ';
348  $property->setAccessible(true);
349  $dump .= self::renderDump($property->getValue($object), $level, $plainText, $ansiColors);
350  if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject && !$object->_isNew() && $object->_isDirty($property->getName())) {
351  if ($plainText) {
352  $dump .= ' ' . self::ansiEscapeWrap('modified', '43;30', $ansiColors);
353  } else {
354  $dump .= '<span class="extbase-debug-dirty">modified</span>';
355  }
356  }
357  }
358  }
359  return $dump;
360  }
361 
369  protected static function renderCollection($collection, $level, $plainText, $ansiColors)
370  {
371  $dump = '';
372  foreach ($collection as $key => $value) {
373  $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level) . ($plainText ? '' : '<span class="extbase-debug-property">') . self::ansiEscapeWrap($key, '37', $ansiColors) . ($plainText ? '' : '</span>') . ' => ';
374  $dump .= self::renderDump($value, $level, $plainText, $ansiColors);
375  }
376  if ($collection instanceof \Iterator) {
377  $collection->rewind();
378  }
379  return $dump;
380  }
381 
390  protected static function ansiEscapeWrap($string, $ansiColors, $enable = true)
391  {
392  if ($enable) {
393  return '[' . $ansiColors . 'm' . $string . '';
394  } else {
395  return $string;
396  }
397  }
398 
413  public static function var_dump($variable, $title = null, $maxDepth = 8, $plainText = false, $ansiColors = true, $return = false, $blacklistedClassNames = null, $blacklistedPropertyNames = null)
414  {
415  self::$maxDepth = $maxDepth;
416  if ($title === null) {
417  $title = 'Extbase Variable Dump';
418  }
419  $ansiColors = $plainText && $ansiColors;
420  if ($ansiColors === true) {
421  $title = '' . $title . '';
422  }
423  $backupBlacklistedClassNames = self::$blacklistedClassNames;
424  if (is_array($blacklistedClassNames)) {
425  self::$blacklistedClassNames = $blacklistedClassNames;
426  }
427  $backupBlacklistedPropertyNames = self::$blacklistedPropertyNames;
428  if (is_array($blacklistedPropertyNames)) {
429  self::$blacklistedPropertyNames = $blacklistedPropertyNames;
430  }
431  self::clearState();
432  $css = '';
433  if (!$plainText && self::$stylesheetEchoed === false) {
434  $css = '
435  <style type=\'text/css\'>
436  .extbase-debugger-tree{position:relative}
437  .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}
438  .extbase-debugger-tree input~.extbase-debug-content{display:none}
439  .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}
440  .extbase-debugger-tree input:checked~.extbase-debug-content{display:inline}
441  .extbase-debugger-tree input:checked~.extbase-debug-header:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTIgMTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEyIDEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHN0eWxlIHR5cGU9InRleHQvY3NzIj4uc3Qwe2ZpbGw6Izg4ODg4ODt9PC9zdHlsZT48cGF0aCBpZD0iQm9yZGVyIiBjbGFzcz0ic3QwIiBkPSJNMTEsMTFIMFYwaDExVjExeiBNMTAsMUgxdjloOVYxeiIvPjxnIGlkPSJJbm5lciI+PHJlY3QgeD0iMiIgeT0iNSIgY2xhc3M9InN0MCIgd2lkdGg9IjciIGhlaWdodD0iMSIvPjwvZz48L3N2Zz4=)}
442  .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}
443  .extbase-debugger-floating{position:relative;z-index:999}
444  .extbase-debugger-top{background:#444;font-size:12px;font-family:monospace;color:#f1f1f1;padding:6px 15px}
445  .extbase-debugger-center{padding:0 15px;margin:15px 0;background-image:repeating-linear-gradient(to bottom,transparent 0,transparent 20px,#252525 20px,#252525 40px)}
446  .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}
447  .extbase-debugger-center pre{background-color:transparent;margin:0;padding:0;border:0;word-wrap:break-word;color:#999}
448  .extbase-debugger-center .extbase-debug-string{color:#ce9178;white-space:normal}
449  .extbase-debugger-center .extbase-debug-type{color:#569CD6;padding-right:4px}
450  .extbase-debugger-center .extbase-debug-unregistered{background-color:#dce1e8}
451  .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}
452  .extbase-debugger-center .extbase-debug-scope{background-color:#497AA2}
453  .extbase-debugger-center .extbase-debug-ptype{background-color:#698747}
454  .extbase-debugger-center .extbase-debug-dirty{background-color:#FFFFB6}
455  .extbase-debugger-center .extbase-debug-filtered{background-color:#4F4F4F}
456  .extbase-debugger-center .extbase-debug-seeabove{text-decoration:none;font-style:italic}
457  .extbase-debugger-center .extbase-debug-property{color:#f1f1f1}
458  </style>';
459  self::$stylesheetEchoed = true;
460  }
461  if ($plainText) {
462  $output = $title . PHP_EOL . self::renderDump($variable, 0, true, $ansiColors) . PHP_EOL . PHP_EOL;
463  } else {
464  $output = '
465  <div class="extbase-debugger ' . ($return ? 'extbase-debugger-inline' : 'extbase-debugger-floating') . '">
466  <div class="extbase-debugger-top">' . htmlspecialchars($title) . '</div>
467  <div class="extbase-debugger-center">
468  <pre dir="ltr">' . self::renderDump($variable, 0, false, false) . '</pre>
469  </div>
470  </div>
471  ';
472  }
473  self::$blacklistedClassNames = $backupBlacklistedClassNames;
474  self::$blacklistedPropertyNames = $backupBlacklistedPropertyNames;
475  if ($return === true) {
476  return $css . $output;
477  } else {
478  echo $css . $output;
479  }
480  return '';
481  }
482 }
debug($variable='', $name=' *variable *', $line=' *line *', $file=' *file *', $recursiveDepth=3, $debugLevel='E_DEBUG')
static renderHeader($object, $level, $plainText, $ansiColors)
static renderArray($array, $level, $plainText=false, $ansiColors=false)
static renderDump($value, $level, $plainText, $ansiColors)
static renderObject($object, $level, $plainText=false, $ansiColors=false)