Commit d70a4798 authored by Christian Kuhn's avatar Christian Kuhn
Browse files

[!!!][TASK] Cleanup and harden Extbase Request

The Extbase Request has been turned into a
PSR-7 decorator in v11. Most interface changes
have not been enforced in v11 though, since that
would have been breaking at this point in the
release cycle.

With v12, we can now activate the RequestInterface
changes. The patch does this, and migrates the "setX()"
that violate PSR-7 request immutability towards
their 'withX()' counterparts which create new
objects.

The patch adds quite a bit of syntactic sugar to
consuming classes: core phpstan is significantly more
happy and extbase extension devs benefit from
improved type hints and interface coverage.

Change-Id: I111be724fd4e5d6dc9b1305efa31b7c90952730e
Resolves: #98370
Related: #94428
Releases: main
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/75749


Tested-by: Lina Wolf's avatarLina Wolf <112@linawolf.de>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Lina Wolf's avatarLina Wolf <112@linawolf.de>
Reviewed-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
parent 79459bb7
......@@ -1780,11 +1780,6 @@ parameters:
count: 1
path: ../../typo3/sysext/extbase/Classes/Configuration/AbstractConfigurationManager.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerActionName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/extbase/Classes/Core/Bootstrap.php
-
message: "#^Instanceof between TYPO3\\\\CMS\\\\Extbase\\\\Domain\\\\Model\\\\Category\\|null and TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\Generic\\\\LazyLoadingProxy will always evaluate to false\\.$#"
count: 1
......@@ -1820,11 +1815,6 @@ parameters:
count: 2
path: ../../typo3/sysext/extbase/Classes/Mvc/Controller/MvcPropertyMappingConfigurationService.php
-
message: "#^Parameter \\#1 \\$currentRequest of static method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Dispatcher\\:\\:buildRequestFromCurrentRequestAndForwardResponse\\(\\) expects TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Request, TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface given\\.$#"
count: 1
path: ../../typo3/sysext/extbase/Classes/Mvc/Dispatcher.php
-
message: "#^Method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Request\\:\\:withoutAttribute\\(\\) should return static\\(TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Request\\) but returns Psr\\\\Http\\\\Message\\\\ServerRequestInterface\\.$#"
count: 1
......@@ -1835,11 +1825,6 @@ parameters:
count: 13
path: ../../typo3/sysext/extbase/Classes/Mvc/Request.php
-
message: "#^Property TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Web\\\\RequestBuilder\\:\\:\\$allowedControllerAliases is never read, only written\\.$#"
count: 1
path: ../../typo3/sysext/extbase/Classes/Mvc/Web/RequestBuilder.php
-
message: "#^Dead catch \\- TYPO3\\\\CMS\\\\Backend\\\\Routing\\\\Exception\\\\ResourceNotFoundException is never thrown in the try block\\.$#"
count: 1
......@@ -2045,11 +2030,6 @@ parameters:
count: 1
path: ../../typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Model/TtContent.php
-
message: "#^Parameter \\#1 \\$currentRequest of static method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Dispatcher\\:\\:buildRequestFromCurrentRequestAndForwardResponse\\(\\) expects TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Request, TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface given\\.$#"
count: 1
path: ../../typo3/sysext/extbase/Tests/Functional/Mvc/Controller/ActionControllerArgumentTest.php
-
message: "#^Variable \\$expectations on left side of \\?\\? always exists and is not nullable\\.$#"
count: 1
......@@ -2060,16 +2040,6 @@ parameters:
count: 1
path: ../../typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Extension/action_controller_test/Classes/Controller/TestController.php
-
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertSame\\(\\) with 0 and array\\|string will always evaluate to false\\.$#"
count: 1
path: ../../typo3/sysext/extbase/Tests/Functional/Mvc/Web/RequestBuilderTest.php
-
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertSame\\(\\) with 98174 and array\\|string will always evaluate to false\\.$#"
count: 1
path: ../../typo3/sysext/extbase/Tests/Functional/Mvc/Web/RequestBuilderTest.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\QueryInterface\\:\\:between\\(\\)\\.$#"
count: 1
......@@ -2355,16 +2325,6 @@ parameters:
count: 5
path: ../../typo3/sysext/extbase/Tests/Unit/Utility/ExtensionUtilityTest.php
-
message: "#^Parameter \\#2 \\$lowestVersion of method TYPO3\\\\CMS\\\\Extensionmanager\\\\Domain\\\\Repository\\\\ExtensionRepository\\:\\:findByVersionRangeAndExtensionKeyOrderedByVersion\\(\\) expects int, array\\|string given\\.$#"
count: 1
path: ../../typo3/sysext/extensionmanager/Classes/Controller/DownloadController.php
-
message: "#^Parameter \\#3 \\$highestVersion of method TYPO3\\\\CMS\\\\Extensionmanager\\\\Domain\\\\Repository\\\\ExtensionRepository\\:\\:findByVersionRangeAndExtensionKeyOrderedByVersion\\(\\) expects int, array\\|string given\\.$#"
count: 1
path: ../../typo3/sysext/extensionmanager/Classes/Controller/DownloadController.php
-
message: "#^Parameter \\#2 \\$messageTitle of method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Controller\\\\ActionController\\:\\:addFlashMessage\\(\\) expects string, int given\\.$#"
count: 1
......@@ -2470,36 +2430,11 @@ parameters:
count: 2
path: ../../typo3/sysext/filelist/Classes/Controller/FileListController.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface&TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerActionName\\(\\)\\.$#"
count: 2
path: ../../typo3/sysext/fluid/Classes/Core/Rendering/RenderingContext.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface&TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerName\\(\\)\\.$#"
count: 2
path: ../../typo3/sysext/fluid/Classes/Core/Rendering/RenderingContext.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface&TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:setControllerActionName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/Core/Rendering/RenderingContext.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface&TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:setControllerName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/Core/Rendering/RenderingContext.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface\\:\\:getFormat\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/View/StandaloneView.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface\\:\\:setFormat\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/View/StandaloneView.php
-
message: "#^Method TYPO3\\\\CMS\\\\Fluid\\\\View\\\\TemplatePaths\\:\\:ensureAbsolutePath\\(\\) should return string but returns array\\.$#"
count: 1
......@@ -2510,11 +2445,6 @@ parameters:
count: 2
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Be/Menus/ActionMenuItemViewHelper.php
-
message: "#^Parameter \\#1 \\$request of method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Web\\\\Routing\\\\UriBuilder\\:\\:setRequest\\(\\) expects TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Request, TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface given\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Be/Menus/ActionMenuItemViewHelper.php
-
message: "#^Method TYPO3\\\\CMS\\\\Fluid\\\\ViewHelpers\\\\Be\\\\Menus\\\\ActionMenuViewHelper\\:\\:compile\\(\\) should return string but returns null\\.$#"
count: 1
......@@ -2525,11 +2455,6 @@ parameters:
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Be/PageRendererViewHelper.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerExtensionKey\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Be/PageRendererViewHelper.php
-
message: "#^Parameter \\#1 \\$namespace of method TYPO3\\\\CMS\\\\Core\\\\Page\\\\PageRenderer\\:\\:addInlineSettingArray\\(\\) expects string, null given\\.$#"
count: 1
......@@ -2545,31 +2470,11 @@ parameters:
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/FlashMessagesViewHelper.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerExtensionName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/FlashMessagesViewHelper.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getPluginName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/FlashMessagesViewHelper.php
-
message: "#^Call to an undefined method TYPO3Fluid\\\\Fluid\\\\Core\\\\Rendering\\\\RenderingContextInterface\\:\\:getRequest\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Form/AbstractFormFieldViewHelper.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getOriginalRequest\\(\\)\\.$#"
count: 2
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Form/AbstractFormFieldViewHelper.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getOriginalRequestMappingResults\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Form/AbstractFormFieldViewHelper.php
-
message: "#^Call to an undefined method TYPO3Fluid\\\\Fluid\\\\Core\\\\Rendering\\\\RenderingContextInterface\\:\\:getRequest\\(\\)\\.$#"
count: 1
......@@ -2580,21 +2485,6 @@ parameters:
count: 4
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/FormViewHelper.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerActionName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/FormViewHelper.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerExtensionName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/FormViewHelper.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/FormViewHelper.php
-
message: "#^Call to function is_int\\(\\) with mixed will always evaluate to false\\.$#"
count: 1
......@@ -2620,11 +2510,6 @@ parameters:
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Link/ActionViewHelper.php
-
message: "#^Parameter \\#1 \\$request of method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Web\\\\Routing\\\\UriBuilder\\:\\:setRequest\\(\\) expects TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Request, TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface given\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Link/ActionViewHelper.php
-
message: "#^Call to an undefined method TYPO3Fluid\\\\Fluid\\\\Core\\\\Rendering\\\\RenderingContextInterface\\:\\:getRequest\\(\\)\\.$#"
count: 2
......@@ -2635,11 +2520,6 @@ parameters:
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Link/PageViewHelper.php
-
message: "#^Parameter \\#1 \\$request of method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Web\\\\Routing\\\\UriBuilder\\:\\:setRequest\\(\\) expects TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Request, TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface given\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Link/PageViewHelper.php
-
message: "#^Parameter \\#2 \\$attributeValue of method TYPO3Fluid\\\\Fluid\\\\Core\\\\ViewHelper\\\\TagBuilder\\:\\:addAttribute\\(\\) expects array\\|string\\|Traversable\\|null, TYPO3\\\\CMS\\\\Core\\\\Imaging\\\\ImageManipulation\\\\Area given\\.$#"
count: 1
......@@ -2650,41 +2530,16 @@ parameters:
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Transform/HtmlViewHelper.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerExtensionName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php
-
message: "#^Call to an undefined method TYPO3Fluid\\\\Fluid\\\\Core\\\\Rendering\\\\RenderingContextInterface\\:\\:getRequest\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Uri/ActionViewHelper.php
-
message: "#^Parameter \\#1 \\$request of method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Web\\\\Routing\\\\UriBuilder\\:\\:setRequest\\(\\) expects TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Request, TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface given\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Uri/ActionViewHelper.php
-
message: "#^Call to an undefined method TYPO3Fluid\\\\Fluid\\\\Core\\\\Rendering\\\\RenderingContextInterface\\:\\:getRequest\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Uri/PageViewHelper.php
-
message: "#^Parameter \\#1 \\$request of method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Web\\\\Routing\\\\UriBuilder\\:\\:setRequest\\(\\) expects TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\Request, TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface given\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Uri/PageViewHelper.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface&TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerExtensionKey\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Uri/ResourceViewHelper.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:getControllerExtensionKey\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/fluid/Classes/ViewHelpers/Uri/ResourceViewHelper.php
-
message: "#^Call to an undefined method Psr\\\\Container\\\\ContainerInterface\\:\\:set\\(\\)\\.$#"
count: 4
......@@ -2705,11 +2560,6 @@ parameters:
count: 1
path: ../../typo3/sysext/fluid_styled_content/Classes/ViewHelpers/Link/ClickEnlargeViewHelper.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface\\:\\:getArguments\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/form/Classes/Controller/FormEditorController.php
-
message: "#^Call to an undefined method TYPO3Fluid\\\\Fluid\\\\Core\\\\Rendering\\\\RenderingContextInterface\\:\\:setRequest\\(\\)\\.$#"
count: 1
......@@ -3030,31 +2880,6 @@ parameters:
count: 5
path: ../../typo3/sysext/frontend/Classes/ContentObject/FilesContentObject.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface\\:\\:setControllerActionName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/frontend/Classes/ContentObject/FluidTemplateContentObject.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface\\:\\:setControllerExtensionName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/frontend/Classes/ContentObject/FluidTemplateContentObject.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface\\:\\:setControllerName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/frontend/Classes/ContentObject/FluidTemplateContentObject.php
-
message: "#^Call to an undefined method Psr\\\\Http\\\\Message\\\\ServerRequestInterface\\:\\:setPluginName\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/frontend/Classes/ContentObject/FluidTemplateContentObject.php
-
message: "#^Call to an undefined method TYPO3Fluid\\\\Fluid\\\\Core\\\\Rendering\\\\RenderingContextInterface\\:\\:setRequest\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/frontend/Classes/ContentObject/FluidTemplateContentObject.php
-
message: "#^Property TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\ContentObjectRenderer\\:\\:\\$currentValKey \\(string\\) on left side of \\?\\? is not nullable\\.$#"
count: 1
......@@ -3495,11 +3320,6 @@ parameters:
count: 1
path: ../../typo3/sysext/impexp/Tests/Functional/ImportExportTest.php
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Extbase\\\\Mvc\\\\RequestInterface\\:\\:setArguments\\(\\)\\.$#"
count: 1
path: ../../typo3/sysext/indexed_search/Classes/Controller/AdministrationController.php
-
message: "#^Elseif condition is always true\\.$#"
count: 1
......
......@@ -88,7 +88,7 @@ class BackendUserController extends ActionController
&& $moduleData->has('defaultAction')
&& in_array((string)$moduleData->get('defaultAction'), ['index', 'groups', 'online'])
) {
$request->setControllerActionName((string)$moduleData->get('defaultAction'));
$request = $request->withControllerActionName((string)$moduleData->get('defaultAction'));
}
return parent::processRequest($request);
}
......
.. include:: /Includes.rst.txt
.. _breaking-98370-1663513316:
========================================================
Breaking: #98370 - Extbase Request cleanup and hardening
========================================================
See :issue:`98370`
Description
===========
Extbase :php:`\TYPO3\CMS\Extbase\Mvc\Request` has been turned into a decorator of the
PSR-7 :php:`ServerRequestInterface` :doc:`with core v11 <../11.3/Feature-94428-ExtbaseRequestImplementsServerRequestInterface>`:
Extbase based extensions work with the PSR-7 core Request, Extbase
specific Request state is attached as an attribute to the PSR-7 Request.
Most of these extbase specific attribute properties are now available in core v12
by activating the according decorator methods in :php:`\TYPO3\CMS\Extbase\Mvc\RequestInterface`,
which is implemented by :php:`\TYPO3\CMS\Extbase\Mvc\Request`. The :php:`RequestInterface` now
also properly extends PSR-7 :php:`ServerRequestInterface` and is type-hinted within
the Extbase framework.
PSR-7 interfaces rely on object immutability: A created Request object is never changed, instead
a new object is created and returned when changed. The old fashioned Extbase Request violated this with
various :php:`setXY()` methods. These have been removed, and this is the part that is considered
breaking for consuming extensions.
Impact
======
Extbase based extensions using static code analyzers like phpstan in CI
should benefit from improved scanner results and can further harden their
codebase.
Affected installations
======================
Extensions that actively manipulate the given Extbase :php:`Request` using setter methods
will trigger fatal PHP "method does not exist" errors.
Migration
=========
It is relatively seldom that Extbase extensions need to actively manipulate the Extbase
Request since most of that is handled by the Extbase Framework internally for consuming
extensions.
Extensions that use the :php:`setXY()` methods for whatever reasons have to
change them to their :php:`withXY()` counterparts, though: Nearly all "withers" are now
declared as part of :php:`RequestInterface` and already exist in v11, "setters" can be
migrated quite easily.
From an Extbase Framework API point of view, extension should *only* rely on :php:`RequestInterface`
methods: The second level :php:`ExtbaseRequestParameters` attribute is considered
:php:`@internal` and extensions shouldn't work with it directly. There are just a couple
of methods used by Extbase Framework based on direct :php:`ExtbaseRequestParameters` manipulation,
most of them are related to the action argument validation and action forwarding behavior of Extbase,
which Extbase extensions in general shouldn't need to deal with themselves.
When changing from "setters" to "withers", the important key change is that calling a
:php:`withXY()` method *does not* manipulate the existing request, but *returns a new*
instance instead. In practice, if the previous Request object has been set to some other
client object beforehand, and if a new Request is created using a :php:`withXY()`
method, those client objects may need to be updated with the new object. A typical use-case
is :php:`$this-view` in a controller class, which may now need :php:`$this->view->setRequest($myNewRequest)`
to receive the new :php:`Request` to work on, and it's most likely also a good idea to update
:php:`$this->request = $myNewRequest` as well.
.. index:: PHP-API, NotScanned, ext:extbase
......@@ -32,7 +32,7 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Creates a request and dispatches it to the controller which was specified
* by TS Setup, flexForm and returns the content.
* by TS Setup and returns the content.
*
* This class is the main entry point for extbase extensions.
*/
......@@ -120,7 +120,7 @@ class Bootstrap
}
/**
* Runs the the Extbase Framework by resolving an appropriate Request Handler and passing control to it.
* Runs the Extbase Framework by resolving an appropriate Request Handler and passing control to it.
* If the Framework is not initialized yet, it will be initialized.
*
* This is usually used in Frontend plugins.
......
......@@ -36,6 +36,7 @@ use TYPO3\CMS\Extbase\Mvc\Controller\Exception\RequiredArgumentMissingException;
use TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException;
use TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException;
use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchActionException;
use TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters;
use TYPO3\CMS\Extbase\Mvc\Request;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
use TYPO3\CMS\Extbase\Mvc\View\GenericViewResolver;
......@@ -130,11 +131,8 @@ abstract class ActionController implements ControllerInterface
/**
* The current request.
*
* @var Request
* @todo v12: Change @var to RequestInterface, when RequestInterface extends ServerRequestInterface
*/
protected $request;
protected RequestInterface $request;
/**
* @var \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder
......@@ -674,7 +672,9 @@ abstract class ActionController implements ControllerInterface
*/
protected function forwardToReferringRequest(): ?ResponseInterface
{
$referringRequestArguments = $this->request->getInternalArguments()['__referrer'] ?? null;
/** @var ExtbaseRequestParameters $extbaseRequestParameters */
$extbaseRequestParameters = $this->request->getAttribute('extbase');
$referringRequestArguments = $extbaseRequestParameters->getInternalArgument('__referrer') ?? null;
if (is_string($referringRequestArguments['@request'] ?? null)) {
$referrerArray = json_decode(
$this->hashService->validateAndStripHmac($referringRequestArguments['@request']),
......
......@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Extbase\Mvc\Controller;
use TYPO3\CMS\Core\Error\Http\BadRequestException;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters;
use TYPO3\CMS\Extbase\Mvc\Request;
use TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration;
use TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter;
......@@ -131,7 +132,9 @@ class MvcPropertyMappingConfigurationService implements SingletonInterface
*/
public function initializePropertyMappingConfigurationFromRequest(Request $request, Arguments $controllerArguments)
{
$trustedPropertiesToken = $request->getInternalArgument('__trustedProperties');
/** @var ExtbaseRequestParameters $extbaseRequestParameters */
$extbaseRequestParameters = $request->getAttribute('extbase');
$trustedPropertiesToken = $extbaseRequestParameters->getInternalArgument('__trustedProperties');
if (!is_string($trustedPropertiesToken)) {
return;
}
......
......@@ -104,28 +104,22 @@ class Dispatcher
* @internal only to be used within Extbase, not part of TYPO3 Core API.
* @todo: make this a private method again as soon as the tests, that fake the dispatching of requests, are refactored.
*/
public static function buildRequestFromCurrentRequestAndForwardResponse(Request $currentRequest, ForwardResponse $forwardResponse): Request
public static function buildRequestFromCurrentRequestAndForwardResponse(RequestInterface $currentRequest, ForwardResponse $forwardResponse): RequestInterface
{
$extbaseAttribute = clone $currentRequest->getAttribute('extbase');
$request = $currentRequest->withAttribute('extbase', $extbaseAttribute);
$request->setControllerActionName($forwardResponse->getActionName());
$request = $currentRequest->withControllerActionName($forwardResponse->getActionName());
if ($forwardResponse->getControllerName() !== null) {
$request->setControllerName($forwardResponse->getControllerName());
$request = $request->withControllerName($forwardResponse->getControllerName());
}
if ($forwardResponse->getExtensionName() !== null) {
$request->setControllerExtensionName($forwardResponse->getExtensionName());
$request = $request->withControllerExtensionName($forwardResponse->getExtensionName());
}
if ($forwardResponse->getArguments() !== null) {
$request->setArguments($forwardResponse->getArguments());
$request = $request->withArguments($forwardResponse->getArguments());
}
$request->setOriginalRequest($currentRequest);
$request->setOriginalRequestMappingResults($forwardResponse->getArgumentsValidationResult());
return $request;
/** @var ExtbaseRequestParameters $extbaseRequestParameters */
$extbaseRequestParameters = clone $request->getAttribute('extbase');
$extbaseRequestParameters->setOriginalRequest($currentRequest);
$extbaseRequestParameters->setOriginalRequestMappingResults($forwardResponse->getArgumentsValidationResult());
return $request->withAttribute('extbase', $extbaseRequestParameters);
}
}
......@@ -95,7 +95,7 @@ class ExtbaseRequestParameters
/**
* If this request is a forward because of an error, the original request gets filled.
*/
protected ?Request $originalRequest = null;
protected ?RequestInterface $originalRequest = null;
/**
* If the request is a forward because of an error, these mapping results get filled here.
......@@ -126,11 +126,9 @@ class ExtbaseRequestParameters
return $this;
}
public function setPluginName(?string $pluginName = null): self
public function setPluginName(string $pluginName): self
{
if ($pluginName !== null) {
$this->pluginName = $pluginName;
}
$this->pluginName = $pluginName;
return $this;
}
......@@ -139,11 +137,9 @@ class ExtbaseRequestParameters
return $this->pluginName;
}
public function setControllerExtensionName(?string $controllerExtensionName = null): self
public function setControllerExtensionName(string $controllerExtensionName): self
{
if ($controllerExtensionName !== null) {
$this->controllerExtensionName = $controllerExtensionName;
}
$this->controllerExtensionName = $controllerExtensionName;
return $this;
}
......@@ -165,13 +161,11 @@ class ExtbaseRequestParameters
return $this;
}
public function setControllerName(?string $controllerName = null): self
public function setControllerName(string $controllerName): self
{
if ($controllerName !== null) {
$this->controllerName = $controllerName;
// There might be no Controller Class, for example for Fluid Templates.
$this->controllerObjectName = $this->controllerAliasToClassNameMapping[$controllerName] ?? '';
}
$this->controllerName = $controllerName;
// There might be no Controller Class, for example for Fluid Templates.
$this->controllerObjectName = $this->controllerAliasToClassNameMapping[$controllerName] ?? '';
return $this;
}
......@@ -183,14 +177,12 @@ class ExtbaseRequestParameters
/**
* @throws InvalidActionNameException if the action name is not valid
*/
public function setControllerActionName(?string $actionName = null): self
public function setControllerActionName(string $actionName): self
{
if ($actionName[0] !== strtolower($actionName[0]) && $actionName !== null) {
if ($actionName[0] !== strtolower($actionName[0])) {
throw new InvalidActionNameException('The action name must start with a lower case letter, "' . $actionName . '" does not match this criteria.', 1218473352);
}
if ($actionName !== null) {
$this->controllerActionName = $actionName;
}
$this->controllerActionName = $actionName;
return $this;
}
......@@ -219,7 +211,7 @@ class ExtbaseRequestParameters
* @param mixed $value The new value
* @throws InvalidArgumentNameException
*/
public function setArgument(string $argumentName, $value): self
public function setArgument(string $argumentName, mixed $value): self
{
if ($argumentName === '') {
throw new InvalidArgumentNameException('Invalid argument name.', 1210858767);
......@@ -237,6 +229,7 @@ class ExtbaseRequestParameters
/**
* Sets the whole arguments array and therefore replaces any arguments which existed before.
*
* @param array<string, mixed> $arguments
* @throws InvalidArgumentNameException
*/
public function setArguments(array $arguments): self
......@@ -256,10 +249,10 @@ class ExtbaseRequestParameters
/**
* Returns the value of the specified argument.
*
* @return string|array Value of the argument
* @return mixed Value of the argument
* @throws NoSuchArgumentException if such an argument does not exist
*/
public function getArgument(string $argumentName)
public function getArgument(string $argumentName): mixed
{
if (!isset($this->arguments[$argumentName])) {
throw new NoSuchArgumentException('An argument "' . $argumentName . '" does not exist for this request.', 1176558158);
......@@ -289,14 +282,15 @@ class ExtbaseRequestParameters
/**
* Returns the original request. Filled only if a property mapping error occurred.
*/
public function getOriginalRequest(): ?Request