2000 ? substr($value, 0, 2000) . '...' : $value; if ($plainText) { $dump = self::ansiEscapeWrap('"' . implode(PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level + 1), str_split($croppedValue, 76)) . '"', '33', $ansiColors) . ' (' . strlen($value) . ' chars)'; } else { $lines = str_split($croppedValue, 76); $lines = array_map('htmlspecialchars', $lines); $dump = sprintf('\'%s\' (%s chars)', implode('
' . str_repeat(self::HTML_INDENT, $level + 1), $lines), strlen($value)); } } elseif (is_numeric($value)) { $dump = sprintf('%s (%s)', self::ansiEscapeWrap($value, '35', $ansiColors), gettype($value)); } elseif (is_bool($value)) { $dump = $value ? self::ansiEscapeWrap('TRUE', '32', $ansiColors) : self::ansiEscapeWrap('FALSE', '32', $ansiColors); } elseif ($value === null || is_resource($value)) { $dump = gettype($value); } elseif (is_array($value)) { $dump = self::renderArray($value, $level + 1, $plainText, $ansiColors); } elseif (is_object($value)) { if ($value instanceof \Closure) { $dump = self::renderClosure($value, $level + 1, $plainText, $ansiColors); } else { $dump = self::renderObject($value, $level + 1, $plainText, $ansiColors); } } return $dump; } /** * Renders a dump of the given array * * @param array|\Traversable $array * @param int $level * @param bool $plainText * @param bool $ansiColors * @return string */ protected static function renderArray($array, $level, $plainText = false, $ansiColors = false) { $content = ''; $count = count($array); if ($plainText) { $header = self::ansiEscapeWrap('array', '36', $ansiColors); } else { $header = 'array'; } $header .= $count > 0 ? '(' . $count . ' item' . ($count > 1 ? 's' : '') . ')' : '(empty)'; if ($level >= self::$maxDepth) { if ($plainText) { $header .= ' ' . self::ansiEscapeWrap('max depth', '47;30', $ansiColors); } else { $header .= 'max depth'; } } else { $content = self::renderCollection($array, $level, $plainText, $ansiColors); if (!$plainText) { $header = ($level > 1 && $count > 0 ? '' : '') . $header . ''; } } if ($level > 1 && $count > 0 && !$plainText) { $dump = '' . $header . '' . $content . ''; } else { $dump = $header . $content; } return $dump; } /** * Renders a dump of the given object * * @param object $object * @param int $level * @param bool $plainText * @param bool $ansiColors * @return string */ protected static function renderObject($object, $level, $plainText = false, $ansiColors = false) { if ($object instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) { $object = $object->_loadRealInstance(); if (!$object) { return gettype($object); } } $header = self::renderHeader($object, $level, $plainText, $ansiColors); if ($level < self::$maxDepth && !self::isBlacklisted($object) && !(self::isAlreadyRendered($object) && $plainText !== true)) { $content = self::renderContent($object, $level, $plainText, $ansiColors); } else { $content = ''; } if ($plainText) { return $header . $content; } return '' . $header . '' . $content . ''; } /** * Renders a dump of the given closure * * @param \Closure $object * @param int $level * @param bool $plainText * @param bool $ansiColors * @return string */ protected static function renderClosure($object, $level, $plainText = false, $ansiColors = false) { $header = self::renderHeader($object, $level, $plainText, $ansiColors); if ($level < self::$maxDepth && (!self::isAlreadyRendered($object) || $plainText)) { $content = self::renderContent($object, $level, $plainText, $ansiColors); } else { $content = ''; } if ($plainText) { return $header . $content; } return '' . $header . '' . $content . ''; } /** * Checks if a given object or property should be excluded/filtered * * @param object $value An ReflectionProperty or other Object * @return bool TRUE if the given object should be filtered */ protected static function isBlacklisted($value) { $result = false; if ($value instanceof \ReflectionProperty) { $result = in_array($value->getName(), self::$blacklistedPropertyNames, true); } elseif (is_object($value)) { $result = in_array(get_class($value), self::$blacklistedClassNames, true); } return $result; } /** * Checks if a given object was already rendered. * * @param object $object * @return bool TRUE if the given object was already rendered */ protected static function isAlreadyRendered($object) { return self::$renderedObjects->contains($object); } /** * Renders the header of a given object/collection. It is usually the class name along with some flags. * * @param object $object * @param int $level * @param bool $plainText * @param bool $ansiColors * @return string The rendered header with tags */ protected static function renderHeader($object, $level, $plainText, $ansiColors) { $dump = ''; $persistenceType = ''; $className = get_class($object); $classReflection = new \ReflectionClass($className); if ($plainText) { $dump .= self::ansiEscapeWrap($className, '36', $ansiColors); } else { $dump .= '' . htmlspecialchars($className) . ''; } if (!$object instanceof \Closure) { if ($object instanceof \TYPO3\CMS\Core\SingletonInterface) { $scope = 'singleton'; } else { $scope = 'prototype'; } if ($plainText) { $dump .= ' ' . self::ansiEscapeWrap($scope, '44;37', $ansiColors); } else { $dump .= $scope ? '' . $scope . '' : ''; } if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject) { if ($object->_isDirty()) { $persistenceType = 'modified'; } elseif ($object->_isNew()) { $persistenceType = 'transient'; } else { $persistenceType = 'persistent'; } } if ($object instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage && $object->_isDirty()) { $persistenceType = 'modified'; } if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractEntity) { $domainObjectType = 'entity'; } elseif ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject) { $domainObjectType = 'valueobject'; } else { $domainObjectType = 'object'; } if ($plainText) { $dump .= ' ' . self::ansiEscapeWrap(($persistenceType ? $persistenceType . ' ' : '') . $domainObjectType, '42;30', $ansiColors); } else { $dump .= '' . ($persistenceType ? $persistenceType . ' ' : '') . $domainObjectType . ''; } } if (strpos(implode('|', self::$blacklistedClassNames), get_class($object)) > 0) { if ($plainText) { $dump .= ' ' . self::ansiEscapeWrap('filtered', '47;30', $ansiColors); } else { $dump .= 'filtered'; } } elseif (self::$renderedObjects->contains($object) && !$plainText) { $dump = '' . $dump . 'see above'; } elseif ($level >= self::$maxDepth && !$object instanceof \DateTimeInterface) { if ($plainText) { $dump .= ' ' . self::ansiEscapeWrap('max depth', '47;30', $ansiColors); } else { $dump .= 'max depth'; } } elseif ($level > 1 && !$object instanceof \DateTimeInterface && !$plainText) { if (($object instanceof \Countable && empty($object)) || empty($classReflection->getProperties())) { $dump = '' . $dump . ''; } else { $dump = '' . $dump . ''; } } if ($object instanceof \Countable) { $objectCount = count($object); $dump .= $objectCount > 0 ? ' (' . $objectCount . ' items)' : ' (empty)'; } if ($object instanceof \DateTimeInterface) { $dump .= ' (' . $object->format(\DateTimeInterface::RFC3339) . ', ' . $object->getTimestamp() . ')'; } if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface && !$object->_isNew()) { $dump .= ' (uid=' . $object->getUid() . ', pid=' . $object->getPid() . ')'; } return $dump; } /** * @param object $object * @param int $level * @param bool $plainText * @param bool $ansiColors * @return string The rendered body content of the Object(Storage) */ protected static function renderContent($object, $level, $plainText, $ansiColors) { $dump = ''; if ($object instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage || $object instanceof \Iterator || $object instanceof \ArrayObject) { $dump .= self::renderCollection($object, $level, $plainText, $ansiColors); } else { self::$renderedObjects->attach($object); if (!$plainText) { $dump .= ''; } if ($object instanceof \Closure) { $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level) . ($plainText ? '' : '') . self::ansiEscapeWrap('function (', '33', $ansiColors) . ($plainText ? '' : ''); $reflectionFunction = new \ReflectionFunction($object); $params = []; foreach ($reflectionFunction->getParameters() as $parameter) { $parameterDump = ''; if ($parameter->isArray()) { if ($plainText) { $parameterDump .= self::ansiEscapeWrap('array ', '36', $ansiColors); } else { $parameterDump .= 'array '; } } elseif ($parameter->getClass()) { if ($plainText) { $parameterDump .= self::ansiEscapeWrap($parameter->getClass()->name . ' ', '36', $ansiColors); } else { $parameterDump .= '' . htmlspecialchars($parameter->getClass()->name) . ''; } } if ($parameter->isPassedByReference()) { $parameterDump .= '&'; } if ($parameter->isVariadic()) { $parameterDump .= '...'; } if ($plainText) { $parameterDump .= self::ansiEscapeWrap('$' . $parameter->name, '37', $ansiColors); } else { $parameterDump .= '' . htmlspecialchars('$' . $parameter->name) . ''; } if ($parameter->isDefaultValueAvailable()) { $parameterDump .= ' = '; if ($plainText) { $parameterDump .= self::ansiEscapeWrap(var_export($parameter->getDefaultValue(), true), '33', $ansiColors); } else { $parameterDump .= '' . htmlspecialchars(var_export($parameter->getDefaultValue(), true)) . ''; } } $params[] = $parameterDump; } $dump .= implode(', ', $params); if ($plainText) { $dump .= self::ansiEscapeWrap(') {' . PHP_EOL, '33', $ansiColors); } else { $dump .= ') {' . PHP_EOL . ''; } $lines = file($reflectionFunction->getFileName()); for ($l = $reflectionFunction->getStartLine(); $l < $reflectionFunction->getEndLine() - 1; ++$l) { $dump .= $plainText ? $lines[$l] : htmlspecialchars($lines[$l]); } $dump .= str_repeat(self::PLAINTEXT_INDENT, $level); if ($plainText) { $dump .= self::ansiEscapeWrap('}' . PHP_EOL, '33', $ansiColors); } else { $dump .= '}'; } } else { if (get_class($object) === 'stdClass') { $objReflection = new \ReflectionObject($object); $properties = $objReflection->getProperties(); } else { $classReflection = new \ReflectionClass(get_class($object)); $properties = $classReflection->getProperties(); } foreach ($properties as $property) { if (self::isBlacklisted($property)) { continue; } $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level); if ($plainText) { $dump .= self::ansiEscapeWrap($property->getName(), '37', $ansiColors); } else { $dump .= '' . htmlspecialchars($property->getName()) . ''; } $dump .= ' => '; $property->setAccessible(true); $visibility = ($property->isProtected() ? 'protected' : ($property->isPrivate() ? 'private' : 'public')); if ($plainText) { $dump .= self::ansiEscapeWrap($visibility, '42;30', $ansiColors) . ' '; } else { $dump .= '' . $visibility . ''; } $dump .= self::renderDump($property->getValue($object), $level, $plainText, $ansiColors); if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject && !$object->_isNew() && $object->_isDirty($property->getName())) { if ($plainText) { $dump .= ' ' . self::ansiEscapeWrap('modified', '43;30', $ansiColors); } else { $dump .= 'modified'; } } } } } return $dump; } /** * @param mixed $collection * @param int $level * @param bool $plainText * @param bool $ansiColors * @return string */ protected static function renderCollection($collection, $level, $plainText, $ansiColors) { $dump = ''; foreach ($collection as $key => $value) { $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level); if ($plainText) { $dump .= self::ansiEscapeWrap($key, '37', $ansiColors); } else { $dump .= '' . htmlspecialchars($key) . ''; } $dump .= ' => '; $dump .= self::renderDump($value, $level, $plainText, $ansiColors); } if ($collection instanceof \Iterator && !$collection instanceof \Generator) { $collection->rewind(); } return $dump; } /** * Wrap a string with the ANSI escape sequence for colorful output * * @param string $string The string to wrap * @param string $ansiColors The ansi color sequence (e.g. "1;37") * @param bool $enable If FALSE, the raw string will be returned * @return string The wrapped or raw string */ protected static function ansiEscapeWrap($string, $ansiColors, $enable = true) { if ($enable) { return '[' . $ansiColors . 'm' . $string . ''; } return $string; } /** * A var_dump function optimized for Extbase's object structures * * @param mixed $variable The value to dump * @param string $title optional custom title for the debug output * @param int $maxDepth Sets the max recursion depth of the dump. De- or increase the number according to your needs and memory limit. * @param bool $plainText If TRUE, the dump is in plain text, if FALSE the debug output is in HTML format. * @param bool $ansiColors If TRUE (default), ANSI color codes is added to the output, if FALSE the debug output not colored. * @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. * @param array $blacklistedClassNames An array of class names (RegEx) to be filtered. Default is an array of some common class names. * @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. * @return string if $return is TRUE, the dump is returned. By default, the dump is directly displayed, and nothing is returned. */ public static function var_dump($variable, $title = null, $maxDepth = 8, $plainText = false, $ansiColors = true, $return = false, $blacklistedClassNames = null, $blacklistedPropertyNames = null) { self::$maxDepth = $maxDepth; if ($title === null) { $title = 'Extbase Variable Dump'; } $ansiColors = $plainText && $ansiColors; if ($ansiColors === true) { $title = '' . $title . ''; } $backupBlacklistedClassNames = self::$blacklistedClassNames; if (is_array($blacklistedClassNames)) { self::$blacklistedClassNames = $blacklistedClassNames; } $backupBlacklistedPropertyNames = self::$blacklistedPropertyNames; if (is_array($blacklistedPropertyNames)) { self::$blacklistedPropertyNames = $blacklistedPropertyNames; } self::clearState(); $css = ''; if (!$plainText && self::$stylesheetEchoed === false) { $css = ' '; self::$stylesheetEchoed = true; } if ($plainText) { $output = $title . PHP_EOL . self::renderDump($variable, 0, true, $ansiColors) . PHP_EOL . PHP_EOL; } else { $output = '
' . htmlspecialchars($title) . '
' . self::renderDump($variable, 0, false, false) . '
'; } self::$blacklistedClassNames = $backupBlacklistedClassNames; self::$blacklistedPropertyNames = $backupBlacklistedPropertyNames; if ($return === true) { return $css . $output; } echo $css . $output; return ''; } }