diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst new file mode 100644 index 0000000000000000000000000000000000000000..8e4442562f4dab9055ce94c5d1dbdadf0e0cfb31 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst @@ -0,0 +1,65 @@ +.. include:: ../../Includes.txt + +================================================================== +Deprecation: #84965 - Various TypoScriptFrontendController methods +================================================================== + +See :issue:`84965` + +Description +=========== + +A lot of functionality from TypoScriptFrontendController (a.k.a. `TSFE`) has been migrated +into new PSR-15 middlewares, which are flexible modules to modify a HTTP request workflow. + +Most of the functionality which is now in a PSR-15-based middleware is related to setting up various +permission and GET/POST variable resolving. + +The following methods within :php:`TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController` have been marked +as deprecated: + +* connectToDB() +* checkAlternativeIdMethods() +* initializeBackendUser() +* handleDataSubmission() +* setCSS() +* convPOSTCharset() + +All hooks previously located within these methods still work as expected, as they are now called within +a PSR-15 middleware. + +Additionally, there are some methods within TSFE which have been marked as "internal" for a long time, +but had the PHP visibility "public" from a legacy code base. These methods, which are internal for TYPO3 Core +purposes, now have the visibility "protected". + +- getPageAndRootline() +- checkRootlineForIncludeSection() +- setSysPageWhereClause() +- checkAndSetAlias() +- getHash() +- getLockHash() +- setUrlIdToken() + + +Impact +====== + +Calling any of the deprecated methods above will trigger a PHP deprecation message. + + +Affected Installations +====================== + +Any TYPO3 installation with a custom extension setting up or mimicking a custom frontend request by +calling TypoScriptFrontendController methods directly. + + +Migration +========= + +Extensions that bootstrap their own frontend should ensure that the respective Middlewares are run, +e.g. via custom stacks or just by setting up the "frontend" middleware stack. + +Additionally, extensions can create custom middlewares to modify a HTTP request or response as well. + +.. index:: Frontend, PHP-API, FullyScanned, ext:frontend \ No newline at end of file diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php index 3bb1bdcad244d2b555e5779596e3029b72c9aebf..ae43cf96218240f94d7523dd4c4d708cd245f97c 100644 --- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php +++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php @@ -840,9 +840,11 @@ class TypoScriptFrontendController implements LoggerAwareInterface * * @throws \RuntimeException * @throws ServiceUnavailableException + * @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10.0. */ public function connectToDB() { + trigger_error('The method "' . __METHOD__ . '" will be removed in TYPO3 v10.0, as the database connection is checked in the TypoScriptFrontendInitialization middleware.', E_USER_DEPRECATED); try { $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages'); $connection->connect(); @@ -976,9 +978,12 @@ class TypoScriptFrontendController implements LoggerAwareInterface * Two options: * 1) Use PATH_INFO (also Apache) to extract id and type from that var. Does not require any special modules compiled with apache. (less typical) * 2) Using hook which enables features like those provided from "realurl" extension (AKA "Speaking URLs") + * + * @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10.0. */ public function checkAlternativeIdMethods() { + trigger_error('This method "' . __METHOD__ . '" is removed, extensions should use a Frontend PSR-15-based middleware to hook into the frontend process. There is no need to call this method directly.', E_USER_DEPRECATED); $this->siteScript = GeneralUtility::getIndpEnv('TYPO3_SITE_SCRIPT'); // Call post processing function for custom URL methods. $_params = ['pObj' => &$this]; @@ -1015,9 +1020,11 @@ class TypoScriptFrontendController implements LoggerAwareInterface * Creates the backend user object and returns it. * * @return FrontendBackendUserAuthentication the backend user object + * @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10.0. */ public function initializeBackendUser() { + trigger_error('The method "' . __METHOD__ . '" is deprecated, and will be removed in TYPO3 v10. Extensions should ensure that the BackendAuthenticator middleware is run to load a backend user.', E_USER_DEPRECATED); // PRE BE_USER HOOK foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'] ?? [] as $_funcRef) { $_params = []; @@ -1382,9 +1389,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface * * @throws ServiceUnavailableException * @throws PageNotFoundException - * @access private */ - public function getPageAndRootline() + protected function getPageAndRootline() { $this->resolveTranslatedPageId(); if (empty($this->page)) { @@ -1603,9 +1609,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface * @todo Invert boolean return value. Return true if visible. * * @return bool - * @access private */ - public function checkRootlineForIncludeSection(): bool + protected function checkRootlineForIncludeSection(): bool { $c = count($this->rootLine); $removeTheRestFlag = false; @@ -1816,10 +1821,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface /** * Sets sys_page where-clause - * - * @access private */ - public function setSysPageWhereClause() + protected function setSysPageWhereClause() { $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getConnectionForTable('pages') @@ -2063,10 +2066,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface /** * Fetches the integer page id for a page alias. * Looks if ->id is not an integer and if so it will search for a page alias and if found the page uid of that page is stored in $this->id - * - * @access private */ - public function checkAndSetAlias() + protected function checkAndSetAlias() { if ($this->id && !MathUtility::canBeInterpretedAsInteger($this->id)) { $aid = $this->sys_page->getPageIdFromAlias($this->id); @@ -2350,10 +2351,9 @@ class TypoScriptFrontendController implements LoggerAwareInterface * Used to get and later store the cached data. * * @return string MD5 hash of serialized hash base from createHashBase() - * @access private * @see getFromCache(), getLockHash() */ - public function getHash() + protected function getHash() { return md5($this->createHashBase(false)); } @@ -2363,10 +2363,9 @@ class TypoScriptFrontendController implements LoggerAwareInterface * This hash is unique to the above hash, except that it doesn't contain the template information in $this->all. * * @return string MD5 hash - * @access private * @see getFromCache(), getHash() */ - public function getLockHash() + protected function getLockHash() { $lockHash = $this->createHashBase(true); return md5($lockHash); @@ -2745,9 +2744,11 @@ class TypoScriptFrontendController implements LoggerAwareInterface /** * Handle data submission * This is done at this point, because we need the config values + * @deprecated since TYPO3 v9, will be removed in TYPO3 v10. */ public function handleDataSubmission() { + trigger_error('The method "' . __METHOD__ . '" is deprecated since TYPO3 v9, and will be removed in TYPO3 v10. Implement the hooks directly, as they are still executed within TYPO3 via a PSR-15 middleware.', E_USER_DEPRECATED); // Hook for processing data submission to extensions foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'] ?? [] as $className) { $_procObj = GeneralUtility::makeInstance($className); @@ -2815,10 +2816,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface /** * Sets the URL_ID_TOKEN in the internal var, $this->getMethodUrlIdToken * This feature allows sessions to use a GET-parameter instead of a cookie. - * - * @access private */ - public function setUrlIdToken() + protected function setUrlIdToken() { if ($this->config['config']['ftu']) { $this->getMethodUrlIdToken = $GLOBALS['TYPO3_CONF_VARS']['FE']['get_url_id_token']; @@ -4125,9 +4124,12 @@ class TypoScriptFrontendController implements LoggerAwareInterface * @param string $key Is the key in the array, for num-key let the value be empty * @param string $content Is the content if you want any * @see setJS() + * + * @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10.0 */ public function setCSS($key, $content) { + trigger_error('The method "' . __METHOD__ . '" will be removed in TYPO3 v10, use PageRenderer instead to add CSS.', E_USER_DEPRECATED); if ($key) { $this->additionalCSS[$key] = $content; } @@ -4374,9 +4376,11 @@ class TypoScriptFrontendController implements LoggerAwareInterface /** * Converts the $_POST array from metaCharset (page HTML charset from input form) to utf-8 (internal processing) IF the two charsets are different. + * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. */ public function convPOSTCharset() { + trigger_error('The method "' . __METHOD__ . '" is deprecated since TYPO3 v9, and will be removed in TYPO3 v10. A PSR-15 middleware is now taking care of the conversion. It seems you called this method from your own bootstrap code - ensure that the PrepareTypoScriptFrontendRendering middleware is called and you can remove the method call.', E_USER_DEPRECATED); if ($this->metaCharset !== 'utf-8' && is_array($_POST) && !empty($_POST)) { $this->convertCharsetRecursivelyToUtf8($_POST, $this->metaCharset); $GLOBALS['HTTP_POST_VARS'] = $_POST; @@ -4388,6 +4392,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface * * @param mixed $data given by reference (string/array usually) * @param string $fromCharset convert FROM this charset + * @deprecated since TYPO3 v9, will be removed when convPOSTCharset() is removed as well in TYPO3 v10. */ protected function convertCharsetRecursivelyToUtf8(&$data, string $fromCharset) { diff --git a/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php b/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php index 45129c6609cbb1b3adf06c5112202fe094543165..d0e0ded10dfbf9f6b62be807bf7e7c1bdfe20d9d 100644 --- a/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php +++ b/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php @@ -75,11 +75,40 @@ class PrepareTypoScriptFrontendRendering implements MiddlewareInterface $this->timeTracker->pull(); // Convert POST data to utf-8 for internal processing if metaCharset is different - $this->controller->convPOSTCharset(); + if ($this->controller->metaCharset !== 'utf-8' && is_array($_POST) && !empty($_POST)) { + $this->convertCharsetRecursivelyToUtf8($_POST, $this->controller->metaCharset); + $GLOBALS['HTTP_POST_VARS'] = $_POST; + $parsedBody = $request->getParsedBody(); + $this->convertCharsetRecursivelyToUtf8($parsedBody, $this->controller->metaCharset); + $request = $request->withParsedBody($parsedBody); + $GLOBALS['TYPO3_REQUEST'] = $request; + } $this->controller->initializeRedirectUrlHandlers(); - $this->controller->handleDataSubmission(); + + // Hook for processing data submission to extensions + // This is done at this point, because we need the config values + foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'] ?? [] as $className) { + GeneralUtility::makeInstance($className)->checkDataSubmission($this->controller); + } return $handler->handle($request); } + + /** + * Small helper function to convert charsets for arrays to UTF-8 + * + * @param mixed $data given by reference (string/array usually) + * @param string $fromCharset convert FROM this charset + */ + protected function convertCharsetRecursivelyToUtf8(&$data, string $fromCharset) + { + foreach ($data as $key => $value) { + if (is_array($data[$key])) { + $this->convertCharsetRecursivelyToUtf8($data[$key], $fromCharset); + } elseif (is_string($data[$key])) { + $data[$key] = mb_convert_encoding($data[$key], 'utf-8', $fromCharset); + } + } + } } diff --git a/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php b/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php index 5e2db74da160d0c58d6e62f951cc687440e8a3fd..0838cecb9c8d57282dad384daa1707173694c982 100644 --- a/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php +++ b/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php @@ -15,11 +15,17 @@ namespace TYPO3\CMS\Frontend\Middleware; * The TYPO3 project - inspiring people to share! */ +use Doctrine\DBAL\Exception\ConnectionException; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Error\Http\ServiceUnavailableException; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Frontend\Controller\ErrorController; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; /** @@ -31,10 +37,13 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; * * @internal */ -class TypoScriptFrontendInitialization implements MiddlewareInterface +class TypoScriptFrontendInitialization implements MiddlewareInterface, LoggerAwareInterface { + use LoggerAwareTrait; + /** - * Creates an instance of TSFE and sets it as a global variable + * Creates an instance of TSFE and sets it as a global variable, + * also pings the database in order ensure a valid database connection. * * @param ServerRequestInterface $request * @param RequestHandlerInterface $handler @@ -52,7 +61,27 @@ class TypoScriptFrontendInitialization implements MiddlewareInterface null, GeneralUtility::_GP('MP') ); - $GLOBALS['TSFE']->connectToDB(); + + // Set up the database connection and see if the connection can be established + try { + $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages'); + $connection->connect(); + } catch (ConnectionException $exception) { + // Cannot connect to current database + $message = 'Cannot connect to the configured database "' . $connection->getDatabase() . '"'; + $this->logger->emergency($message, ['exception' => $exception]); + try { + return GeneralUtility::makeInstance(ErrorController::class)->unavailableAction($request, $message); + } catch (ServiceUnavailableException $e) { + throw new ServiceUnavailableException($message, 1526013723); + } + } + // Call post processing function for DB connection: + $_params = ['pObj' => &$GLOBALS['TSFE']]; + foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'] ?? [] as $_funcRef) { + GeneralUtility::callUserFunction($_funcRef, $_params, $GLOBALS['TSFE']); + } + return $handler->handle($request); } } diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php index 95eb70c325c844ca0feef5ac966abb7472abf639..e94bd50f86a770414ca0b057969525d75ae896f5 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php @@ -2130,4 +2130,46 @@ return [ 'Deprecation-84725-SysDomainResolvingMovedIntoMiddleware.rst', ], ], + 'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->connectToDB' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst', + ], + ], + 'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->checkAlternativeIdMethods' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst', + ], + ], + 'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->initializeBackendUser' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst', + ], + ], + 'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->handleDataSubmission' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst', + ], + ], + 'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->setCSS' => [ + 'numberOfMandatoryArguments' => 2, + 'maximumNumberOfArguments' => 2, + 'restFiles' => [ + 'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst', + ], + ], + 'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->convPOSTCharset' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst', + ], + ], ]; diff --git a/typo3/sysext/redirects/Classes/Service/RedirectService.php b/typo3/sysext/redirects/Classes/Service/RedirectService.php index 5f0f8b363294c374b4ed0e536710fd0295ef0df6..9593c42f13a724743e8327bf4398e0ade2e490a0 100644 --- a/typo3/sysext/redirects/Classes/Service/RedirectService.php +++ b/typo3/sysext/redirects/Classes/Service/RedirectService.php @@ -246,8 +246,6 @@ class RedirectService implements LoggerAwareInterface GeneralUtility::_GP('id'), GeneralUtility::_GP('type') ); - $GLOBALS['TSFE']->initFEuser(); - $GLOBALS['TSFE']->initializeBackendUser(); $GLOBALS['TSFE']->fetch_the_id(); $GLOBALS['TSFE']->initTemplate(); $GLOBALS['TSFE']->getConfigArray();