diff --git a/typo3/sysext/extbase/Classes/Configuration/ConfigurationManager.php b/typo3/sysext/extbase/Classes/Configuration/ConfigurationManager.php index 4c25c4eaee71..5294c210258f 100644 --- a/typo3/sysext/extbase/Classes/Configuration/ConfigurationManager.php +++ b/typo3/sysext/extbase/Classes/Configuration/ConfigurationManager.php @@ -67,6 +67,7 @@ public function getContentObject(): ?ContentObjectRenderer * Note that this is a low level method and only makes sense to be used by Extbase internally. * * @param array $configuration The new configuration + * @internal */ public function setConfiguration(array $configuration = []): void { diff --git a/typo3/sysext/extbase/Classes/Configuration/ConfigurationManagerInterface.php b/typo3/sysext/extbase/Classes/Configuration/ConfigurationManagerInterface.php index 9b1e79a24b00..be87d35b3c4f 100644 --- a/typo3/sysext/extbase/Classes/Configuration/ConfigurationManagerInterface.php +++ b/typo3/sysext/extbase/Classes/Configuration/ConfigurationManagerInterface.php @@ -55,6 +55,7 @@ public function getConfiguration(string $configurationType, ?string $extensionNa * Note that this is a low level method and only makes sense to be used by Extbase internally. * * @param array $configuration The new configuration + * @internal */ public function setConfiguration(array $configuration = []): void; diff --git a/typo3/sysext/extbase/Classes/ContentObject/ExtbasePluginContentObject.php b/typo3/sysext/extbase/Classes/ContentObject/ExtbasePluginContentObject.php index 121866f7743d..088f3291faee 100644 --- a/typo3/sysext/extbase/Classes/ContentObject/ExtbasePluginContentObject.php +++ b/typo3/sysext/extbase/Classes/ContentObject/ExtbasePluginContentObject.php @@ -41,8 +41,8 @@ public function render($conf = []) // Come here only if we are not called from $TSFE->processNonCacheableContentPartsAndSubstituteContentMarkers()! $this->cObj->setUserObjectType(ContentObjectRenderer::OBJECTTYPE_USER); } - $extbaseBootstrap->initialize($conf); - $content = $extbaseBootstrap->handleFrontendRequest($this->request); + $request = $extbaseBootstrap->initialize($conf, $this->request); + $content = $extbaseBootstrap->handleFrontendRequest($request); // Rendering is deferred, as the action should not be cached, we pump this now to TSFE to be executed later-on if ($this->cObj->doConvertToUserIntObject) { $this->cObj->doConvertToUserIntObject = false; diff --git a/typo3/sysext/extbase/Classes/Core/Bootstrap.php b/typo3/sysext/extbase/Classes/Core/Bootstrap.php index 78d97d2a7e3b..aaca576c3aa0 100644 --- a/typo3/sysext/extbase/Classes/Core/Bootstrap.php +++ b/typo3/sysext/extbase/Classes/Core/Bootstrap.php @@ -79,14 +79,14 @@ public function setContentObjectRenderer(ContentObjectRenderer $cObj) /** * Explicitly initializes all necessary Extbase objects by invoking the various initialize* methods. * - * Usually this method is only called from unit tests or other applications which need a more fine grained control over + * Usually this method is only called from unit tests or other applications which need a more fine-grained control over * the initialization and request handling process. Most other applications just call the run() method. * * @param array $configuration The TS configuration array * @throws \RuntimeException * @see run() */ - public function initialize(array $configuration): void + public function initialize(array $configuration, ServerRequestInterface $request): ServerRequestInterface { if (!Environment::isCli()) { if (!isset($configuration['extensionName']) || $configuration['extensionName'] === '') { @@ -96,7 +96,7 @@ public function initialize(array $configuration): void throw new \RuntimeException('Invalid configuration: "pluginName" is not set', 1290623027); } } - $this->initializeConfiguration($configuration); + return $this->initializeConfiguration($configuration, $request); } /** @@ -105,11 +105,16 @@ public function initialize(array $configuration): void * @see initialize() * @internal */ - public function initializeConfiguration(array $configuration): void + public function initializeConfiguration(array $configuration, ServerRequestInterface $request): ServerRequestInterface { - $this->cObj ??= $this->container->get(ContentObjectRenderer::class); + if ($this->cObj === null) { + $this->cObj = $this->container->get(ContentObjectRenderer::class); + $request = $request->withAttribute('currentContentObject', $this->cObj); + } + $this->cObj->setRequest($request); $this->configurationManager->setContentObject($this->cObj); $this->configurationManager->setConfiguration($configuration); + return $request; // todo: Shouldn't the configuration manager object – which is a singleton – be stateless? // todo: At this point we give the configuration manager a state, while we could directly pass the // todo: configuration (i.e. controllerName, actionName and such), directly to the request @@ -133,7 +138,7 @@ public function initializeConfiguration(array $configuration): void */ public function run(string $content, array $configuration, ServerRequestInterface $request): string { - $this->initialize($configuration); + $request = $this->initialize($configuration, $request); return $this->handleFrontendRequest($request); } @@ -204,7 +209,6 @@ public function handleFrontendRequest(ServerRequestInterface $request): string * * Creates an Extbase Request, dispatches it and then returns the Response * - * @param ServerRequestInterface $request * @internal */ public function handleBackendRequest(ServerRequestInterface $request): ResponseInterface @@ -216,7 +220,7 @@ public function handleBackendRequest(ServerRequestInterface $request): ResponseI 'pluginName' => $module?->getIdentifier(), ]; - $this->initialize($configuration); + $request = $this->initialize($configuration, $request); $extbaseRequest = $this->extbaseRequestBuilder->build($request); $response = $this->dispatcher->dispatch($extbaseRequest); $this->resetSingletons(); diff --git a/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php b/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php index 29f72e9974c9..b21216b825b9 100644 --- a/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php +++ b/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php @@ -40,22 +40,11 @@ */ class UriBuilder { - /** - * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface - */ - protected $configurationManager; + protected ConfigurationManagerInterface $configurationManager; - /** - * @var \TYPO3\CMS\Extbase\Service\ExtensionService - */ - protected $extensionService; + protected ExtensionService $extensionService; - /** - * An instance of \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer - * - * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer - */ - protected $contentObject; + protected ContentObjectRenderer $contentObject; protected ?RequestInterface $request = null; @@ -147,16 +136,6 @@ public function injectExtensionService(ExtensionService $extensionService): void $this->extensionService = $extensionService; } - /** - * Life-cycle method that is called by the DI container as soon as this object is completely built - * @internal only to be used within Extbase, not part of TYPO3 Core API. - */ - public function initializeObject(): void - { - $this->contentObject = $this->configurationManager->getContentObject() - ?? GeneralUtility::makeInstance(ContentObjectRenderer::class); - } - /** * Sets the current request * @@ -165,6 +144,15 @@ public function initializeObject(): void public function setRequest(RequestInterface $request): UriBuilder { $this->request = $request; + $contentObject = $request->getAttribute('currentContentObject'); + if ($contentObject === null) { + // @todo: Review this. This should never be the case since extbase + // bootstrap adds 'currentContentObject' to request. When this + // if() kicks in, it most likely indicates an "out of scope" usage. + $contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class); + $contentObject->setRequest($request->withAttribute('currentContentObject', $contentObject)); + } + $this->contentObject = $contentObject; return $this; } @@ -478,7 +466,7 @@ public function reset(): UriBuilder /* * $this->request MUST NOT be reset here because the request is actually a hard dependency and not part * of the internal state of this object. - * todo: consider making the request a constructor dependency or get rid of it's usage + * todo: make the request a constructor dependency */ return $this; } diff --git a/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php b/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php index a1743d08c9aa..e4ad44a0e1f0 100644 --- a/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php @@ -73,7 +73,6 @@ protected function setUp(): void $this->subject->setRequest($this->mockRequest); $this->subject->injectConfigurationManager($this->createMock(ConfigurationManagerInterface::class)); $this->subject->injectExtensionService($this->mockExtensionService); - $this->subject->initializeObject(); $this->subject->_set('contentObject', $this->mockContentObject); $requestContextFactory = new RequestContextFactory(new BackendEntryPointResolver()); $router = new Router($requestContextFactory); diff --git a/typo3/sysext/extbase/Tests/UnitDeprecated/Mvc/Web/Routing/UriBuilderTest.php b/typo3/sysext/extbase/Tests/UnitDeprecated/Mvc/Web/Routing/UriBuilderTest.php index 9492914d0012..d50addfc1993 100644 --- a/typo3/sysext/extbase/Tests/UnitDeprecated/Mvc/Web/Routing/UriBuilderTest.php +++ b/typo3/sysext/extbase/Tests/UnitDeprecated/Mvc/Web/Routing/UriBuilderTest.php @@ -41,7 +41,6 @@ protected function setUp(): void $this->subject->setRequest($this->createMock(Request::class)); $this->subject->injectConfigurationManager($this->createMock(ConfigurationManagerInterface::class)); $this->subject->injectExtensionService($this->mockExtensionService); - $this->subject->initializeObject(); $this->subject->_set('contentObject', $this->createMock(ContentObjectRenderer::class)); } diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/CObjectViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/CObjectViewHelper.php index e814d8f25b90..4e3e2bb4a7eb 100644 --- a/typo3/sysext/fluid/Classes/ViewHelpers/CObjectViewHelper.php +++ b/typo3/sysext/fluid/Classes/ViewHelpers/CObjectViewHelper.php @@ -128,7 +128,7 @@ public static function renderStatic(array $arguments, \Closure $renderChildrenCl /** @var RenderingContext $renderingContext */ $request = $renderingContext->getRequest(); $contentObjectRenderer = self::getContentObjectRenderer($request); - $contentObjectRenderer->setRequest($request); + $contentObjectRenderer->setRequest($request->withAttribute('currentContentObject', $contentObjectRenderer)); $tsfeBackup = null; if (!isset($GLOBALS['TSFE']) || !($GLOBALS['TSFE'] instanceof TypoScriptFrontendController)) { $tsfeBackup = self::simulateFrontendEnvironment(); diff --git a/typo3/sysext/form/Classes/Controller/FormFrontendController.php b/typo3/sysext/form/Classes/Controller/FormFrontendController.php index 42f56fcddf6c..a331ad2f4862 100644 --- a/typo3/sysext/form/Classes/Controller/FormFrontendController.php +++ b/typo3/sysext/form/Classes/Controller/FormFrontendController.php @@ -69,7 +69,7 @@ public function renderAction(): ResponseInterface $formDefinition['persistenceIdentifier'] = $this->settings['persistenceIdentifier']; $formDefinition = $this->overrideByFlexFormSettings($formDefinition); $formDefinition = ArrayUtility::setValueByPath($formDefinition, 'renderingOptions._originalIdentifier', $formDefinition['identifier'], '.'); - $formDefinition['identifier'] .= '-' . $this->configurationManager->getContentObject()->data['uid']; + $formDefinition['identifier'] .= '-' . $this->request->getAttribute('currentContentObject')->data['uid']; } $this->view->assign('formConfiguration', $formDefinition); @@ -93,7 +93,7 @@ public function performAction(): ResponseInterface */ protected function overrideByFlexFormSettings(array $formDefinition): array { - $flexFormData = GeneralUtility::xml2array($this->configurationManager->getContentObject()->data['pi_flexform'] ?? ''); + $flexFormData = GeneralUtility::xml2array($this->request->getAttribute('currentContentObject')->data['pi_flexform'] ?? ''); if (!is_array($flexFormData)) { return $formDefinition; diff --git a/typo3/sysext/form/Classes/Domain/Finishers/ConfirmationFinisher.php b/typo3/sysext/form/Classes/Domain/Finishers/ConfirmationFinisher.php index 4731641a98ad..9092fb5f36b2 100644 --- a/typo3/sysext/form/Classes/Domain/Finishers/ConfirmationFinisher.php +++ b/typo3/sysext/form/Classes/Domain/Finishers/ConfirmationFinisher.php @@ -57,20 +57,11 @@ class ConfirmationFinisher extends AbstractFinisher 'typoscriptObjectPath' => 'lib.tx_form.contentElementRendering', ]; - /** - * @var array - */ - protected $typoScriptSetup = []; + protected array $typoScriptSetup = []; - /** - * @var ConfigurationManagerInterface - */ - protected $configurationManager; + protected ConfigurationManagerInterface $configurationManager; - /** - * @var ContentObjectRenderer - */ - protected $contentObjectRenderer; + protected ContentObjectRenderer $contentObjectRenderer; public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager) { @@ -109,7 +100,8 @@ protected function executeInternal() } $setup = $setup[$segment . '.']; } - $this->contentObjectRenderer->start([$contentElementUid], ''); + $this->contentObjectRenderer->setRequest($this->finisherContext->getRequest()); + $this->contentObjectRenderer->start([$contentElementUid]); $this->contentObjectRenderer->setCurrentVal((string)$contentElementUid); $message = $this->contentObjectRenderer->cObjGetSingle($setup[$lastSegment], $setup[$lastSegment . '.'], $lastSegment); } else { diff --git a/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php b/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php index 77c3508b59c3..4d0bf895210f 100644 --- a/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php +++ b/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php @@ -504,7 +504,7 @@ protected function isPostRequest(): bool */ protected function isRenderedCached(): bool { - $contentObject = $this->configurationManager->getContentObject(); + $contentObject = $this->request->getAttribute('currentContentObject'); return $contentObject === null ? true // @todo this does not work when rendering a cached `FLUIDTEMPLATE` (not nested in `COA_INT`) @@ -1079,8 +1079,8 @@ protected function getConditionResolver(): Resolver } $contentObjectData = []; - if ($this->configurationManager->getContentObject() instanceof ContentObjectRenderer) { - $contentObjectData = $this->configurationManager->getContentObject()->data; + if ($this->request->getAttribute('currentContentObject') instanceof ContentObjectRenderer) { + $contentObjectData = $this->request->getAttribute('currentContentObject')->data; } return GeneralUtility::makeInstance( diff --git a/typo3/sysext/form/Tests/Functional/RequestHandling/Fixtures/Extensions/form_caching_tests/Classes/Controller/FormCachingTestsController.php b/typo3/sysext/form/Tests/Functional/RequestHandling/Fixtures/Extensions/form_caching_tests/Classes/Controller/FormCachingTestsController.php index a5f24ffae29a..f613d10fc4af 100644 --- a/typo3/sysext/form/Tests/Functional/RequestHandling/Fixtures/Extensions/form_caching_tests/Classes/Controller/FormCachingTestsController.php +++ b/typo3/sysext/form/Tests/Functional/RequestHandling/Fixtures/Extensions/form_caching_tests/Classes/Controller/FormCachingTestsController.php @@ -24,14 +24,14 @@ class FormCachingTestsController extends ActionController { public function someRenderAction(): ResponseInterface { - $this->view->assign('formIdentifier', $this->request->getPluginName() . '-' . $this->configurationManager->getContentObject()->data['uid']); + $this->view->assign('formIdentifier', $this->request->getPluginName() . '-' . $this->request->getAttribute('currentContentObject')->data['uid']); $this->view->assign('pluginName', $this->request->getPluginName()); return $this->htmlResponse(); } public function somePerformAction(): ResponseInterface { - $this->view->assign('formIdentifier', $this->request->getPluginName() . '-' . $this->configurationManager->getContentObject()->data['uid']); + $this->view->assign('formIdentifier', $this->request->getPluginName() . '-' . $this->request->getAttribute('currentContentObject')->data['uid']); $this->view->assign('pluginName', $this->request->getPluginName()); return $this->htmlResponse(); } diff --git a/typo3/sysext/form/Tests/Unit/Controller/FormFrontendControllerTest.php b/typo3/sysext/form/Tests/Unit/Controller/FormFrontendControllerTest.php index bc6f8837f6c0..939b2372ba35 100644 --- a/typo3/sysext/form/Tests/Unit/Controller/FormFrontendControllerTest.php +++ b/typo3/sysext/form/Tests/Unit/Controller/FormFrontendControllerTest.php @@ -21,9 +21,11 @@ use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Cache\Frontend\NullFrontend; use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools; +use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Tests\Unit\Fixtures\EventDispatcher\MockEventDispatcher; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Configuration\FrontendConfigurationManager; +use TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters; +use TYPO3\CMS\Extbase\Mvc\Request; use TYPO3\CMS\Form\Controller\FormFrontendController; use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; @@ -65,7 +67,12 @@ public function overrideByFlexFormSettingsReturnsNoOverriddenConfigurationIfFlex ); $flexFormTools = new FlexFormTools(new MockEventDispatcher()); + $request = (new ServerRequest())->withAttribute('extbase', new ExtbaseRequestParameters()); + $request = (new Request($request)); $contentObject = new ContentObjectRenderer(); + $request = $request->withAttribute('currentContentObject', $contentObject); + $contentObject->setRequest($request); + $mockController->_set('request', $request); $contentObject->data = [ 'pi_flexform' => $flexFormTools->flexArray2Xml([ 'data' => [ @@ -99,13 +106,6 @@ public function overrideByFlexFormSettingsReturnsNoOverriddenConfigurationIfFlex ]), ]; - $frontendConfigurationManager = $this->createMock(FrontendConfigurationManager::class); - $frontendConfigurationManager - ->method('getContentObject') - ->willReturn($contentObject); - - $mockController->_set('configurationManager', $frontendConfigurationManager); - $configurationServiceMock->method('getPrototypeConfiguration')->with(self::anything())->willReturn([ 'finishersDefinition' => [ 'EmailToReceiver' => [ @@ -196,7 +196,12 @@ public function overrideByFlexFormSettingsReturnsOverriddenConfigurationIfFlexfo ); $flexFormTools = new FlexFormTools(new MockEventDispatcher()); + $request = (new ServerRequest())->withAttribute('extbase', new ExtbaseRequestParameters()); + $request = (new Request($request)); $contentObject = new ContentObjectRenderer(); + $request = $request->withAttribute('currentContentObject', $contentObject); + $contentObject->setRequest($request); + $mockController->_set('request', $request); $contentObject->data = [ 'pi_flexform' => $flexFormTools->flexArray2Xml([ 'data' => [ @@ -230,13 +235,6 @@ public function overrideByFlexFormSettingsReturnsOverriddenConfigurationIfFlexfo ]), ]; - $frontendConfigurationManager = $this->createMock(FrontendConfigurationManager::class); - $frontendConfigurationManager - ->method('getContentObject') - ->willReturn($contentObject); - - $mockController->_set('configurationManager', $frontendConfigurationManager); - $configurationServiceMock->method('getPrototypeConfiguration')->with(self::anything())->willReturn([ 'finishersDefinition' => [ 'EmailToReceiver' => [ @@ -354,7 +352,12 @@ public function overrideByFlexFormSettingsReturnsNotOverriddenConfigurationKeyIf GeneralUtility::addInstance(FlexFormTools::class, new FlexFormTools($eventDispatcher)); $flexFormTools = new FlexFormTools(new MockEventDispatcher()); + $request = (new ServerRequest())->withAttribute('extbase', new ExtbaseRequestParameters()); + $request = (new Request($request)); $contentObject = new ContentObjectRenderer(); + $request = $request->withAttribute('currentContentObject', $contentObject); + $contentObject->setRequest($request); + $mockController->_set('request', $request); $contentObject->data = [ 'pi_flexform' => $flexFormTools->flexArray2Xml([ 'data' => [ @@ -388,13 +391,6 @@ public function overrideByFlexFormSettingsReturnsNotOverriddenConfigurationKeyIf ]), ]; - $frontendConfigurationManager = $this->createMock(FrontendConfigurationManager::class); - $frontendConfigurationManager - ->method('getContentObject') - ->willReturn($contentObject); - - $mockController->_set('configurationManager', $frontendConfigurationManager); - $configurationServiceMock->method('getPrototypeConfiguration')->with(self::anything())->willReturn([ 'finishersDefinition' => [ 'EmailToReceiver' => [ diff --git a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php index 76126deefcb7..bb361a8a44db 100644 --- a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php +++ b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php @@ -468,8 +468,7 @@ public function __wakeup() $this->container = GeneralUtility::getContainer(); // We do not derive $this->request from globals here. The request is expected to be injected - // using setRequest() after deserialization or with start(). - // (A fallback to $GLOBALS['TYPO3_REQUEST'] is available in getRequest() for BC) + // using setRequest(), a fallback to $GLOBALS['TYPO3_REQUEST'] is available in getRequest() for BC. } /** @@ -5847,16 +5846,39 @@ protected function shallDebug(): bool return !empty($GLOBALS['TYPO3_CONF_VARS']['FE']['debug']); } + /** + * @todo: This getRequest() handling is pretty messy. We created a loop from + * request to 'currentContentObject' back to request with this. + * v13 should be refactored, probably with this patch chain: + * * Remove fallback to $GLOBALS['TYPO3_REQUEST'] to force consumers + * actually setting the request using setRequest(). + * * Protect this method. + * * Get rid of public TSFE->cObj (the "page" instance of cObj). + * * Avoid TSFE as constructor argument and make ContentObjectRenderer + * free for DI, for instance to get the container injected. + * * When getRequest() is protected or private, setRequest() should + * *remove* the currentContentObject attribute again, to prevent + * the object loop. This will work, since local getRequest() could + * use $this when needed. + * + * @internal This method will be set to protected with TYPO3 v13. + */ public function getRequest(): ServerRequestInterface { if ($this->request instanceof ServerRequestInterface) { + // Note attribute 'currentContentObject' has been set by setRequest() already. return $this->request; } - if (isset($GLOBALS['TYPO3_REQUEST']) && $GLOBALS['TYPO3_REQUEST'] instanceof ServerRequestInterface) { - return $GLOBALS['TYPO3_REQUEST']; + if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface) { + // @todo: We may want to deprecate this fallback and force consumers + // to setRequest() after object instantiation / unserialization instead. + return $GLOBALS['TYPO3_REQUEST']->withAttribute('currentContentObject', $this); } - throw new ContentRenderingException('PSR-7 request is missing in ContentObjectRenderer. Inject with start(), setRequest() or provide via $GLOBALS[\'TYPO3_REQUEST\'].', 1607172972); + throw new ContentRenderingException( + 'PSR-7 request is missing in ContentObjectRenderer. Inject with start(), setRequest() or provide via $GLOBALS[\'TYPO3_REQUEST\'].', + 1607172972 + ); } } diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php index 8dc8c92f6dcc..e009ba0bc96e 100644 --- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php +++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php @@ -2276,7 +2276,7 @@ protected function processNonCacheableContentPartsAndSubstituteContentMarkers(ar $nonCacheableContent = ''; $contentObjectRendererForNonCacheable = unserialize($nonCacheableData[$nonCacheableKey]['cObj']); /* @var ContentObjectRenderer $contentObjectRendererForNonCacheable */ - $contentObjectRendererForNonCacheable->setRequest($request); + $contentObjectRendererForNonCacheable->setRequest($request->withAttribute('currentContentObject', $contentObjectRendererForNonCacheable)); switch ($nonCacheableData[$nonCacheableKey]['type']) { case 'COA': $nonCacheableContent = $contentObjectRendererForNonCacheable->cObjGetSingle('COA', $nonCacheableData[$nonCacheableKey]['conf']); diff --git a/typo3/sysext/frontend/Classes/DataProcessing/FlexFormProcessor.php b/typo3/sysext/frontend/Classes/DataProcessing/FlexFormProcessor.php index 830019b1f047..f9082c60373d 100644 --- a/typo3/sysext/frontend/Classes/DataProcessing/FlexFormProcessor.php +++ b/typo3/sysext/frontend/Classes/DataProcessing/FlexFormProcessor.php @@ -17,6 +17,7 @@ namespace TYPO3\CMS\Frontend\DataProcessing; +use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Core\Service\FlexFormService; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor; @@ -79,7 +80,9 @@ public function process( $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'flexFormData'); if (isset($processorConfiguration['dataProcessing.']) && is_array($processorConfiguration['dataProcessing.'])) { - $flexFormData = $this->processAdditionalDataProcessors($flexFormData, $processorConfiguration); + // @todo: It looks as if data processors should retrieve the current request from the outside, + // this would avoid $cObj->getRequest() here. + $flexFormData = $this->processAdditionalDataProcessors($flexFormData, $processorConfiguration, $cObj->getRequest()); } $processedData[$targetVariableName] = $flexFormData; @@ -90,10 +93,11 @@ public function process( /** * Recursively process sub processors of a data processor */ - public function processAdditionalDataProcessors(array $data, array $processorConfiguration): array + protected function processAdditionalDataProcessors(array $data, array $processorConfiguration, ServerRequestInterface $request): array { $contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); - $contentObjectRenderer->start([$data]); + $contentObjectRenderer->setRequest($request->withAttribute('currentContentObject', $contentObjectRenderer)); + $contentObjectRenderer->start([$data], ''); return GeneralUtility::makeInstance(ContentDataProcessor::class)->process( $contentObjectRenderer, $processorConfiguration, diff --git a/typo3/sysext/frontend/Tests/Unit/ContentObject/CaseContentObjectTest.php b/typo3/sysext/frontend/Tests/Unit/ContentObject/CaseContentObjectTest.php index 7041a175a936..95b64783ef13 100644 --- a/typo3/sysext/frontend/Tests/Unit/ContentObject/CaseContentObjectTest.php +++ b/typo3/sysext/frontend/Tests/Unit/ContentObject/CaseContentObjectTest.php @@ -43,6 +43,7 @@ protected function setUp(): void $request = new ServerRequest(); $contentObjectRenderer = new ContentObjectRenderer($tsfe); + $request = $request->withAttribute('currentContentObject', $contentObjectRenderer); $contentObjectRenderer->setRequest($request); $cObjectFactoryMock = $this->getMockBuilder(ContentObjectFactory::class)->disableOriginalConstructor()->getMock(); @@ -51,7 +52,7 @@ protected function setUp(): void $caseContentObject->setContentObjectRenderer($contentObjectRenderer); $textContentObject = new TextContentObject(); - $textContentObject->setRequest(new ServerRequest()); + $textContentObject->setRequest($request); $textContentObject->setContentObjectRenderer($contentObjectRenderer); $cObjectFactoryMock->method('getContentObject')->willReturnMap([ diff --git a/typo3/sysext/frontend/Tests/Unit/DataProcessing/FlexFormProcessorTest.php b/typo3/sysext/frontend/Tests/Unit/DataProcessing/FlexFormProcessorTest.php index 0c1f75771bb0..6763db6861a6 100644 --- a/typo3/sysext/frontend/Tests/Unit/DataProcessing/FlexFormProcessorTest.php +++ b/typo3/sysext/frontend/Tests/Unit/DataProcessing/FlexFormProcessorTest.php @@ -18,6 +18,7 @@ namespace TYPO3\CMS\Frontend\Tests\Unit\DataProcessing; use PHPUnit\Framework\MockObject\MockObject; +use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Service\FlexFormService; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor; @@ -204,6 +205,9 @@ public function subDataProcessorIsResolved(): void ], ]; $this->contentObjectRendererMock->expects(self::once())->method('start')->with([$convertedFlexFormData]); + $request = new ServerRequest(); + $request = $request->withAttribute('currentContentObject', $this->contentObjectRendererMock); + $this->contentObjectRendererMock->method('getRequest')->willReturn($request); $contentDataProcessorMock = $this->getMockBuilder(ContentDataProcessor::class)->disableOriginalConstructor()->getMock(); $renderedDataFromProcessors = [