From 3bddf0968f758711847be5a39d1aeaee792c3bd5 Mon Sep 17 00:00:00 2001
From: Claus Due <claus@namelesscoder.net>
Date: Fri, 31 Jan 2020 20:44:11 +0100
Subject: [PATCH] [TASK] Check methods in RenderingContext before calling

Checks for method presence before attempting to call
them, and exposes getControllerName/getControllerAction
for public usage.

Release: master, 9.5, 8.7
Resolves: #90284
Change-Id: I29b850ccb2f535c1f708af32a61e50c04386d9b7
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/63111
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Manuel Selbach <manuel_selbach@yahoo.de>
Tested-by: Kevin Appelt <kevin.appelt@icloud.com>
Tested-by: Claus Due <claus@phpmind.net>
Reviewed-by: Manuel Selbach <manuel_selbach@yahoo.de>
Reviewed-by: Kevin Appelt <kevin.appelt@icloud.com>
Reviewed-by: Claus Due <claus@phpmind.net>
---
 .../Core/Rendering/RenderingContext.php       | 94 ++++++++++++++-----
 .../Core/Rendering/RenderingContextTest.php   |  8 +-
 2 files changed, 77 insertions(+), 25 deletions(-)

diff --git a/typo3/sysext/fluid/Classes/Core/Rendering/RenderingContext.php b/typo3/sysext/fluid/Classes/Core/Rendering/RenderingContext.php
index 12577e91e8d4..d2365d181214 100644
--- a/typo3/sysext/fluid/Classes/Core/Rendering/RenderingContext.php
+++ b/typo3/sysext/fluid/Classes/Core/Rendering/RenderingContext.php
@@ -41,6 +41,16 @@ class RenderingContext extends \TYPO3Fluid\Fluid\Core\Rendering\RenderingContext
      */
     protected $controllerContext;
 
+    /**
+     * @var string
+     */
+    protected $controllerName = 'Default';
+
+    /**
+     * @var string
+     */
+    protected $controllerAction = 'Default';
+
     /**
      * @param \TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperVariableContainer $viewHelperVariableContainer
      */
@@ -62,26 +72,46 @@ class RenderingContext extends \TYPO3Fluid\Fluid\Core\Rendering\RenderingContext
         } else {
             // Reproduced partial initialisation from parent::__construct; minus the custom
             // implementations we attach below.
-            $this->setTemplateParser(new TemplateParser());
-            $this->setTemplateCompiler(new TemplateCompiler());
-            $this->setViewHelperInvoker(new ViewHelperInvoker());
+            $this->setTemplateParser(new TemplateParser($this));
+            if (method_exists($this, 'setTemplateCompiler')) {
+                $this->setTemplateCompiler(new TemplateCompiler());
+            }
+            if (method_exists($this, 'setViewHelperInvoker')) {
+                $this->setViewHelperInvoker(new ViewHelperInvoker());
+            }
             $this->setViewHelperVariableContainer(new ViewHelperVariableContainer());
             $this->setVariableProvider(new StandardVariableProvider());
         }
 
         $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
-        $this->setTemplateProcessors(array_map([$objectManager, 'get'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['preProcessors']));
+        if (method_exists($this, 'setTemplateProcessors')) {
+            $this->setTemplateProcessors(array_map([$objectManager, 'get'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['preProcessors']));
+        }
         $this->setExpressionNodeTypes($GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['expressionNodeTypes']);
         $this->setTemplatePaths($objectManager->get(TemplatePaths::class));
         $this->setViewHelperResolver($objectManager->get(ViewHelperResolver::class));
 
-        /** @var FluidTemplateCache $cache */
-        $cache = $objectManager->get(CacheManager::class)->getCache('fluid_template');
-        if (is_a($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['fluid_template']['frontend'], FluidTemplateCache::class, true)) {
-            $this->setCache($cache);
+        if (method_exists($this, 'setCache')) {
+            /** @var FluidTemplateCache $cache */
+            $cache = $objectManager->get(CacheManager::class)->getCache('fluid_template');
+            if (is_a($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['fluid_template']['frontend'], FluidTemplateCache::class, true)) {
+                $this->setCache($cache);
+            }
         }
     }
 
+    /**
+     * Alternative to buildParserConfiguration, called only in Fluid 3.0
+     *
+     * @return Configuration
+     */
+    public function getParserConfiguration(): Configuration
+    {
+        $parserConfiguration = parent::getParserConfiguration();
+        $this->addInterceptorsToParserConfiguration($GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['interceptors'], $parserConfiguration);
+        return $parserConfiguration;
+    }
+
     /**
      * Build parser configuration
      *
@@ -91,17 +121,19 @@ class RenderingContext extends \TYPO3Fluid\Fluid\Core\Rendering\RenderingContext
     public function buildParserConfiguration()
     {
         $parserConfiguration = parent::buildParserConfiguration();
-        if (!empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['interceptors'])) {
-            foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['interceptors'] as $className) {
-                $interceptor = GeneralUtility::makeInstance($className);
-                if (!$interceptor instanceof InterceptorInterface) {
-                    throw new \InvalidArgumentException('Interceptor "' . $className . '" needs to implement ' . InterceptorInterface::class . '.', 1462869795);
-                }
-                $parserConfiguration->addInterceptor($interceptor);
+        $this->addInterceptorsToParserConfiguration($GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['interceptors'], $parserConfiguration);
+        return $parserConfiguration;
+    }
+
+    protected function addInterceptorsToParserConfiguration(iterable $interceptors, Configuration $parserConfiguration): void
+    {
+        foreach ($interceptors as $className) {
+            $interceptor = GeneralUtility::makeInstance($className);
+            if (!$interceptor instanceof InterceptorInterface) {
+                throw new \InvalidArgumentException('Interceptor "' . $className . '" needs to implement ' . InterceptorInterface::class . '.', 1462869795);
             }
+            $parserConfiguration->addInterceptor($interceptor);
         }
-
-        return $parserConfiguration;
     }
 
     /**
@@ -123,8 +155,10 @@ class RenderingContext extends \TYPO3Fluid\Fluid\Core\Rendering\RenderingContext
         if ($dotPosition !== false) {
             $action = substr($action, 0, $dotPosition);
         }
-        parent::setControllerAction($action);
-        $this->controllerContext->getRequest()->setControllerActionName(lcfirst($action));
+        $this->controllerAction = $action;
+        if ($this->controllerContext) {
+            $this->controllerContext->getRequest()->setControllerActionName(lcfirst($action));
+        }
     }
 
     /**
@@ -133,8 +167,26 @@ class RenderingContext extends \TYPO3Fluid\Fluid\Core\Rendering\RenderingContext
      */
     public function setControllerName($controllerName)
     {
-        parent::setControllerName($controllerName);
-        $this->controllerContext->getRequest()->setControllerName($controllerName);
+        $this->controllerName = $controllerName;
+        if ($this->controllerContext) {
+            $this->controllerContext->getRequest()->setControllerName($controllerName);
+        }
+    }
+
+    /**
+     * @return string
+     */
+    public function getControllerName()
+    {
+        return $this->controllerContext ? $this->controllerContext->getRequest()->getControllerName() : $this->controllerName;
+    }
+
+    /**
+     * @return string
+     */
+    public function getControllerAction()
+    {
+        return $this->controllerContext ? $this->controllerContext->getRequest()->getControllerActionName() : $this->controllerAction;
     }
 
     /**
diff --git a/typo3/sysext/fluid/Tests/Unit/Core/Rendering/RenderingContextTest.php b/typo3/sysext/fluid/Tests/Unit/Core/Rendering/RenderingContextTest.php
index 3b457ecc444a..f3d5d4ac51cb 100644
--- a/typo3/sysext/fluid/Tests/Unit/Core/Rendering/RenderingContextTest.php
+++ b/typo3/sysext/fluid/Tests/Unit/Core/Rendering/RenderingContextTest.php
@@ -109,14 +109,14 @@ class RenderingContextTest extends UnitTestCase
             ->addMethods(['dummy'])
             ->disableOriginalConstructor()
             ->getMock();
-        $request = $this->getMockBuilder(Request::class)->setMethods(['setControllerActionName'])->getMock();
-        $request->expects(self::at(0))->method('setControllerActionName')->with('index');
-        $request->expects(self::at(1))->method('setControllerActionName')->with(lcfirst($expected));
+        $request = $this->getMockBuilder(Request::class)->getMock();
+        $request->expects(self::exactly(2))->method('setControllerActionName')->with(lcfirst($expected));
+        $request->expects(self::exactly(2))->method('getControllerActionName')->willReturn(lcfirst($expected));
         $controllerContext = $this->getMockBuilder(ControllerContext::class)->setMethods(['getRequest'])->getMock();
         $controllerContext->expects(self::atLeastOnce())->method('getRequest')->willReturn($request);
         $subject->setControllerContext($controllerContext);
         $subject->setControllerAction($input);
-        self::assertEquals($expected, $subject->getControllerAction());
+        self::assertSame(lcfirst($expected), $subject->getControllerAction());
     }
 
     /**
-- 
GitLab