From 0235c1360569feb036358978662951b979b890cc Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Thu, 10 May 2018 22:41:08 +0200
Subject: [PATCH] [TASK] Deprecate various TSFE methods

A lot of functionality has been migrated from
TypoScriptFrontendController into middlewares
- functionality that has now no direct influence
in the so-called controller of the frontend (TSFE)
anymore. The respective methods are never called
anymore from TYPO3 Core, and extensions that
bootstrap their own frontend should ensure that
the respective Middlewares are boot up and called,
e.g. via custom stacks or just by setting up
the "frontend" middleware stack.

The following methods are now deprecated:
- connectToDB()
- checkAlternativeIdMethods()
- initializeBackendUser()
- handleDataSubmission()
- setCSS()
- convPOSTCharset()

Additionally, there are some methods in TSFE
which have been marked as "internal" but had the
PHP visibility "public", which were now
migrated to "protected".

- getPageAndRootline()
- checkRootlineForIncludeSection()
- setSysPageWhereClause()
- checkAndSetAlias()
- getHash()
- getLockHash()
- setUrlIdToken()

Resolves: #84965
Releases: master
Change-Id: Ia8e29268189179061c09a204bb7275d231fea0dc
Reviewed-on: https://review.typo3.org/56916
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Cristian Buja <cristian.buja@intera.it>
Tested-by: Cristian Buja <cristian.buja@intera.it>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
---
 ...ousTypoScriptFrontendControllerMethods.rst | 65 +++++++++++++++++++
 .../TypoScriptFrontendController.php          | 39 ++++++-----
 .../PrepareTypoScriptFrontendRendering.php    | 33 +++++++++-
 .../TypoScriptFrontendInitialization.php      | 35 +++++++++-
 .../Php/MethodCallMatcher.php                 | 42 ++++++++++++
 .../Classes/Service/RedirectService.php       |  2 -
 6 files changed, 192 insertions(+), 24 deletions(-)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst

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 000000000000..8e4442562f4d
--- /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 3bb1bdcad244..ae43cf962182 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 45129c6609cb..d0e0ded10dfb 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 5e2db74da160..0838cecb9c8d 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 95eb70c325c8..e94bd50f86a7 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 5f0f8b363294..9593c42f13a7 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();
-- 
GitLab