[FEATURE] Render closures with DebuggerUtility::var_dump() 53/48453/4
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Sun, 5 Jun 2016 14:53:53 +0000 (16:53 +0200)
committerSusanne Moog <typo3@susannemoog.de>
Mon, 6 Jun 2016 16:47:25 +0000 (18:47 +0200)
DebuggerUtility::var_dump() is now able to render closures. The source
code of a closure is now rendered in the output.

Resolves: #76458
Releases: master
Change-Id: Ie9a8e84250a19ee64e3e7d4cb0fbbc078c468bfa
Reviewed-on: https://review.typo3.org/48453
Reviewed-by: Joerg Boesche <typo3@joergboesche.de>
Tested-by: Joerg Boesche <typo3@joergboesche.de>
Reviewed-by: Susanne Moog <typo3@susannemoog.de>
Tested-by: Susanne Moog <typo3@susannemoog.de>
typo3/sysext/core/Documentation/Changelog/master/Feature-76458-LetDebuggerUtilityRenderClosures.rst [new file with mode: 0644]
typo3/sysext/extbase/Classes/Utility/DebuggerUtility.php

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-76458-LetDebuggerUtilityRenderClosures.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-76458-LetDebuggerUtilityRenderClosures.rst
new file mode 100644 (file)
index 0000000..725b5f2
--- /dev/null
@@ -0,0 +1,14 @@
+=====================================================
+Feature: #76458 - Let DebuggerUtility render closures
+=====================================================
+
+Description
+===========
+
+The ``DebuggerUtility`` of Extbase is now able to render closures.
+
+
+Impact
+======
+
+If a closure is part of the debugging object, the source code of the closure is rendered.
\ No newline at end of file
index f1c15af..2434fe1 100644 (file)
@@ -114,7 +114,11 @@ class DebuggerUtility
         } elseif (is_array($value)) {
             $dump = self::renderArray($value, $level + 1, $plainText, $ansiColors);
         } elseif (is_object($value)) {
-            $dump = self::renderObject($value, $level + 1, $plainText, $ansiColors);
+            if ($value instanceof \Closure) {
+                $dump = self::renderClosure($value, $level + 1, $plainText, $ansiColors);
+            } else {
+                $dump = self::renderObject($value, $level + 1, $plainText, $ansiColors);
+            }
         }
         return $dump;
     }
@@ -190,6 +194,30 @@ class DebuggerUtility
     }
 
     /**
+     * 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;
+        } else {
+            return '<span class="extbase-debugger-tree"><input type="checkbox" /><span class="extbase-debug-header">' . $header . '</span><span class="extbase-debug-content">' . $content . '</span></span>';
+        }
+    }
+
+    /**
      * Checks if a given object or property should be excluded/filtered
      *
      * @param object $value An ReflectionProperty or other Object
@@ -322,31 +350,81 @@ class DebuggerUtility
             if (!$plainText) {
                 $dump .= '<a name="' . spl_object_hash($object) . '" id="' . spl_object_hash($object) . '"></a>';
             }
-            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;
+            if ($object instanceof \Closure) {
+                $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level)
+                    . ($plainText ? '' : '<span class="extbase-debug-closure">')
+                    . self::ansiEscapeWrap('function (', '33', $ansiColors) . ($plainText ? '' : '</span>');
+
+                $reflectionFunction = new \ReflectionFunction($object);
+                $params = [];
+                foreach ($reflectionFunction->getParameters() as $parameter) {
+                    $s = '';
+                    if ($parameter->isArray()) {
+                        $s .= ($plainText ? '' : '<span class="extbase-debug-type">')
+                            . self::ansiEscapeWrap('array ', '36', $ansiColors)
+                            . ($plainText ? '' : '</span>');
+                    } else {
+                        if ($parameter->getClass()) {
+                            $s .= ($plainText ? '' : '<span class="extbase-debug-type">')
+                                . self::ansiEscapeWrap($parameter->getClass()->name . ' ', '36', $ansiColors)
+                                . ($plainText ? '' : '</span>');
+                        }
+                    }
+                    if ($parameter->isPassedByReference()) {
+                        $s .= '&';
+                    }
+                    $s .= ($plainText ? '' : '<span class="extbase-debug-property">')
+                        . self::ansiEscapeWrap('$' . $parameter->name, '37', $ansiColors)
+                        . ($plainText ? '' : '</span>');
+                    if ($parameter->isOptional()) {
+                        $s .= ' = ';
+                        $s .= ($plainText ? '' : '<span class="extbase-debug-string">')
+                        . self::ansiEscapeWrap(var_export($parameter->getDefaultValue(), true), '33', $ansiColors)
+                        . ($plainText ? '' : '</span>');
+                    }
+                    $params[] = $s;
                 }
-                $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT, $level) . ($plainText ? '' : '<span class="extbase-debug-property">') . self::ansiEscapeWrap($property->getName(), '37', $ansiColors) . ($plainText ? '' : '</span>') . ' => ';
-                $property->setAccessible(true);
-                $visibility = ($property->isProtected() ? 'protected' : ($property->isPrivate() ? 'private' : 'public'));
-                if ($plainText) {
-                    $dump .= self::ansiEscapeWrap($visibility, '42;30', $ansiColors) . ' ';
+                $dump .= implode(', ', $params);
+                $dump .= ($plainText ? '' : '<span class="extbase-debug-closure">')
+                    . self::ansiEscapeWrap(') {' . PHP_EOL, '33', $ansiColors)
+                    . ($plainText ? '' : '</span>');
+                $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)
+                    . ($plainText ? '' : '<span class="extbase-debug-closure">') . self::ansiEscapeWrap('}' . PHP_EOL, '33', $ansiColors)
+                    . ($plainText ? '' : '</span>');
+            } else {
+                if (get_class($object) === 'stdClass') {
+                    $objReflection = new \ReflectionObject($object);
+                    $properties = $objReflection->getProperties();
                 } else {
-                    $dump .= '<span class="extbase-debug-visibility">' . $visibility . '</span>';
+                    $classReflection = new \ReflectionClass(get_class($object));
+                    $properties = $classReflection->getProperties();
                 }
-                $dump .= self::renderDump($property->getValue($object), $level, $plainText, $ansiColors);
-                if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject && !$object->_isNew() && $object->_isDirty($property->getName())) {
+                foreach ($properties as $property) {
+                    if (self::isBlacklisted($property)) {
+                        continue;
+                    }
+                    $dump .= PHP_EOL . str_repeat(self::PLAINTEXT_INDENT,
+                            $level) . ($plainText ? '' : '<span class="extbase-debug-property">') . self::ansiEscapeWrap($property->getName(),
+                            '37', $ansiColors) . ($plainText ? '' : '</span>') . ' => ';
+                    $property->setAccessible(true);
+                    $visibility = ($property->isProtected() ? 'protected' : ($property->isPrivate() ? 'private' : 'public'));
                     if ($plainText) {
-                        $dump .= ' ' . self::ansiEscapeWrap('modified', '43;30', $ansiColors);
+                        $dump .= self::ansiEscapeWrap($visibility, '42;30', $ansiColors) . ' ';
                     } else {
-                        $dump .= '<span class="extbase-debug-dirty">modified</span>';
+                        $dump .= '<span class="extbase-debug-visibility">' . $visibility . '</span>';
+                    }
+                    $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 .= '<span class="extbase-debug-dirty">modified</span>';
+                        }
                     }
                 }
             }
@@ -449,6 +527,7 @@ class DebuggerUtility
                                        .extbase-debugger-center .extbase-debug-filtered{background-color:#4F4F4F}
                                        .extbase-debugger-center .extbase-debug-seeabove{text-decoration:none;font-style:italic}
                                        .extbase-debugger-center .extbase-debug-property{color:#f1f1f1}
+                                       .extbase-debugger-center .extbase-debug-closure{color:#9BA223;}
                                </style>';
             self::$stylesheetEchoed = true;
         }