[BUGFIX] Fix output of DebugUtility 98/48398/3
authorHelmut Hummel <info@helhum.io>
Wed, 25 May 2016 17:26:52 +0000 (19:26 +0200)
committerHelmut Hummel <helmut.hummel@typo3.org>
Wed, 8 Jun 2016 10:36:52 +0000 (12:36 +0200)
This change prettifies the output of the debug output
methods, by using the Extbase DebuggerUtility for that purpose.

The benefits are consistent, well readable, properly encoded output
for every case the class provides.

In the long run, this class should be replaced with
a more object oriented approach.

Resolves: #76302
Releases: master, 7.6
Change-Id: Iacbb48701f6c98139bd7db86795952a123e076da
Reviewed-on: https://review.typo3.org/48398
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Helmut Hummel <helmut.hummel@typo3.org>
Tested-by: Helmut Hummel <helmut.hummel@typo3.org>
typo3/sysext/core/Classes/Utility/DebugUtility.php
typo3/sysext/core/Tests/Unit/Utility/DebugUtilityTest.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Utility/DebuggerUtility.php
typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php

index 980646d..e8f491d 100644 (file)
@@ -21,31 +21,44 @@ use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
 class DebugUtility
 {
     /**
+     * @var bool
+     */
+    protected static $plainTextOutput = true;
+
+    /**
+     * @var bool
+     */
+    protected static $ansiColorUsage = true;
+
+    /**
      * Debug
      *
+     * Directly echos out debug information as HTML (or plain in CLI context)
+     *
      * @param string $var
      * @param string $header
      * @param string $group
-     * @return void
      */
     public static function debug($var = '', $header = '', $group = 'Debug')
     {
         // buffer the output of debug if no buffering started before
-        if (ob_get_level() == 0) {
+        if (ob_get_level() === 0) {
             ob_start();
         }
-        $debug = self::convertVariableToString($var);
-        if (TYPO3_MODE === 'BE' && !(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI)) {
+        if (TYPO3_MODE === 'BE' && !self::isCommandLine()) {
             $tabHeader = $header ?: 'Debug';
+            $debug = self::renderDump($var);
+            $debugPlain = PHP_EOL . self::renderDump($var, '', true, false);
             $script = '
                                (function debug() {
                                        var message = ' . GeneralUtility::quoteJSvalue($debug) . ',
-                                               header = ' . GeneralUtility::quoteJSvalue($header) . ',
+                                               messagePlain = ' . GeneralUtility::quoteJSvalue($debugPlain) . ',
+                                               header = ' . GeneralUtility::quoteJSvalue($tabHeader) . ',
                                                group = ' . GeneralUtility::quoteJSvalue($group) . ';
-                                       if (top.TYPO3.DebugConsole) {
+                                       if (top.TYPO3 && top.TYPO3.DebugConsole) {
                                                top.TYPO3.DebugConsole.add(message, header, group);
                                        } else {
-                                               var consoleMessage = [group, header, message].join(" | ");
+                                               var consoleMessage = [group, header, messagePlain].join(" | ");
                                                if (typeof console === "object" && typeof console.log === "function") {
                                                        console.log(consoleMessage);
                                                }
@@ -54,7 +67,7 @@ class DebugUtility
                        ';
             echo GeneralUtility::wrapJS($script);
         } else {
-            echo $debug;
+            echo self::renderDump($var);
         }
     }
 
@@ -62,20 +75,12 @@ class DebugUtility
      * Converts a variable to a string
      *
      * @param mixed $variable
-     * @return string
+     * @return string plain, not HTML encoded string
      */
     public static function convertVariableToString($variable)
     {
-        if (is_array($variable)) {
-            $string = self::viewArray($variable);
-        } elseif (is_object($variable)) {
-            $string = json_encode($variable, true);
-        } elseif ((string)$variable !== '') {
-            $string = htmlspecialchars((string)$variable);
-        } else {
-            $string = '| debug |';
-        }
-        return $string;
+        $string = self::renderDump($variable, '', true, false);
+        return $string === '' ? '| debug |' : $string;
     }
 
     /**
@@ -87,7 +92,7 @@ class DebugUtility
      */
     public static function debugInPopUpWindow($debugVariable, $header = 'Debug', $group = 'Debug')
     {
-        $debugString = self::convertVariableToString($debugVariable);
+        $debugString = self::renderDump($debugVariable, sprintf('%s (%s)', $header, $group));
         $script = '
                        (function debug() {
                                var debugMessage = ' . GeneralUtility::quoteJSvalue($debugString) . ',
@@ -128,7 +133,7 @@ class DebugUtility
     /**
      * Displays the "path" of the function call stack in a string, using debug_backtrace
      *
-     * @return string
+     * @return string plain, not HTML encoded string
      */
     public static function debugTrail()
     {
@@ -153,14 +158,13 @@ class DebugUtility
      * @param mixed $rows Array of arrays with similar keys
      * @param string $header Table header
      * @param bool $returnHTML If TRUE, will return content instead of echo'ing out. Deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
-     * @return void Outputs to browser.
      */
     public static function debugRows($rows, $header = '', $returnHTML = false)
     {
         if ($returnHTML !== false) {
             GeneralUtility::deprecationLog('Setting the parameter $returnHTML is deprecated since TYPO3 CMS 7 and will be removed in TYPO3 CMS 8.');
         }
-        self::debug('<pre>' . DebuggerUtility::var_dump($rows, $header, 8, true, false, true), $header . '</pre>');
+        self::debug($rows, $header);
     }
 
     /**
@@ -192,7 +196,7 @@ class DebugUtility
      */
     public static function viewArray($array_in)
     {
-        return '<pre>' . DebuggerUtility::var_dump($array_in, '', 8, true, false, true) . '</pre>';
+        return self::renderDump($array_in);
     }
 
     /**
@@ -204,6 +208,62 @@ class DebugUtility
      */
     public static function printArray($array_in)
     {
-        echo self::viewArray($array_in);
+        echo self::renderDump($array_in);
+    }
+
+    /**
+     * Renders the dump according to the context, either for command line or as HTML output
+     *
+     * @param mixed $variable
+     * @param string $title
+     * @param bool|null $plainText
+     * @param bool|null $ansiColors
+     * @return string
+     */
+    protected static function renderDump($variable, $title = '', $plainText = null, $ansiColors = null)
+    {
+        $plainText = $plainText === null ? self::isCommandLine() && self::$plainTextOutput : $plainText;
+        $ansiColors = $ansiColors === null ? self::isCommandLine() && self::$ansiColorUsage : $ansiColors;
+        return trim(DebuggerUtility::var_dump($variable, $title, 8, $plainText, $ansiColors, true));
+    }
+
+    /**
+     * Checks some constants to determine if we are in CLI context
+     *
+     * @return bool
+     */
+    protected static function isCommandLine()
+    {
+        return (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) || PHP_SAPI === 'cli';
+    }
+
+    /**
+     * Preset plaintext output
+     *
+     * Warning:
+     * This is NOT a public API method and must not be used in own extensions!
+     * This method is usually only used in tests to preset the output behaviour
+     *
+     * @internal
+     * @param bool $plainTextOutput
+     */
+    public static function usePlainTextOutput($plainTextOutput)
+    {
+        static::$plainTextOutput = $plainTextOutput;
+    }
+
+    /**
+     * Preset ansi color usage
+     *
+     * Warning:
+     * This is NOT a public API method and must not be used in own extensions!
+     * This method is usually only used in tests to preset the ansi color usage
+     *
+     * @internal
+     * @param bool $ansiColorUsage
+     */
+    public static function useAnsiColor($ansiColorUsage)
+    {
+        static::$ansiColorUsage = $ansiColorUsage;
     }
 }
diff --git a/typo3/sysext/core/Tests/Unit/Utility/DebugUtilityTest.php b/typo3/sysext/core/Tests/Unit/Utility/DebugUtilityTest.php
new file mode 100644 (file)
index 0000000..6c7479b
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+namespace TYPO3\CMS\Core\Tests\Unit\Utility;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Tests\UnitTestCase;
+use TYPO3\CMS\Core\Utility\DebugUtility;
+
+/**
+ * Testcase for class \TYPO3\CMS\Core\Utility\DebugUtility
+ */
+class DebugUtilityTest extends UnitTestCase
+{
+    protected function tearDown()
+    {
+        parent::tearDown();
+        DebugUtility::usePlainTextOutput(true);
+        DebugUtility::useAnsiColor(true);
+    }
+
+    /**
+     * @test
+     */
+    public function debugNotEncodesHtmlInputIfPlainText()
+    {
+        DebugUtility::usePlainTextOutput(true);
+        DebugUtility::useAnsiColor(false);
+
+        ob_start();
+        DebugUtility::debug('<script>alert(\'Hello world!\')</script>');
+        $output = ob_get_contents();
+        ob_end_clean();
+
+        $this->assertContains(
+            '<script>alert(\'Hello world!\')</script>',
+            $output
+        );
+    }
+
+    /**
+     * @test
+     */
+    public function debugEncodesHtmlInputIfNoPlainText()
+    {
+        DebugUtility::usePlainTextOutput(false);
+        DebugUtility::useAnsiColor(false);
+
+        ob_start();
+        DebugUtility::debug('<script>alert(\'Hello world!\')</script>');
+        $output = ob_get_contents();
+        ob_end_clean();
+
+        $this->assertContains(
+            '&lt;script&gt;alert(\'Hello world!\')&lt;/script&gt;',
+            $output
+        );
+    }
+
+    /**
+     * @return array
+     */
+    public function convertVariableToStringReturnsVariableContentDataProvider()
+    {
+        $object = new \stdClass();
+        $object->foo = 42;
+        $object->bar = ['baz'];
+
+        return [
+            'Debug string' => [
+                'Hello world!',
+                '"Hello world!" (12 chars)',
+            ],
+            'Debug array' => [
+                [
+                    'foo',
+                    'bar',
+                    'baz' => [
+                        42,
+                    ],
+                ],
+                'array(3 items)' . PHP_EOL
+. '   0 => "foo" (3 chars)' . PHP_EOL
+. '   1 => "bar" (3 chars)' . PHP_EOL
+. '   baz => array(1 item)' . PHP_EOL
+. '      0 => 42 (integer)',
+            ],
+            'Debug object' => [
+                $object,
+                'stdClass prototype object' . PHP_EOL
+. '   foo => 42 (integer)' . PHP_EOL
+. '   bar => array(1 item)' . PHP_EOL
+. '      0 => "baz" (3 chars)'
+            ],
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider convertVariableToStringReturnsVariableContentDataProvider
+     * @param mixed $variable
+     * @param string $expected
+     */
+    public function convertVariableToStringReturnsVariableContent($variable, $expected)
+    {
+        $this->assertSame($expected, DebugUtility::convertVariableToString($variable));
+    }
+}
index bee8160..7c0ae47 100644 (file)
@@ -267,7 +267,7 @@ class DebuggerUtility
             $domainObjectType = 'object';
         }
         if ($plainText) {
-            $dump .= ' ' . self::ansiEscapeWrap(($persistenceType . ' ' . $domainObjectType), '42;30', $ansiColors);
+            $dump .= ' ' . self::ansiEscapeWrap(($persistenceType ? $persistenceType . ' ' : '') . $domainObjectType, '42;30', $ansiColors);
         } else {
             $dump .= '<span class="extbase-debug-ptype">' . ($persistenceType ? $persistenceType . ' ' : '') . $domainObjectType . '</span>';
         }
@@ -421,7 +421,7 @@ class DebuggerUtility
             $css = '
                                <style type=\'text/css\'>
                                        .extbase-debugger-tree{position:relative}
-                                       .extbase-debugger-tree input{position:absolute;top:0;left:0;height:14px;width:14px;margin:0;cursor:pointer;opacity:0;z-index:2}
+                                       .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}
                                        .extbase-debugger-tree input~.extbase-debug-content{display:none}
                                        .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}
                                        .extbase-debugger-tree input:checked~.extbase-debug-content{display:inline}
index 38d4546..f19e43d 100644 (file)
@@ -20,6 +20,7 @@ use TYPO3\CMS\Core\Core\ApplicationContext;
 use TYPO3\CMS\Core\Log\LogManager;
 use TYPO3\CMS\Core\TimeTracker\NullTimeTracker;
 use TYPO3\CMS\Core\TypoScript\TemplateService;
+use TYPO3\CMS\Core\Utility\DebugUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\ContentObject\AbstractContentObject;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
@@ -2979,9 +2980,9 @@ class ContentObjectRendererTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
         $expectedResult = 'array(3items)0=>array(2items)uid=>1(integer)title=>"title1"(6chars)1=>array(2items)uid=>2(integer)title=>"title2"(6chars)2=>array(2items)uid=>3(integer)title=>""(0chars)';
         $GLOBALS['TSFE']->tmpl->rootLine = $rootline;
 
+        DebugUtility::useAnsiColor(false);
         $result = $this->subject->getData('debug:rootLine');
-        $cleanedResult = strip_tags($result);
-        $cleanedResult = str_replace("\r", '', $cleanedResult);
+        $cleanedResult = str_replace("\r", '', $result);
         $cleanedResult = str_replace("\n", '', $cleanedResult);
         $cleanedResult = str_replace("\t", '', $cleanedResult);
         $cleanedResult = str_replace(' ', '', $cleanedResult);
@@ -3004,9 +3005,9 @@ class ContentObjectRendererTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
         $expectedResult = 'array(3items)0=>array(2items)uid=>1(integer)title=>"title1"(6chars)1=>array(2items)uid=>2(integer)title=>"title2"(6chars)2=>array(2items)uid=>3(integer)title=>""(0chars)';
         $GLOBALS['TSFE']->rootLine = $rootline;
 
+        DebugUtility::useAnsiColor(false);
         $result = $this->subject->getData('debug:fullRootLine');
-        $cleanedResult = strip_tags($result);
-        $cleanedResult = str_replace("\r", '', $cleanedResult);
+        $cleanedResult = str_replace("\r", '', $result);
         $cleanedResult = str_replace("\n", '', $cleanedResult);
         $cleanedResult = str_replace("\t", '', $cleanedResult);
         $cleanedResult = str_replace(' ', '', $cleanedResult);
@@ -3027,9 +3028,9 @@ class ContentObjectRendererTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
 
         $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
 
+        DebugUtility::useAnsiColor(false);
         $result = $this->subject->getData('debug:data');
-        $cleanedResult = strip_tags($result);
-        $cleanedResult = str_replace("\r", '', $cleanedResult);
+        $cleanedResult = str_replace("\r", '', $result);
         $cleanedResult = str_replace("\n", '', $cleanedResult);
         $cleanedResult = str_replace("\t", '', $cleanedResult);
         $cleanedResult = str_replace(' ', '', $cleanedResult);
@@ -3050,9 +3051,9 @@ class ContentObjectRendererTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
 
         $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
 
+        DebugUtility::useAnsiColor(false);
         $result = $this->subject->getData('debug:register');
-        $cleanedResult = strip_tags($result);
-        $cleanedResult = str_replace("\r", '', $cleanedResult);
+        $cleanedResult = str_replace("\r", '', $result);
         $cleanedResult = str_replace("\n", '', $cleanedResult);
         $cleanedResult = str_replace("\t", '', $cleanedResult);
         $cleanedResult = str_replace(' ', '', $cleanedResult);
@@ -3072,9 +3073,9 @@ class ContentObjectRendererTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
 
         $expectedResult = 'array(1item)uid=>' . $uid . '(integer)';
 
+        DebugUtility::useAnsiColor(false);
         $result = $this->subject->getData('debug:page');
-        $cleanedResult = strip_tags($result);
-        $cleanedResult = str_replace("\r", '', $cleanedResult);
+        $cleanedResult = str_replace("\r", '', $result);
         $cleanedResult = str_replace("\n", '', $cleanedResult);
         $cleanedResult = str_replace("\t", '', $cleanedResult);
         $cleanedResult = str_replace(' ', '', $cleanedResult);