Skip to content
Snippets Groups Projects
Commit 77fd49ff authored by Christian Kuhn's avatar Christian Kuhn
Browse files

[!!!][TASK] Avoid Extbase Request init in StandaloneView

The StandaloneView has a very unfortunate flaw in
__construct(): It creates an Extbase Request by
default without asking and adds it to RenderingContext.

The patch removes this default initialization.

StandaloneView is typically *not* used within Extbase
context: Extbase by default uses TemplateView.

As such, creating an Extbase Request in StandaloneView
by default is bad, since it switches context to Extbase
when it shouldn't. This can make ViewHelpers behave differently
and in general adds trouble and cross dependencies.

As a side effect, some ViewHelpers will now actively refuse
to work in non-extabse context and throw exceptions. Most
notably, f:form and f:form.* throw exceptions when used
in a FLUIDTEMPLATE content object. We *might* be able to
relax this during further v12 development, though.

It is possible we later add a factory to the Fluid views
in general that takes care of setting a request to the view.
This however needs some streamlining in Fluid standalone
library first and requires some further decisions.

Change-Id: I1cf7b8a7baf5280d67f887c55b7b6493942c136a
Resolves: #98377
Related: #96473
Releases: main
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/75779


Tested-by: default avatarBenni Mack <benni@typo3.org>
Tested-by: default avatarStefan Bürk <stefan@buerk.tech>
Tested-by: default avatarcore-ci <typo3@b13.com>
Tested-by: default avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: default avatarBenni Mack <benni@typo3.org>
Reviewed-by: default avatarStefan Bürk <stefan@buerk.tech>
Reviewed-by: default avatarChristian Kuhn <lolli@schwarzbu.ch>
parent f375dac3
Branches
Tags
No related merge requests found
.. include:: /Includes.rst.txt
.. _breaking-98377-1663607123:
==================================================================================
Breaking: #98377 - Fluid StandaloneView does not create an Extbase Request anymore
==================================================================================
See :issue:`98377`
Description
===========
In our efforts to further speed up, streamline and separate Fluid from Extbase,
the :php:`\TYPO3\CMS\Fluid\View\StandaloneView` has been changed to no longer
create a Extbase Request anymore.
StandaloneView is typically not used in Extbase context, creating an Extbase
Request at this point was a very unfortunate architectural flaw leading to a
not wanted context switch.
Not having an Extbase Request within StandaloneView anymore can have impact on
behavior of some Fluid ViewHelpers.
Impact
======
Common usages of StandaloneView are a frontend related :typoscript:`FLUIDTEMPLATE`
content object, plus various usages in non-Extbase extensions like rendering eMails
or similar. Within :typoscript:`FLUIDTEMPLATE`, the current non-Extbase PSR-7
ServerRequest is actively set to StandaloneView, custom extension usages may need
to :php:`$view->setRequest($request)` explicitly.
Some ViewHelpers that rely on Extbase functionality throw exceptions when
a Request is not set, or if the Request is not an Extbase Request. Those will
refuse to work for instance when used in a template triggered by a :typoscript:`FLUIDTEMPLATE`
content object.
Most notably, all :html:`f:form` ViewHelpers are affected of this, plus
eventually custom ViewHelpers that access Extbase specific :php:`Request`
methods.
Affected installations
======================
Instances with extensions using StandaloneView in their code may need attention,
and frontend rendering using :typoscript:`FLUIDTEMPLATE` content objects may need
adaptions if Extbase-only ViewHelpers like :html:`f:form` are used.
Migration
=========
Avoiding :html:`f:form` in non-Extbase context
----------------------------------------------
The :html:`f:form` ViewHelpers are Extbase specific: They especially take care of
handling Extbase internal fields like :html:`__referrer` and similar. The casual solution
is to switch these usages away from those ViewHelpers, and use the HTML counterparts
directly, for instance using :html:`<input ...>` instead of :html:`<f:form.input ...>`.
Custom StandaloneView code
--------------------------
Extensions that instantiate :php:`StandaloneView` may want to :php:`$view->setRequest($request)`
to hand over the current request to the view, since the request is no longer initialized
automatically. This is needed for ViewHelpers that rely on :php:`$renderingContext->getRequest()`.
Custom ViewHelpers
------------------
Custom ViewHelpers used in :php:`StandaloneView` that call methods from Extbase
:php:`TYPO3\CMS\Extbase\Mvc\Request` which are not part of
:php:`Psr\Http\Message\ServerRequestInterface` will throw fatal PHP errors.
Possible solutions:
* Create an Extbase Request within a controller and :php:`setRequest()` it to the
view instance as quick solution.
* Properly boot Extbase using the Extbase Bootstrap to have a fully initialized
Extbase Request in the View.
* Avoid using Extbase specific methods within the ViewHelper by checking if the
incoming request implements Extbase :php:`TYPO3\CMS\Extbase\Mvc\RequestInterface`.
This allows creating "hybrid" ViewHelpers that work in both contexts.
* Avoid using Extbase specific methods within the ViewHelper by fetching data from
the given :php:`Psr\Http\Message\ServerRequestInterface` Request, or it's attached
core attributes.
.. index:: Fluid, PHP-API, NotScanned, ext:fluid
......@@ -18,13 +18,8 @@ declare(strict_types=1);
namespace TYPO3\CMS\Fluid\View;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Http\ServerRequest;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters;
use TYPO3\CMS\Extbase\Mvc\Request;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextFactory;
use TYPO3Fluid\Fluid\View\Exception\InvalidTemplateResourceException;
/**
......@@ -33,21 +28,6 @@ use TYPO3Fluid\Fluid\View\Exception\InvalidTemplateResourceException;
*/
class StandaloneView extends AbstractTemplateView
{
public function __construct()
{
$renderingContext = GeneralUtility::makeInstance(RenderingContextFactory::class)->create();
// @todo: This is very unfortunate. This creates an extbase request by default. Standalone
// usage is typically *not* extbase context. Controllers that want to get rid of this
// have to ->setRequest($myServerRequestInterface), or even ->setRequest(null) after
// object construction to get rid of an extbase request again.
$request = $GLOBALS['TYPO3_REQUEST'] ?? new ServerRequest();
if ($request->getAttribute('extbase') === null) {
$request = $request->withAttribute('extbase', new ExtbaseRequestParameters());
}
$renderingContext->setRequest(GeneralUtility::makeInstance(Request::class, $request));
parent::__construct($renderingContext);
}
/**
* Sets the format of the current request (default format is "html")
*
......
......@@ -121,6 +121,7 @@ class FluidTemplateContentObject extends AbstractContentObject
protected function initializeStandaloneViewInstance()
{
$this->view = GeneralUtility::makeInstance(StandaloneView::class);
$this->view->setRequest($this->request);
}
/**
......
......@@ -173,6 +173,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$contentObjectRenderer = $this->prophesize(ContentObjectRenderer::class);
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplateRootPaths(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->setTemplate(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
......@@ -208,6 +209,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
$standAloneView->renderSection(Argument::cetera())->willReturn('');
......@@ -242,6 +244,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
$standAloneView->renderSection(Argument::cetera())->willReturn('');
......@@ -277,6 +280,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplateRootPaths(Argument::cetera())->shouldBeCalled();
$standAloneView->assignMultiple(Argument::cetera())->shouldBeCalled();
$standAloneView->renderSection(Argument::cetera())->willReturn('');
......@@ -316,6 +320,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplateRootPaths(Argument::cetera())->shouldBeCalled();
$standAloneView->assignMultiple(Argument::cetera())->shouldBeCalled();
$standAloneView->renderSection(Argument::cetera())->willReturn('');
......@@ -346,6 +351,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -380,6 +386,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -418,6 +425,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setLayoutRootPaths(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
......@@ -472,6 +480,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -506,6 +515,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -539,6 +549,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$contentObjectRenderer = $this->prophesize(ContentObjectRenderer::class);
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setPartialRootPaths(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
......@@ -574,6 +585,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setPartialRootPaths(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
......@@ -622,6 +634,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$contentObjectRenderer->stdWrapValue('partialRootPath', $configuration)->willReturn(Environment::getPublicPath() . '/main');
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -663,6 +676,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -694,6 +708,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -732,6 +747,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename('')->shouldBeCalled();
$standAloneView->assignMultiple(['data' => [], 'current' => null])->shouldBeCalled();
$standAloneView->renderSection(Argument::cetera())->willReturn('');
......@@ -766,6 +782,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -804,6 +821,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename('')->shouldBeCalled();
$standAloneView->assignMultiple(['data' => [], 'current' => null])->shouldBeCalled();
$standAloneView->renderSection(Argument::cetera())->willReturn('');
......@@ -840,6 +858,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -877,6 +896,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename('')->shouldBeCalled();
$standAloneView->assignMultiple(['data' => [], 'current' => null])->shouldBeCalled();
$standAloneView->renderSection(Argument::cetera())->willReturn('');
......@@ -912,6 +932,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -949,6 +970,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($request->reveal());
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename('')->shouldBeCalled();
$standAloneView->assignMultiple(['data' => [], 'current' => null])->shouldBeCalled();
$standAloneView->renderSection(Argument::cetera())->willReturn('');
......@@ -985,6 +1007,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -1031,6 +1054,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
GeneralUtility::addInstance(TypoScriptService::class, $typoScriptServiceMock);
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -1108,6 +1132,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setRequest($this->request);
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->assignMultiple(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
......@@ -1147,6 +1172,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename(Argument::cetera())->willReturn(new ReturnTypeNode('void'));
$standAloneView->render(Argument::cetera())->willReturn('');
$standAloneView->renderSection(Argument::cetera())->willReturn('');
......@@ -1238,6 +1264,7 @@ class FluidTemplateContentObjectTest extends UnitTestCase
$subject->setContentObjectRenderer($contentObjectRenderer->reveal());
$standAloneView = $this->prophesize(StandaloneView::class);
$standAloneView->setRequest(Argument::cetera());
$standAloneView->setTemplatePathAndFilename('')->shouldBeCalled();
$standAloneView->assignMultiple(['data' => [], 'current' => null])->shouldbeCalled();
$standAloneView->renderSection(Argument::cetera())->willReturn('');
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment