From 79e4947911273778234718056be16268b2c1975c Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Fri, 16 Jan 2026 09:25:57 +0100 Subject: [PATCH] [BUGFIX] Load server request from rendering context Since commit 99129162418ae0170e66dfafc4cf55980c60a002 > [BUGFIX] Always fetch ContentObject from request in TYPO3v12+ the current content element is not available anymore in view helpers unless passed manually ("v:content.resource.fal(record: record)"). This leads to e.g. "v:content.resources.fal" not loading images anymore. The current content object is part of the Request object. A new request object is created when a new content element is rendered. This is not $GLOBALS['TYPO3_REQUEST'] - the per-element request object gets passed to each view helper via the rendering context. The rendering context API changed with TYPO3 v13[1], making it necessary to first try the attribute (TYPO3 v13+) and fall back to getRequest() in TYPO3v12. [1] https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/13.3/Deprecation-104684-FluidRenderingContext-getRequest.html Resolves: https://github.com/FluidTYPO3/vhs/issues/1937 --- Classes/Traits/SourceSetViewHelperTrait.php | 2 +- Classes/Utility/ContentObjectFetcher.php | 42 ++++++++++++++++--- .../Content/AbstractContentViewHelper.php | 2 +- .../ViewHelpers/Content/InfoViewHelper.php | 2 +- .../Format/Placeholder/LipsumViewHelper.php | 6 +-- .../Media/Image/AbstractImageViewHelper.php | 2 +- .../ViewHelpers/Media/SourceViewHelper.php | 2 +- .../Page/LanguageMenuViewHelper.php | 2 +- .../ViewHelpers/Render/RequestViewHelper.php | 2 +- .../ViewHelpers/Render/UncacheViewHelper.php | 6 +-- .../Resource/AbstractImageViewHelper.php | 2 +- .../AbstractRecordResourceViewHelper.php | 2 +- .../Classes/DummySourceSetViewHelper.php | 2 + 13 files changed, 53 insertions(+), 21 deletions(-) diff --git a/Classes/Traits/SourceSetViewHelperTrait.php b/Classes/Traits/SourceSetViewHelperTrait.php index e296ae40e..95075c2e3 100644 --- a/Classes/Traits/SourceSetViewHelperTrait.php +++ b/Classes/Traits/SourceSetViewHelperTrait.php @@ -96,7 +96,7 @@ public function getImgResource( ?string $params = null, ?string $crop = null ): array { - $contentObject = ContentObjectFetcher::resolve($this->configurationManager); + $contentObject = ContentObjectFetcher::resolve($this->configurationManager, $this->renderingContext); if ($contentObject === null) { throw new Exception(static::class . ' requires a ContentObjectRenderer, none found', 1737808465); } diff --git a/Classes/Utility/ContentObjectFetcher.php b/Classes/Utility/ContentObjectFetcher.php index 6a08cbf94..198368b9f 100644 --- a/Classes/Utility/ContentObjectFetcher.php +++ b/Classes/Utility/ContentObjectFetcher.php @@ -12,16 +12,17 @@ use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; +use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; class ContentObjectFetcher { - public static function resolve(?ConfigurationManagerInterface $configurationManager = null): ?ContentObjectRenderer - { - $contentObject = null; - $request = ($configurationManager !== null && method_exists($configurationManager, 'getRequest') - ? $configurationManager->getRequest() - : ($GLOBALS['TYPO3_REQUEST'] ?? null)) ?? $GLOBALS['TYPO3_REQUEST'] ?? null; + public static function resolve( + ?ConfigurationManagerInterface $configurationManager = null, + ?RenderingContextInterface $renderingContext = null + ): ?ContentObjectRenderer { + $contentObject = null; + $request = static::getRequest($configurationManager, $renderingContext); if ($request) { $contentObject = static::resolveFromRequest($request); } @@ -45,4 +46,33 @@ protected static function resolveFromRequest(ServerRequestInterface $request): ? $controller = $request->getAttribute('frontend.controller'); return $controller instanceof TypoScriptFrontendController ? $controller->cObj : null; } + + protected static function getRequest( + ?ConfigurationManagerInterface $configurationManager = null, + ?RenderingContextInterface $renderingContext = null + ): ?ServerRequestInterface { + + if ($renderingContext !== null) { + //TYPO3 v13+ + if (method_exists($renderingContext, 'getAttribute') + && method_exists($renderingContext, 'hasAttribute') + && $renderingContext->hasAttribute(ServerRequestInterface::class) + ) { + return $renderingContext->getAttribute(ServerRequestInterface::class); + } + + //TYPO3 v12 + if (method_exists($renderingContext, 'getRequest')) { + return $renderingContext->getRequest(); + } + } + + if ($configurationManager !== null) { + if (method_exists($configurationManager, 'getRequest')) { + return $configurationManager->getRequest(); + } + } + + return $GLOBALS['TYPO3_REQUEST'] ?? null; + } } diff --git a/Classes/ViewHelpers/Content/AbstractContentViewHelper.php b/Classes/ViewHelpers/Content/AbstractContentViewHelper.php index e248317c1..250722717 100644 --- a/Classes/ViewHelpers/Content/AbstractContentViewHelper.php +++ b/Classes/ViewHelpers/Content/AbstractContentViewHelper.php @@ -190,7 +190,7 @@ protected function getPageUid(): int */ protected function getRenderedRecords(array $rows): array { - $contentObject = ContentObjectFetcher::resolve($this->configurationManager); + $contentObject = ContentObjectFetcher::resolve($this->configurationManager, $this->renderingContext); /** @var array $loadRegister */ $loadRegister = $this->arguments['loadRegister']; diff --git a/Classes/ViewHelpers/Content/InfoViewHelper.php b/Classes/ViewHelpers/Content/InfoViewHelper.php index 2b3dfb452..4de2c07ee 100644 --- a/Classes/ViewHelpers/Content/InfoViewHelper.php +++ b/Classes/ViewHelpers/Content/InfoViewHelper.php @@ -72,7 +72,7 @@ public function render() $record = false; if (0 === $contentUid) { - $cObj = ContentObjectFetcher::resolve($this->configurationManager); + $cObj = ContentObjectFetcher::resolve($this->configurationManager, $this->renderingContext); if ($cObj === null) { throw new Exception('v:content.info requires a ContentObjectRenderer, none found', 1737807859); diff --git a/Classes/ViewHelpers/Format/Placeholder/LipsumViewHelper.php b/Classes/ViewHelpers/Format/Placeholder/LipsumViewHelper.php index 7c7a8502d..138029891 100644 --- a/Classes/ViewHelpers/Format/Placeholder/LipsumViewHelper.php +++ b/Classes/ViewHelpers/Format/Placeholder/LipsumViewHelper.php @@ -91,7 +91,7 @@ public static function renderStatic( $lipsum = implode("\n", $paragraphs); if ($arguments['html']) { $tsParserPath = $arguments['parseFuncTSPath'] ? '< ' . $arguments['parseFuncTSPath'] : null; - $lipsum = static::getContentObject()->parseFunc($lipsum, [], (string) $tsParserPath); + $lipsum = static::getContentObject($renderingContext)->parseFunc($lipsum, [], (string) $tsParserPath); } return $lipsum; } @@ -164,12 +164,12 @@ protected static function getDefaultLoremIpsum(): string return $safeLipsum; } - protected static function getContentObject(): ContentObjectRenderer + protected static function getContentObject(RenderingContextInterface $renderingContext): ContentObjectRenderer { /** @var ConfigurationManagerInterface $configurationManager */ $configurationManager = GeneralUtility::makeInstance(ConfigurationManagerInterface::class); /** @var ContentObjectRenderer|null $contentObject */ - $contentObject = ContentObjectFetcher::resolve($configurationManager); + $contentObject = ContentObjectFetcher::resolve($configurationManager, $renderingContext); if ($contentObject === null) { throw new Exception('v:format.placeholder.lipsum requires a ContentObjectRenderer, none found', 1737807859); } diff --git a/Classes/ViewHelpers/Media/Image/AbstractImageViewHelper.php b/Classes/ViewHelpers/Media/Image/AbstractImageViewHelper.php index 7c0c9899f..d4d1aaf10 100644 --- a/Classes/ViewHelpers/Media/Image/AbstractImageViewHelper.php +++ b/Classes/ViewHelpers/Media/Image/AbstractImageViewHelper.php @@ -133,7 +133,7 @@ public function preprocessImage(?string $imageSource = null): void $tsfeBackup = FrontendSimulationUtility::simulateFrontendEnvironment(); - $contentObject = ContentObjectFetcher::resolve($this->configurationManager); + $contentObject = ContentObjectFetcher::resolve($this->configurationManager, $this->renderingContext); if ($contentObject === null) { throw new Exception(static::class . ' requires a ContentObjectRenderer, none found', 1737808465); } diff --git a/Classes/ViewHelpers/Media/SourceViewHelper.php b/Classes/ViewHelpers/Media/SourceViewHelper.php index a9d88d031..b1b50182b 100644 --- a/Classes/ViewHelpers/Media/SourceViewHelper.php +++ b/Classes/ViewHelpers/Media/SourceViewHelper.php @@ -131,7 +131,7 @@ public function render() if (is_string($imageSource) && ContextUtility::isBackend() && '../' === mb_substr($imageSource, 0, 3)) { $imageSource = mb_substr($imageSource, 3); } - $contentObject = ContentObjectFetcher::resolve($this->configurationManager); + $contentObject = ContentObjectFetcher::resolve($this->configurationManager, $this->renderingContext); if ($contentObject === null) { throw new Exception('v:media.source requires a ContentObjectRenderer, none found', 1737807859); } diff --git a/Classes/ViewHelpers/Page/LanguageMenuViewHelper.php b/Classes/ViewHelpers/Page/LanguageMenuViewHelper.php index edc5dac78..f7e9cee47 100644 --- a/Classes/ViewHelpers/Page/LanguageMenuViewHelper.php +++ b/Classes/ViewHelpers/Page/LanguageMenuViewHelper.php @@ -144,7 +144,7 @@ public function render() return ''; } /** @var ContentObjectRenderer|null $contentObject */ - $contentObject = ContentObjectFetcher::resolve($this->configurationManager); + $contentObject = ContentObjectFetcher::resolve($this->configurationManager, $this->renderingContext); if ($contentObject === null) { throw new Exception('v:page.languageMenu requires a ContentObjectRenderer, none found', 1737807859); } diff --git a/Classes/ViewHelpers/Render/RequestViewHelper.php b/Classes/ViewHelpers/Render/RequestViewHelper.php index bc1e190b0..97feb7f51 100644 --- a/Classes/ViewHelpers/Render/RequestViewHelper.php +++ b/Classes/ViewHelpers/Render/RequestViewHelper.php @@ -82,7 +82,7 @@ public static function renderStatic( $pluginName = $arguments['pluginName']; $requestArguments = is_array($arguments['arguments']) ? $arguments['arguments'] : []; $configurationManager = static::getConfigurationManager(); - $contentObjectBackup = ContentObjectFetcher::resolve($configurationManager); + $contentObjectBackup = ContentObjectFetcher::resolve($configurationManager, $renderingContext); $configurationBackup = $configurationManager->getConfiguration( ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK ); diff --git a/Classes/ViewHelpers/Render/UncacheViewHelper.php b/Classes/ViewHelpers/Render/UncacheViewHelper.php index 928798930..8596c8f5a 100644 --- a/Classes/ViewHelpers/Render/UncacheViewHelper.php +++ b/Classes/ViewHelpers/Render/UncacheViewHelper.php @@ -109,7 +109,7 @@ public static function renderStatic( $conf['partialRootPaths'] = $renderingContext->getTemplatePaths()->getPartialRootPaths(); } - $contentObjectRenderer = static::getContentObject(); + $contentObjectRenderer = static::getContentObject($renderingContext); $content = $contentObjectRenderer->cObjGetSingle( 'COA_INT', @@ -121,12 +121,12 @@ public static function renderStatic( return $content; } - protected static function getContentObject(): ContentObjectRenderer + protected static function getContentObject(RenderingContextInterface $renderingContext): ContentObjectRenderer { /** @var ConfigurationManagerInterface $configurationManager */ $configurationManager = GeneralUtility::makeInstance(ConfigurationManagerInterface::class); /** @var ContentObjectRenderer|null $contentObject */ - $contentObject = ContentObjectFetcher::resolve($configurationManager); + $contentObject = ContentObjectFetcher::resolve($configurationManager, $renderingContext); if ($contentObject === null) { throw new Exception('v:render.uncache requires a ContentObjectRenderer, none found', 1737808465); } diff --git a/Classes/ViewHelpers/Resource/AbstractImageViewHelper.php b/Classes/ViewHelpers/Resource/AbstractImageViewHelper.php index 2ce90e8a5..f18a40f08 100644 --- a/Classes/ViewHelpers/Resource/AbstractImageViewHelper.php +++ b/Classes/ViewHelpers/Resource/AbstractImageViewHelper.php @@ -95,7 +95,7 @@ public function preprocessImages(array $files, bool $onlyProperties = false): ?a $tsfeBackup = FrontendSimulationUtility::simulateFrontendEnvironment(); - $contentObject = ContentObjectFetcher::resolve($this->configurationManager); + $contentObject = ContentObjectFetcher::resolve($this->configurationManager, $this->renderingContext); if ($contentObject === null) { throw new Exception(static::class . ' requires a ContentObjectRenderer, none found', 1737807859); } diff --git a/Classes/ViewHelpers/Resource/Record/AbstractRecordResourceViewHelper.php b/Classes/ViewHelpers/Resource/Record/AbstractRecordResourceViewHelper.php index 9e33b4eb2..978dc137b 100644 --- a/Classes/ViewHelpers/Resource/Record/AbstractRecordResourceViewHelper.php +++ b/Classes/ViewHelpers/Resource/Record/AbstractRecordResourceViewHelper.php @@ -167,7 +167,7 @@ public function getRecord(int $id): ?array public function getActiveRecord(): array { - $contentObject = ContentObjectFetcher::resolve($this->configurationManager); + $contentObject = ContentObjectFetcher::resolve($this->configurationManager, $this->renderingContext); if ($contentObject === null) { throw new Exception(static::class . ' requires a ContentObjectRenderer, none found', 1737807859); } diff --git a/Tests/Fixtures/Classes/DummySourceSetViewHelper.php b/Tests/Fixtures/Classes/DummySourceSetViewHelper.php index c1866178e..a69a06571 100644 --- a/Tests/Fixtures/Classes/DummySourceSetViewHelper.php +++ b/Tests/Fixtures/Classes/DummySourceSetViewHelper.php @@ -4,6 +4,7 @@ use FluidTYPO3\Vhs\Traits\SourceSetViewHelperTrait; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; +use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; class DummySourceSetViewHelper { @@ -11,6 +12,7 @@ class DummySourceSetViewHelper public ?ConfigurationManagerInterface $configurationManager = null; public array $arguments = []; + public ?RenderingContextInterface $renderingContext = null; public static function preprocessSourceUri(string $src, array $arguments): string {