diff --git a/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php b/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php index 9df3726def971e53f5ae9aeea6c9bc8ea61fa870..136b0a65e3d7f7f20558021acb92bc26c179fc9e 100644 --- a/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php +++ b/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php @@ -213,7 +213,6 @@ class SystemEnvironmentBuilder protected static function initializeGlobalVariables() { // Unset variable(s) in global scope (security issue #13959) - unset($GLOBALS['error']); $GLOBALS['TYPO3_MISC'] = []; $GLOBALS['T3_VAR'] = []; $GLOBALS['T3_SERVICES'] = []; diff --git a/typo3/sysext/core/Classes/ExtDirect/ExtDirectDebug.php b/typo3/sysext/core/Classes/ExtDirect/ExtDirectDebug.php deleted file mode 100644 index 5d96f22fb55267ca333233ccdff2513e5830c69e..0000000000000000000000000000000000000000 --- a/typo3/sysext/core/Classes/ExtDirect/ExtDirectDebug.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -namespace TYPO3\CMS\Core\ExtDirect; - -/* - * 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! - */ - -/** - * Ext Direct Debug - */ -class ExtDirectDebug -{ - /** - * Internal debug message array - * - * @var array - */ - protected $debugMessages = []; - - /** - * destructor - * - * Currently empty, but automatically registered and called during - * ExtDirect shutdown. - * - * @see http://forge.typo3.org/issues/25278 - */ - public function __destruct() - { - } - - /** - * Adds a new message of any data type to the internal debug message array. - * - * @param mixed $message - */ - public function debug($message) - { - $this->debugMessages[] = $message; - } - - /** - * Returns the internal debug messages as a string. - * - * @return string - */ - public function toString() - { - $messagesAsString = ''; - if (!empty($this->debugMessages)) { - $messagesAsString = \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($this->debugMessages); - } - return $messagesAsString; - } -} diff --git a/typo3/sysext/core/Classes/ExtDirect/ExtDirectRouter.php b/typo3/sysext/core/Classes/ExtDirect/ExtDirectRouter.php index 7055d68a331492ec9224af692117123238cd680e..310720c972e41d5c73113a2958584dd952dcb86d 100644 --- a/typo3/sysext/core/Classes/ExtDirect/ExtDirectRouter.php +++ b/typo3/sysext/core/Classes/ExtDirect/ExtDirectRouter.php @@ -32,7 +32,6 @@ class ExtDirectRouter */ public function routeAction(ServerRequestInterface $request, ResponseInterface $response) { - $GLOBALS['error'] = GeneralUtility::makeInstance(\TYPO3\CMS\Core\ExtDirect\ExtDirectDebug::class); $isForm = false; $isUpload = false; $rawPostData = file_get_contents('php://input'); @@ -85,7 +84,6 @@ class ExtDirectRouter } $extResponse[$index]['type'] = 'rpc'; $extResponse[$index]['result'] = $this->processRpc($singleRequest, $namespace); - $extResponse[$index]['debug'] = $GLOBALS['error']->toString(); } catch (\Exception $exception) { $extResponse[$index]['type'] = 'exception'; $extResponse[$index]['message'] = $exception->getMessage(); diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-37180-RemovedExtDirectDebugAndGLOBALSerror.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-37180-RemovedExtDirectDebugAndGLOBALSerror.rst new file mode 100644 index 0000000000000000000000000000000000000000..f4280c51ecae71923b9d07a4280ad9833ac6be34 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Breaking-37180-RemovedExtDirectDebugAndGLOBALSerror.rst @@ -0,0 +1,34 @@ +.. include:: ../../Includes.txt + +=============================================================== +Breaking: #37180 - ExtDirectDebug and $GLOBALS['error'] removed +=============================================================== + +See :issue:`37180` + +Description +=========== + +The class :php:`TYPO3\CMS\Core\ExtDirect\ExtDirectDebug` has been removed and within the change, also the usage of the +global variable :php:`$GLOBALS['error']` has been removed. + +The following global methods are removed as well: + +- :php:`debugBegin()` +- :php:`debugEnd()` + + +Impact +====== + +Accessing the class :php:`TYPO3\CMS\Core\ExtDirect\ExtDirectDebug`, the global variable :php:`$GLOBALS['error']` or the +global methods :php:`debugBegin()` and :php:`debugEnd()` will lead to an exception. + + +Affected Installations +====================== + +All instances, that use the mentioned class, global methods or access the global variable. +The extension scanner of the install tool will find affected extensions. + +.. index:: PHP-API, FullyScanned diff --git a/typo3/sysext/core/Resources/PHP/GlobalDebugFunctions.php b/typo3/sysext/core/Resources/PHP/GlobalDebugFunctions.php index a50802e93b71e6bae029e20f03f2df1bdfb92d99..0224fb3a60971d1bbf86a23f2b7854671753243e 100644 --- a/typo3/sysext/core/Resources/PHP/GlobalDebugFunctions.php +++ b/typo3/sysext/core/Resources/PHP/GlobalDebugFunctions.php @@ -10,7 +10,7 @@ \TYPO3\CMS\Core\Utility\DebugUtility::debug($var, $debugTitle); } - // Debug function which calls $GLOBALS['error'] error handler if available + // Debug function function debug($variable = '', $name = '*variable*', $line = '*line*', $file = '*file*', $recursiveDepth = 3, $debugLevel = E_DEBUG) { // If you wish to use the debug()-function, and it does not output something, @@ -18,25 +18,7 @@ if (!\TYPO3\CMS\Core\Utility\GeneralUtility::cmpIP(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('REMOTE_ADDR'), $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'])) { return; } - if (is_object($GLOBALS['error']) && @is_callable([$GLOBALS['error'], 'debug'])) { - $GLOBALS['error']->debug($variable, $name, $line, $file, $recursiveDepth, $debugLevel); - } else { - $title = $name === '*variable*' ? '' : $name; - $group = $line === '*line*' ? null : $line; - \TYPO3\CMS\Core\Utility\DebugUtility::debug($variable, $title, $group); - } - } - - function debugBegin() - { - if (is_object($GLOBALS['error']) && @is_callable([$GLOBALS['error'], 'debugBegin'])) { - $GLOBALS['error']->debugBegin(); - } - } - - function debugEnd() - { - if (is_object($GLOBALS['error']) && @is_callable([$GLOBALS['error'], 'debugEnd'])) { - $GLOBALS['error']->debugEnd(); - } + $title = $name === '*variable*' ? '' : $name; + $group = $line === '*line*' ? null : $line; + \TYPO3\CMS\Core\Utility\DebugUtility::debug($variable, $title, $group); } diff --git a/typo3/sysext/core/Tests/Unit/Core/SystemEnvironmentBuilderTest.php b/typo3/sysext/core/Tests/Unit/Core/SystemEnvironmentBuilderTest.php index feeb8cd059949dc9f6a44c3cbe1ba584e8715b11..92988a39f01747e44a5845a4597a9b0e530a0938 100644 --- a/typo3/sysext/core/Tests/Unit/Core/SystemEnvironmentBuilderTest.php +++ b/typo3/sysext/core/Tests/Unit/Core/SystemEnvironmentBuilderTest.php @@ -105,16 +105,6 @@ class SystemEnvironmentBuilderTest extends \TYPO3\TestingFramework\Core\Unit\Uni $this->assertStringStartsWith($fakedAbsolutePart, $this->subject->_call('getPathThisScriptCli')); } - /** - * @test - */ - public function initializeGlobalVariablesUnsetsGlobalErrorArray() - { - $GLOBALS['error'] = 'foo'; - $this->subject->_call('initializeGlobalVariables'); - $this->assertFalse(isset($GLOBALS['error'])); - } - /** * @test */ diff --git a/typo3/sysext/frontend/Classes/Http/RequestHandler.php b/typo3/sysext/frontend/Classes/Http/RequestHandler.php index c54cb4c1edd1767913085ebe2000c86615c49d8d..7215158f9fea408d878bd0f651d8bb39a7a99b62 100644 --- a/typo3/sysext/frontend/Classes/Http/RequestHandler.php +++ b/typo3/sysext/frontend/Classes/Http/RequestHandler.php @@ -261,10 +261,6 @@ class RequestHandler implements RequestHandlerInterface $response = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\Response::class); $response->getBody()->write($this->controller->content); } - // Debugging Output - if (isset($GLOBALS['error']) && is_object($GLOBALS['error']) && @is_callable([$GLOBALS['error'], 'debugOutput'])) { - $GLOBALS['error']->debugOutput(); - } GeneralUtility::devLog('END of FRONTEND session', 'cms', 0, ['_FLUSH' => true]); return $response; } diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/ExtensionScannerScanFile.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/ExtensionScannerScanFile.php index 2f8f39909d4db32cd9fce692a4f0ac3ed4a953a1..ecc904df1219599a82330ca64b80b5a5ff595e56 100644 --- a/typo3/sysext/install/Classes/Controller/Action/Ajax/ExtensionScannerScanFile.php +++ b/typo3/sysext/install/Classes/Controller/Action/Ajax/ExtensionScannerScanFile.php @@ -29,6 +29,7 @@ use TYPO3\CMS\Install\ExtensionScanner\Php\Matcher\ArrayGlobalMatcher; use TYPO3\CMS\Install\ExtensionScanner\Php\Matcher\ClassConstantMatcher; use TYPO3\CMS\Install\ExtensionScanner\Php\Matcher\ClassNameMatcher; use TYPO3\CMS\Install\ExtensionScanner\Php\Matcher\ConstantMatcher; +use TYPO3\CMS\Install\ExtensionScanner\Php\Matcher\FunctionCallMatcher; use TYPO3\CMS\Install\ExtensionScanner\Php\Matcher\InterfaceMethodChangedMatcher; use TYPO3\CMS\Install\ExtensionScanner\Php\Matcher\MethodArgumentDroppedMatcher; use TYPO3\CMS\Install\ExtensionScanner\Php\Matcher\MethodArgumentDroppedStaticMatcher; @@ -70,6 +71,10 @@ class ExtensionScannerScanFile extends AbstractAjaxAction 'class' => ConstantMatcher::class, 'configurationFile' => 'EXT:install/Configuration/ExtensionScanner/Php/ConstantMatcher.php', ], + [ + 'class' => FunctionCallMatcher::class, + 'configurationFile' => 'EXT:install/Configuration/ExtensionScanner/Php/FunctionCallMatcher.php', + ], [ 'class' => InterfaceMethodChangedMatcher::class, 'configurationFile' => 'EXT:install/Configuration/ExtensionScanner/Php/InterfaceMethodChangedMatcher.php', diff --git a/typo3/sysext/install/Classes/ExtensionScanner/Php/Matcher/FunctionCallMatcher.php b/typo3/sysext/install/Classes/ExtensionScanner/Php/Matcher/FunctionCallMatcher.php new file mode 100644 index 0000000000000000000000000000000000000000..dcc3568472c1185089862d29d10a5290a7d33dce --- /dev/null +++ b/typo3/sysext/install/Classes/ExtensionScanner/Php/Matcher/FunctionCallMatcher.php @@ -0,0 +1,73 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Install\ExtensionScanner\Php\Matcher; + +/* + * 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 PhpParser\Node; +use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Name\FullyQualified; + +/** + * Find usages of global function calls which were removed / deprecated. + * This is a strong match. + */ +class FunctionCallMatcher extends AbstractCoreMatcher +{ + /** + * Prepare $this->flatMatcherDefinitions once + * + * @param array $matcherDefinitions Incoming main configuration + */ + public function __construct(array $matcherDefinitions) + { + $this->matcherDefinitions = $matcherDefinitions; + $this->validateMatcherDefinitions(['numberOfMandatoryArguments', 'maximumNumberOfArguments']); + } + + /** + * Called by PhpParser. + * Test for "removedFunction()" (strong match) + * + * @param Node $node + */ + public function enterNode(Node $node) + { + // Match method call (not static) + if (!$this->isFileIgnored($node) + && !$this->isLineIgnored($node) + && $node instanceof FuncCall + && $node->name instanceof FullyQualified + && in_array($node->name->toString(), array_keys($this->matcherDefinitions), true) + ) { + $functionName = $node->name->toString(); + $matchDefinition = $this->matcherDefinitions[$functionName]; + + $numberOfArguments = count($node->args); + $isArgumentUnpackingUsed = $this->isArgumentUnpackingUsed($node->args); + + if ($isArgumentUnpackingUsed + || ($numberOfArguments >= $matchDefinition['numberOfMandatoryArguments'] + && $numberOfArguments <= $matchDefinition['maximumNumberOfArguments']) + ) { + $this->matches[] = [ + 'restFiles' => $matchDefinition['restFiles'], + 'line' => $node->getAttribute('startLine'), + 'message' => 'Call to function "' . $functionName . '"', + 'indicator' => 'strong', + ]; + } + } + } +} diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayGlobalMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayGlobalMatcher.php index e5372822448186e61b1119d8cced212eb36f0031..99156681d3515a59b09311b49aeda8051f5d0936 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayGlobalMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayGlobalMatcher.php @@ -5,4 +5,9 @@ return [ 'Breaking-80929-TYPO3_DBMovedToExtension.rst', ], ], + '$GLOBALS[\'error\']' => [ + 'restFiles' => [ + 'Breaking-37180-RemovedExtDirectDebugAndGLOBALSerror.rst', + ], + ], ]; diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php index 5e07e232a9fbad45958dff4f5c80ed8e1cd322fc..afd4a0e1bfe8da5c93e7d9e944322defffaee7a7 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php @@ -349,6 +349,11 @@ return [ 'Important-82229-FluidImplementationOfCmsVariableProviderRemoved.rst', ], ], + 'TYPO3\CMS\Core\ExtDirect\ExtDirectDebug' => [ + 'restFiles' => [ + 'Breaking-37180-RemovedExtDirectDebugAndGLOBALSerror.rst', + ], + ], // Removed interfaces 'TYPO3\CMS\Backend\Form\DatabaseFileIconsHookInterface' => [ diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/FunctionCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/FunctionCallMatcher.php new file mode 100644 index 0000000000000000000000000000000000000000..063657fd6510477bcd4524ce2e6bf5f13ea1e1f3 --- /dev/null +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/FunctionCallMatcher.php @@ -0,0 +1,18 @@ +<?php +return [ + // Removed global functions + 'debugBegin' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Breaking-37180-RemovedExtDirectDebugAndGLOBALSerror.rst', + ], + ], + 'debugEnd' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Breaking-37180-RemovedExtDirectDebugAndGLOBALSerror.rst', + ], + ], +]; diff --git a/typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/Fixtures/FunctionCallMatcherFixture.php b/typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/Fixtures/FunctionCallMatcherFixture.php new file mode 100644 index 0000000000000000000000000000000000000000..ead019390831d6eeccac331c37faef57ff29a98a --- /dev/null +++ b/typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/Fixtures/FunctionCallMatcherFixture.php @@ -0,0 +1,42 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Install\Tests\Unit\ExtensionScanner\Php\Matcher\Fixtures; + +/* + * 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\Backend\Utility\BackendUtility; + +/** + * Fixture file + */ +class FunctionCallMatcherFixture +{ + public function aMethod() + { + // Matches + \debugBegin(); + + // No match: Not a global call + debugBegin(); + // No match: Only 1 arg is too much + debugBegin('foo'); + // No match: Class context + $foo->debugBegin(); + // No match: Line ignored + // @extensionScannerIgnoreLine + debugBegin(); + // @extensionScannerIgnoreLine + $bar->bar(\debugBegin()); + } +} diff --git a/typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/Fixtures/MethodCallMatcherFixture.php b/typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/Fixtures/MethodCallMatcherFixture.php index 9371e33fa8ce2d8d2571b7a764c85d7d06a52844..9c13b53e623652890afaaa115cc5217a72a477d9 100644 --- a/typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/Fixtures/MethodCallMatcherFixture.php +++ b/typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/Fixtures/MethodCallMatcherFixture.php @@ -34,6 +34,8 @@ class MethodCallMatcherFixture // Match: Too many args but some could be empty arrays $foo->confirmMsg(...$arg1, ...$arg2, ...$arg3, ...$arg4, ...$arg5, ...$arg6); + \confirmMsg(); + // No match: Only 3 args given $foo->confirmMsg('arg1', 'arg2', 'arg3'); // No match: Too many arguments given diff --git a/typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/FunctionCallMatcherTest.php b/typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/FunctionCallMatcherTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1636f5c026f35448ef93f0532bbcae4425df96e2 --- /dev/null +++ b/typo3/sysext/install/Tests/Unit/ExtensionScanner/Php/Matcher/FunctionCallMatcherTest.php @@ -0,0 +1,103 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Install\Tests\Unit\ExtensionScanner\Php\Matcher; + +/* + * 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 PhpParser\NodeTraverser; +use PhpParser\NodeVisitor\NameResolver; +use PhpParser\ParserFactory; +use TYPO3\CMS\Install\ExtensionScanner\Php\Matcher\FunctionCallMatcher; +use TYPO3\TestingFramework\Core\Unit\UnitTestCase; + +/** + * Test case + */ +class FunctionCallMatcherTest extends UnitTestCase +{ + /** + * @test + */ + public function hitsFromFixtureAreFound() + { + $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); + $fixtureFile = __DIR__ . '/Fixtures/FunctionCallMatcherFixture.php'; + $statements = $parser->parse(file_get_contents($fixtureFile)); + + $traverser = new NodeTraverser(); + $traverser->addVisitor(new NameResolver()); + + $configuration = [ + 'debugBegin' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Breaking-37180-RemovedExtDirectDebugAndGLOBALSerror.rst', + ], + ], + ]; + $subject = new FunctionCallMatcher($configuration); + $traverser->addVisitor($subject); + $traverser->traverse($statements); + $expectedHitLineNumbers = [ + 28, + ]; + $actualHitLineNumbers = []; + foreach ($subject->getMatches() as $hit) { + $actualHitLineNumbers[] = $hit['line']; + } + $this->assertEquals($expectedHitLineNumbers, $actualHitLineNumbers); + } + + /** + * @test + */ + public function matchIsIgnoredIfIgnoreFileIsSet() + { + $phpCode = <<<'EOC' +<?php +/** + * Some comment + * @extensionScannerIgnoreFile This file is ignored + */ +class foo +{ + public function aTest() + { + // This valid match should not match since the entire file is ignored + debugBegin(); + } +} +EOC; + + $parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP7); + $statements = $parser->parse($phpCode); + + $traverser = new NodeTraverser(); + $configuration = [ + 'debugBegin' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Breaking-37180-RemovedExtDirectDebugAndGLOBALSerror.rst', + ], + ], + ]; + $subject = new FunctionCallMatcher($configuration); + $traverser->addVisitor($subject); + $traverser->traverse($statements); + + $this->assertEmpty($subject->getMatches()); + } +}