From 7159ff8aaeb5a0b771ce013862241cb33727b7df Mon Sep 17 00:00:00 2001 From: Simon Schaufelberger <simonschaufi+typo3@gmail.com> Date: Tue, 14 Sep 2021 19:09:30 +0200 Subject: [PATCH] [BUGFIX] Fix TypoScriptFrontendController initialization in subrequests With the introduction of #94402 error pages are fetched via a sub-request. Manual "page not found" ErrorController invocations within an extbase action then resulted in rendering the originally requested page instead of the defined 404 page. This happened because the PrepareTypoScriptFrontendRendering middleware hold a reference to the outer TSFE instance which contains the original page id. Instead of injecting the stateful TSFE (which is generally discouraged), TSFE in now passed as request attribute. The container will log an according warning message from now on. Resolves: #95174 Releases: master Change-Id: Ieda58e2bef8f08762fcba06b76df03aff7b10d5c Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/71084 Tested-by: core-ci <typo3@b13.com> Tested-by: Simon Gilli <typo3@gilbertsoft.org> Tested-by: Benjamin Franzke <bfr@qbus.de> Reviewed-by: Simon Gilli <typo3@gilbertsoft.org> Reviewed-by: Benjamin Franzke <bfr@qbus.de> --- .../PrepareTypoScriptFrontendRendering.php | 21 +++++++-------- .../ShortcutAndMountPointRedirect.php | 26 +++++++------------ .../TypoScriptFrontendInitialization.php | 3 +++ .../frontend/Configuration/Services.yaml | 7 +++++ 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php b/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php index d724c74acc18..bd382fe3b259 100644 --- a/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php +++ b/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php @@ -33,19 +33,13 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; */ class PrepareTypoScriptFrontendRendering implements MiddlewareInterface { - /** - * @var TypoScriptFrontendController - */ - protected $controller; - /** * @var TimeTracker */ protected $timeTracker; - public function __construct(TypoScriptFrontendController $controller, TimeTracker $timeTracker) + public function __construct(TimeTracker $timeTracker) { - $this->controller = $controller; $this->timeTracker = $timeTracker; } @@ -58,23 +52,26 @@ class PrepareTypoScriptFrontendRendering implements MiddlewareInterface */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { + /** @var TypoScriptFrontendController */ + $controller = $request->getAttribute('frontend.controller'); + // as long as TSFE throws errors with the global object, this needs to be set, but // should be removed later-on once TypoScript Condition Matcher is built with the current request object. $GLOBALS['TYPO3_REQUEST'] = $request; // Get from cache $this->timeTracker->push('Get Page from cache'); // Locks may be acquired here - $this->controller->getFromCache($request); + $controller->getFromCache($request); $this->timeTracker->pull(); // Get config if not already gotten // After this, we should have a valid config-array ready - $this->controller->getConfigArray($request); + $controller->getConfigArray($request); // Convert POST data to utf-8 for internal processing if metaCharset is different - if ($this->controller->metaCharset !== 'utf-8' && $request->getMethod() === 'POST') { + if ($controller->metaCharset !== 'utf-8' && $request->getMethod() === 'POST') { $parsedBody = $request->getParsedBody(); if (is_array($parsedBody) && !empty($parsedBody)) { - $this->convertCharsetRecursivelyToUtf8($parsedBody, $this->controller->metaCharset); + $this->convertCharsetRecursivelyToUtf8($parsedBody, $controller->metaCharset); $request = $request->withParsedBody($parsedBody); } } @@ -86,7 +83,7 @@ class PrepareTypoScriptFrontendRendering implements MiddlewareInterface * However, when some middlewares returns early (e.g. Shortcut and MountPointRedirect, * which both skip inner middlewares), or due to Exceptions, locks still need to be released explicitly. */ - $this->controller->releaseLocks(); + $controller->releaseLocks(); return $response; } diff --git a/typo3/sysext/frontend/Classes/Middleware/ShortcutAndMountPointRedirect.php b/typo3/sysext/frontend/Classes/Middleware/ShortcutAndMountPointRedirect.php index 315c2955e22a..27256e0c37e7 100644 --- a/typo3/sysext/frontend/Classes/Middleware/ShortcutAndMountPointRedirect.php +++ b/typo3/sysext/frontend/Classes/Middleware/ShortcutAndMountPointRedirect.php @@ -35,16 +35,6 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; */ class ShortcutAndMountPointRedirect implements MiddlewareInterface { - /** - * @var TypoScriptFrontendController - */ - private $controller; - - public function __construct(TypoScriptFrontendController $controller) - { - $this->controller = $controller; - } - public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $exposeInformation = $GLOBALS['TYPO3_CONF_VARS']['FE']['exposeRedirectInformation'] ?? false; @@ -63,14 +53,16 @@ class ShortcutAndMountPointRedirect implements MiddlewareInterface } // See if the current page is of doktype "External URL", if so, do a redirect as well. - if (empty($this->controller->config['config']['disablePageExternalUrl'] ?? null) - && PageRepository::DOKTYPE_LINK === (int)$this->controller->page['doktype']) { + /** @var TypoScriptFrontendController */ + $controller = $request->getAttribute('frontend.controller'); + if (empty($controller->config['config']['disablePageExternalUrl'] ?? null) + && PageRepository::DOKTYPE_LINK === (int)$controller->page['doktype']) { $externalUrl = $this->prefixExternalPageUrl( - $this->controller->page['url'], + $controller->page['url'], $request->getAttribute('normalizedParams')->getSiteUrl() ); if (!empty($externalUrl)) { - $message = 'TYPO3 External URL' . ($exposeInformation ? ' at page with ID ' . $this->controller->page['uid'] : ''); + $message = 'TYPO3 External URL' . ($exposeInformation ? ' at page with ID ' . $controller->page['uid'] : ''); return new RedirectResponse( $externalUrl, 303, @@ -84,11 +76,13 @@ class ShortcutAndMountPointRedirect implements MiddlewareInterface protected function getRedirectUri(ServerRequestInterface $request): ?string { - $redirectToUri = $this->controller->getRedirectUriForShortcut($request); + /** @var TypoScriptFrontendController */ + $controller = $request->getAttribute('frontend.controller'); + $redirectToUri = $controller->getRedirectUriForShortcut($request); if ($redirectToUri !== null) { return $redirectToUri; } - return $this->controller->getRedirectUriForMountPoint($request); + return $controller->getRedirectUriForMountPoint($request); } /** diff --git a/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php b/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php index d8319111630b..017e44b794bf 100644 --- a/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php +++ b/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php @@ -96,7 +96,10 @@ class TypoScriptFrontendInitialization implements MiddlewareInterface $controller->determineId($request); + $request = $request->withAttribute('frontend.controller', $controller); // Make TSFE globally available + // @todo deprecate $GLOBALS['TSFE'] once TSFE is retrieved from the + // PSR-7 request attribute frontend.controller throughout TYPO3 core $GLOBALS['TSFE'] = $controller; return $handler->handle($request); } diff --git a/typo3/sysext/frontend/Configuration/Services.yaml b/typo3/sysext/frontend/Configuration/Services.yaml index 8c070b32e3db..dc0765ac62ab 100644 --- a/typo3/sysext/frontend/Configuration/Services.yaml +++ b/typo3/sysext/frontend/Configuration/Services.yaml @@ -7,15 +7,22 @@ services: TYPO3\CMS\Frontend\: resource: '../Classes/*' + # @deprecated since v11, will be removed in v12 - TypoScriptFrontendController is stateful and must be retrieved from the request attribute frontend.controller. TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController: factory: ['TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController', getGlobalInstance] shared: false autoconfigure: false autowire: false + deprecated: + package: 'typo3/cms-core' + version: '11.5' + message: 'Injection of "%service_id%" breaks subrequests. Please use the PSR-7 request attribute "frontend.controller".' TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer: public: true shared: false + arguments: + $typoScriptFrontendController: null TYPO3\CMS\Frontend\ContentObject\Exception\ProductionExceptionHandler: public: true -- GitLab