diff --git a/typo3/sysext/core/Classes/Utility/DebugUtility.php b/typo3/sysext/core/Classes/Utility/DebugUtility.php index b85ee472fcb062fcf37efbbd171a47b3ecbd1875..6f88a58e776a2fb38dd0e43156aef46e40831ef4 100644 --- a/typo3/sysext/core/Classes/Utility/DebugUtility.php +++ b/typo3/sysext/core/Classes/Utility/DebugUtility.php @@ -20,32 +20,45 @@ use TYPO3\CMS\Extbase\Utility\DebuggerUtility; */ class DebugUtility { + /** + * @var bool + */ + static protected $plainTextOutput = true; + + /** + * @var bool + */ + static protected $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) . ', @@ -129,7 +134,7 @@ class DebugUtility * Displays the "path" of the function call stack in a string, using debug_backtrace * * @param bool $prependFileNames If set to true file names are added to the output - * @return string + * @return string plain, not HTML encoded string */ public static function debugTrail($prependFileNames = false) { @@ -154,11 +159,10 @@ class DebugUtility * * @param mixed $rows Array of arrays with similar keys * @param string $header Table header - * @return void Outputs to browser. */ public static function debugRows($rows, $header = '') { - self::debug('<pre>' . DebuggerUtility::var_dump($rows, $header, 8, true, false, true), $header . '</pre>'); + self::debug($rows, $header); } /** @@ -190,7 +194,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); } /** @@ -202,6 +206,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 index 0000000000000000000000000000000000000000..396514acd4525b434646c97e4ead5e89de781c3e --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Utility/DebugUtilityTest.php @@ -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( + '<script>alert(\'Hello world!\')</script>', + $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 => public 42 (integer)' . PHP_EOL +. ' bar => public 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)); + } +} diff --git a/typo3/sysext/extbase/Classes/Utility/DebuggerUtility.php b/typo3/sysext/extbase/Classes/Utility/DebuggerUtility.php index e7fed4b16a2b3034a14bc7a3b362c0a2a36e5180..f1c15afbe06d7bd680c2c2c3ddccc10d6081d3f0 100644 --- a/typo3/sysext/extbase/Classes/Utility/DebuggerUtility.php +++ b/typo3/sysext/extbase/Classes/Utility/DebuggerUtility.php @@ -337,7 +337,7 @@ class DebuggerUtility $property->setAccessible(true); $visibility = ($property->isProtected() ? 'protected' : ($property->isPrivate() ? 'private' : 'public')); if ($plainText) { - $dump .= ' ' . self::ansiEscapeWrap($visibility, '42;30', $ansiColors) . ' '; + $dump .= self::ansiEscapeWrap($visibility, '42;30', $ansiColors) . ' '; } else { $dump .= '<span class="extbase-debug-visibility">' . $visibility . '</span>'; } @@ -427,7 +427,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} diff --git a/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php b/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php index e08c38d9985247ad085d81c5a00a5f333da8fa63..6d2d87bdca69868a97b928287986a6a0885fa031 100755 --- a/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php +++ b/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php @@ -19,6 +19,7 @@ use TYPO3\CMS\Core\Charset\CharsetConverter; use TYPO3\CMS\Core\Core\ApplicationContext; use TYPO3\CMS\Core\Log\LogManager; 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; @@ -3006,9 +3007,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); @@ -3031,9 +3032,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); @@ -3054,9 +3055,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); @@ -3077,9 +3078,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); @@ -3099,9 +3100,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);