[FEATURE] Move expression language handling into EXT:core 84/57884/10
authorFrank Naegler <frank.naegler@typo3.org>
Mon, 13 Aug 2018 10:41:06 +0000 (12:41 +0200)
committerRalf Zimmermann <ralf.zimmermann@tritum.de>
Mon, 13 Aug 2018 19:02:27 +0000 (21:02 +0200)
The implementation of the symfony expression language has been moved
into EXT:core to centralize the functionality and make it usable
in other scopes.

Resolves: #85828
Releases: master
Change-Id: I5c64001d24a610fd1c3ee4a6bb58e4c1923ec78a
Reviewed-on: https://review.typo3.org/57884
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
Reviewed-by: Ralf Zimmermann <ralf.zimmermann@tritum.de>
Tested-by: Ralf Zimmermann <ralf.zimmermann@tritum.de>
14 files changed:
typo3/sysext/core/Classes/ExpressionLanguage/AbstractProvider.php [new file with mode: 0644]
typo3/sysext/core/Classes/ExpressionLanguage/DefaultProvider.php [new file with mode: 0644]
typo3/sysext/core/Classes/ExpressionLanguage/ProviderInterface.php [new file with mode: 0644]
typo3/sysext/core/Classes/ExpressionLanguage/Resolver.php [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Feature-85828-MoveSymfonyExpressionLanguageHandlingIntoEXTcore.rst [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/ExpressionLanguage/ResolverTest.php [new file with mode: 0644]
typo3/sysext/core/composer.json
typo3/sysext/form/Classes/Domain/Condition/ConditionContext.php [deleted file]
typo3/sysext/form/Classes/Domain/Condition/ConditionProvider.php [new file with mode: 0644]
typo3/sysext/form/Classes/Domain/Condition/ConditionResolver.php [deleted file]
typo3/sysext/form/Classes/Domain/Model/Renderable/RenderableVariant.php
typo3/sysext/form/Classes/Domain/Model/Renderable/RenderableVariantInterface.php
typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php
typo3/sysext/form/composer.json

diff --git a/typo3/sysext/core/Classes/ExpressionLanguage/AbstractProvider.php b/typo3/sysext/core/Classes/ExpressionLanguage/AbstractProvider.php
new file mode 100644 (file)
index 0000000..90ea8af
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Core\ExpressionLanguage;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
+
+/**
+ * Class AbstractProvider
+ * @internal
+ */
+abstract class AbstractProvider implements ProviderInterface
+{
+    /**
+     * @var ExpressionFunctionProviderInterface[]
+     */
+    protected $expressionLanguageProviders = [];
+
+    /**
+     * @var array
+     */
+    protected $expressionLanguageVariables = [];
+
+    /**
+     * An array of objects which implements the ExpressionFunctionProviderInterface
+     *
+     * @return ExpressionFunctionProviderInterface[]
+     */
+    public function getExpressionLanguageProviders(): array
+    {
+        return $this->expressionLanguageProviders;
+    }
+
+    /**
+     * An array with key/value pairs. The key will be available as variable name
+     *
+     * @return array
+     */
+    public function getExpressionLanguageVariables(): array
+    {
+        return $this->expressionLanguageVariables;
+    }
+}
diff --git a/typo3/sysext/core/Classes/ExpressionLanguage/DefaultProvider.php b/typo3/sysext/core/Classes/ExpressionLanguage/DefaultProvider.php
new file mode 100644 (file)
index 0000000..cbbb75b
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Core\ExpressionLanguage;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Class DefaultProvider
+ * @internal
+ */
+class DefaultProvider extends AbstractProvider
+{
+}
diff --git a/typo3/sysext/core/Classes/ExpressionLanguage/ProviderInterface.php b/typo3/sysext/core/Classes/ExpressionLanguage/ProviderInterface.php
new file mode 100644 (file)
index 0000000..3611e0b
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Core\ExpressionLanguage;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
+
+/**
+ * Interface ProviderInterface
+ * @internal
+ */
+interface ProviderInterface
+{
+    /**
+     * An array of objects which implements the ExpressionFunctionProviderInterface
+     *
+     * @return ExpressionFunctionProviderInterface[]
+     */
+    public function getExpressionLanguageProviders(): array;
+
+    /**
+     * An array with key/value pairs. The key will be available as variable name
+     *
+     * @return array
+     */
+    public function getExpressionLanguageVariables(): array;
+}
diff --git a/typo3/sysext/core/Classes/ExpressionLanguage/Resolver.php b/typo3/sysext/core/Classes/ExpressionLanguage/Resolver.php
new file mode 100644 (file)
index 0000000..c911dcf
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Core\ExpressionLanguage;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
+
+/**
+ * Class Resolver
+ * @internal
+ */
+class Resolver
+{
+    /**
+     * @var ProviderInterface
+     */
+    protected $context;
+
+    /**
+     * @var \Symfony\Component\ExpressionLanguage\ExpressionLanguage
+     */
+    protected $expressionLanguage;
+
+    /**
+     * @var array
+     */
+    public $expressionLanguageVariables = [];
+
+    /**
+     * @param ProviderInterface $context
+     */
+    public function __construct(ProviderInterface $context)
+    {
+        $this->context = $context;
+        $this->expressionLanguage = new ExpressionLanguage(null, $context->getExpressionLanguageProviders());
+        $this->expressionLanguageVariables = $context->getExpressionLanguageVariables();
+    }
+
+    /**
+     * Evaluate an expression.
+     *
+     * @param string $condition The expression to parse
+     * @return bool
+     */
+    public function evaluate(string $condition): bool
+    {
+        return (bool)$this->expressionLanguage->evaluate($condition, $this->expressionLanguageVariables);
+    }
+
+    /**
+     * Compiles an expression source code.
+     *
+     * @param string $condition The expression to compile
+     * @return string
+     */
+    public function compile(string $condition): string
+    {
+        return (string)$this->expressionLanguage->compile($condition, array_keys($this->expressionLanguageVariables));
+    }
+}
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-85828-MoveSymfonyExpressionLanguageHandlingIntoEXTcore.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-85828-MoveSymfonyExpressionLanguageHandlingIntoEXTcore.rst
new file mode 100644 (file)
index 0000000..5b57251
--- /dev/null
@@ -0,0 +1,36 @@
+.. include:: ../../Includes.txt
+
+=========================================================================
+Feature: #85828 - Move symfony expression language handling into EXT:core
+=========================================================================
+
+See :issue:`85828`
+
+Description
+===========
+
+The symfony expression language handling has been moved out of EXT:form into EXT:core.
+This step was required to make the expression language usable also in other scopes.
+
+To use the expression language a provider definition is required which implements the :php:`\TYPO3\CMS\Core\ExpressionLanguage\ProviderInterface`.
+The core comes with a :php:`\TYPO3\CMS\Core\ExpressionLanguage\DefaultProvider` class which can be used directly.
+For a custom implementation the :php:`\TYPO3\CMS\Core\ExpressionLanguage\AbstractProvider` class can be extended.
+
+The provider can provide additional variables and expression functions to extend the expression language.
+For a custom implementation check out the :php:`\TYPO3\CMS\Form\Domain\Condition\ConditionProvider` class.
+
+A usage example:
+
+.. code-block:: php
+
+   $provider = GeneralUtility::makeInstance(\TYPO3\CMS\Core\ExpressionLanguage\DefaultProvider::class);
+   $conditionResolver = GeneralUtility::makeInstance(\TYPO3\CMS\Core\ExpressionLanguage\Resolver::class, $provider);
+   $conditionResolver->evaluate('1 < 2'); // result is true
+
+
+Impact
+======
+
+The expression language can now be used in other scopes and has no dependency to EXT:form
+
+.. index:: Backend, Frontend, PHP-API, ext:core
diff --git a/typo3/sysext/core/Tests/Unit/ExpressionLanguage/ResolverTest.php b/typo3/sysext/core/Tests/Unit/ExpressionLanguage/ResolverTest.php
new file mode 100644 (file)
index 0000000..3ba92e0
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Core\Tests\Unit\ExpressionLanguage;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Symfony\Component\ExpressionLanguage\ExpressionFunction;
+use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
+use TYPO3\CMS\Core\ExpressionLanguage\DefaultProvider;
+use TYPO3\CMS\Core\ExpressionLanguage\Resolver;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+/**
+ * Class ResolverTest
+ */
+class ResolverTest extends UnitTestCase
+{
+    /**
+     * @return array
+     */
+    public function basicExpressionsDataHandler(): array
+    {
+        return [
+            '1+1' => ['1+1', true],
+            '1 < 2' => ['1 < 2', true],
+            '2 < 1' => ['2 < 1', false],
+            'true' => ['true', true],
+            'false' => ['false', false],
+            'true != false' => ['true != false', true],
+            'true < false' => ['true < false', false],
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider basicExpressionsDataHandler
+     * @param string $expression
+     * @param mixed $expectedResult
+     */
+    public function basicExpressionHandlingResultsWorksAsExpected(string $expression, $expectedResult)
+    {
+        $expressionLanguageResolver = new Resolver(new DefaultProvider());
+        $this->assertSame($expectedResult, $expressionLanguageResolver->evaluate($expression));
+    }
+
+    /**
+     * @return array
+     */
+    public function basicExpressionsWithVariablesDataHandler(): array
+    {
+        return [
+            'var1 + var2' => ['var1 + var2', true],
+            'var1 < var2' => ['var1 < var2', true],
+            'var2 < var1' => ['var2 < var1', false],
+            'varTrue' => ['varTrue', true],
+            'varFalse' => ['varFalse', false],
+            'varTrue != varFalse' => ['varTrue != varFalse', true],
+            'varTrue < varFalse' => ['varTrue < varFalse', false],
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider basicExpressionsWithVariablesDataHandler
+     * @param string $expression
+     * @param mixed $expectedResult
+     */
+    public function basicExpressionHandlingWithCustomVariablesWorksAsExpected(string $expression, $expectedResult)
+    {
+        $contextProphecy = $this->prophesize(DefaultProvider::class);
+        $contextProphecy->getExpressionLanguageProviders()->willReturn([]);
+        $contextProphecy->getExpressionLanguageVariables()->willReturn([
+            'var1' => '1',
+            'var2' => '2',
+            'varTrue' => true,
+            'varFalse' => false,
+         ]);
+        $expressionLanguageResolver = new Resolver($contextProphecy->reveal());
+        $this->assertSame($expectedResult, $expressionLanguageResolver->evaluate($expression));
+    }
+
+    /**
+     * @return array
+     */
+    public function basicExpressionsWithVariablesAndExpressionLanguageProviderDataHandler(): array
+    {
+        return [
+            'testMeLowercase(var1) == var2' => ['testMeLowercase(var1) == var2', true],
+            'testMeLowercase(var2) == var1' => ['testMeLowercase(var2) == var1', false],
+            'testMeLowercase(var1) == var1' => ['testMeLowercase(var1) == var1', false],
+            'testMeLowercase(var2) == var2' => ['testMeLowercase(var2) == var2', true],
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider basicExpressionsWithVariablesAndExpressionLanguageProviderDataHandler
+     * @param string $expression
+     * @param mixed $expectedResult
+     */
+    public function basicExpressionHandlingWithCustomVariablesAndExpressionLanguageProviderWorksAsExpected(string $expression, $expectedResult)
+    {
+        $expressionProvider = $this->prophesize(ExpressionFunctionProviderInterface::class);
+        $expressionProvider->getFunctions()->willReturn([
+            new ExpressionFunction('testMeLowercase', function ($str) {
+                return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str);
+            }, function ($arguments, $str) {
+                return is_string($str) ? strtolower($str) : $str;
+            })
+        ]);
+        $contextProphecy = $this->prophesize(DefaultProvider::class);
+        $contextProphecy->getExpressionLanguageProviders()->willReturn([$expressionProvider->reveal()]);
+        $contextProphecy->getExpressionLanguageVariables()->willReturn([
+            'var1' => 'FOO',
+            'var2' => 'foo'
+         ]);
+        $expressionLanguageResolver = new Resolver($contextProphecy->reveal());
+        $this->assertSame($expectedResult, $expressionLanguageResolver->evaluate($expression));
+    }
+}
index 4c7eb77..f42f147 100644 (file)
@@ -33,6 +33,7 @@
                "psr/log": "~1.0.0",
                "swiftmailer/swiftmailer": "~5.4.5",
                "symfony/console": "^4.1",
+               "symfony/expression-language": "^4.1",
                "symfony/finder": "^4.1",
                "symfony/polyfill-intl-icu": "^1.6",
                "symfony/polyfill-mbstring": "^1.2",
diff --git a/typo3/sysext/form/Classes/Domain/Condition/ConditionContext.php b/typo3/sysext/form/Classes/Domain/Condition/ConditionContext.php
deleted file mode 100644 (file)
index f59b5db..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-declare(strict_types = 1);
-namespace TYPO3\CMS\Form\Domain\Condition;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Form\Domain\Exception;
-use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
-use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
-use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
-
-/**
- * Scope: frontend
- * **This class is NOT meant to be sub classed by developers.**
- *
- * @internal
- */
-class ConditionContext
-{
-
-    /**
-     * @var array
-     */
-    public $expressionLanguageProviders = [];
-
-    /**
-     * @var array
-     */
-    public $expressionLanguageVariables = [];
-
-    /**
-     * @param FormRuntime $formRuntime
-     */
-    public function __construct(FormRuntime $formRuntime)
-    {
-        $this->expressionLanguageVariables = $this->getInitialExpressionLanguageVariables($formRuntime);
-
-        $conditionContextDefinition = $formRuntime->getFormDefinition()->getConditionContextDefinition();
-
-        foreach ($conditionContextDefinition['expressionLanguageProvider'] ?? [] as $expressionLanguageProviderName => $expressionLanguageProviderDefinition) {
-            if (!isset($expressionLanguageProviderDefinition['implementationClassName'])) {
-                throw new Exception(sprintf('The "implementationClassName" was not set for expression language provider "%s".', $expressionLanguageProviderName), 1526695869);
-            }
-            $implementationClassName = $expressionLanguageProviderDefinition['implementationClassName'];
-
-            /** @see https://symfony.com/doc/4.0/components/expression_language/extending.html#using-expression-providers */
-            $this->expressionLanguageProviders[] = new $implementationClassName();
-        }
-
-        foreach ($conditionContextDefinition['expressionLanguageVariableProvider'] ?? [] as $expressionLanguageVariableProviderName => $expressionLanguageVariableProviderDefinition) {
-            if (!isset($expressionLanguageVariableProviderDefinition['implementationClassName'])) {
-                throw new Exception(sprintf('The "implementationClassName" was not set for expression language variable provider "%s".', $expressionLanguageVariableProviderName), 1526695870);
-            }
-
-            $implementationClassName = $expressionLanguageVariableProviderDefinition['implementationClassName'];
-            $expressionLanguageVariableProvider = new $implementationClassName($formRuntime);
-            if (!($expressionLanguageVariableProvider instanceof ExpressionLanguageVariableProviderInterface)) {
-                throw new Exception(sprintf('The expression language provider "%s" must implement "%s".', $implementationClassName, ExpressionLanguageVariableProviderInterface::class), 1526695874);
-            }
-            /** @see https://symfony.com/doc/4.0/components/expression_language.html#passing-in-variables */
-            $this->expressionLanguageVariables[$expressionLanguageVariableProvider->getVariableName()] = $expressionLanguageVariableProvider->getVariableValue();
-        }
-    }
-
-    /**
-     * @return array
-     */
-    public function getExpressionLanguageProviders(): array
-    {
-        return $this->expressionLanguageProviders;
-    }
-
-    /**
-     * @return array
-     */
-    public function getExpressionLanguageVariables(): array
-    {
-        return $this->expressionLanguageVariables;
-    }
-
-    /**
-     * @param FormRuntime $formRuntime
-     * @return array
-     */
-    protected function getInitialExpressionLanguageVariables(FormRuntime $formRuntime): array
-    {
-        $formValues = array_replace_recursive($formRuntime->getFormState()->getFormValues(), $formRuntime->getRequest()->getArguments());
-        $page = $formRuntime->getCurrentPage() ?? $formRuntime->getFormDefinition()->getPageByIndex(0);
-
-        $finisherIdentifier = '';
-        if ($formRuntime->getCurrentFinisher() !== null) {
-            $finisherIdentifier = (new \ReflectionClass($formRuntime->getCurrentFinisher()))->getShortName();
-            $finisherIdentifier = preg_replace('/Finisher$/', '', $finisherIdentifier);
-        }
-
-        $contentObjectData = [];
-        if (
-            TYPO3_MODE === 'FE'
-            && $this->getTypoScriptFrontendController()->cObj instanceof ContentObjectRenderer
-        ) {
-            $contentObjectData = $this->getTypoScriptFrontendController()->cObj->data;
-        }
-
-        return [
-            'formRuntime' => $formRuntime,
-            // some shortcuts
-            'formValues' => $formValues,
-            'stepIdentifier' => $page->getIdentifier(),
-            'stepType' => $page->getType(),
-            'finisherIdentifier' => $finisherIdentifier,
-            'siteLanguage' => $formRuntime->getCurrentSiteLanguage(),
-            'applicationContext' => GeneralUtility::getApplicationContext()->__toString(),
-            'contentObject' => $contentObjectData,
-        ];
-    }
-
-    /**
-     * @return TypoScriptFrontendController
-     */
-    protected function getTypoScriptFrontendController(): TypoScriptFrontendController
-    {
-        return $GLOBALS['TSFE'];
-    }
-}
diff --git a/typo3/sysext/form/Classes/Domain/Condition/ConditionProvider.php b/typo3/sysext/form/Classes/Domain/Condition/ConditionProvider.php
new file mode 100644 (file)
index 0000000..2881a6b
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Form\Domain\Condition;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\ExpressionLanguage\AbstractProvider;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Form\Domain\Exception;
+use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
+use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
+
+/**
+ * Scope: frontend
+ * **This class is NOT meant to be sub classed by developers.**
+ *
+ * @internal
+ */
+class ConditionProvider extends AbstractProvider
+{
+    /**
+     * @param FormRuntime $formRuntime
+     */
+    public function __construct(FormRuntime $formRuntime)
+    {
+        $this->expressionLanguageVariables = $this->getInitialExpressionLanguageVariables($formRuntime);
+
+        $conditionContextDefinition = $formRuntime->getFormDefinition()->getConditionContextDefinition();
+
+        foreach ($conditionContextDefinition['expressionLanguageProvider'] ?? [] as $expressionLanguageProviderName => $expressionLanguageProviderDefinition) {
+            if (!isset($expressionLanguageProviderDefinition['implementationClassName'])) {
+                throw new Exception(sprintf('The "implementationClassName" was not set for expression language provider "%s".', $expressionLanguageProviderName), 1526695869);
+            }
+            $implementationClassName = $expressionLanguageProviderDefinition['implementationClassName'];
+
+            /** @see https://symfony.com/doc/4.0/components/expression_language/extending.html#using-expression-providers */
+            $this->expressionLanguageProviders[] = new $implementationClassName();
+        }
+
+        foreach ($conditionContextDefinition['expressionLanguageVariableProvider'] ?? [] as $expressionLanguageVariableProviderName => $expressionLanguageVariableProviderDefinition) {
+            if (!isset($expressionLanguageVariableProviderDefinition['implementationClassName'])) {
+                throw new Exception(sprintf('The "implementationClassName" was not set for expression language variable provider "%s".', $expressionLanguageVariableProviderName), 1526695870);
+            }
+
+            $implementationClassName = $expressionLanguageVariableProviderDefinition['implementationClassName'];
+            $expressionLanguageVariableProvider = new $implementationClassName($formRuntime);
+            if (!($expressionLanguageVariableProvider instanceof ExpressionLanguageVariableProviderInterface)) {
+                throw new Exception(sprintf('The expression language provider "%s" must implement "%s".', $implementationClassName, ExpressionLanguageVariableProviderInterface::class), 1526695874);
+            }
+            /** @see https://symfony.com/doc/4.0/components/expression_language.html#passing-in-variables */
+            $this->expressionLanguageVariables[$expressionLanguageVariableProvider->getVariableName()] = $expressionLanguageVariableProvider->getVariableValue();
+        }
+    }
+
+    /**
+     * @param FormRuntime $formRuntime
+     * @return array
+     */
+    protected function getInitialExpressionLanguageVariables(FormRuntime $formRuntime): array
+    {
+        $formValues = array_replace_recursive($formRuntime->getFormState()->getFormValues(), $formRuntime->getRequest()->getArguments());
+        $page = $formRuntime->getCurrentPage() ?? $formRuntime->getFormDefinition()->getPageByIndex(0);
+
+        $finisherIdentifier = '';
+        if ($formRuntime->getCurrentFinisher() !== null) {
+            $finisherIdentifier = (new \ReflectionClass($formRuntime->getCurrentFinisher()))->getShortName();
+            $finisherIdentifier = preg_replace('/Finisher$/', '', $finisherIdentifier);
+        }
+
+        $contentObjectData = [];
+        if (
+            TYPO3_MODE === 'FE'
+            && $this->getTypoScriptFrontendController()->cObj instanceof ContentObjectRenderer
+        ) {
+            $contentObjectData = $this->getTypoScriptFrontendController()->cObj->data;
+        }
+
+        return [
+            'formRuntime' => $formRuntime,
+            // some shortcuts
+            'formValues' => $formValues,
+            'stepIdentifier' => $page->getIdentifier(),
+            'stepType' => $page->getType(),
+            'finisherIdentifier' => $finisherIdentifier,
+            'siteLanguage' => $formRuntime->getCurrentSiteLanguage(),
+            'applicationContext' => GeneralUtility::getApplicationContext()->__toString(),
+            'contentObject' => $contentObjectData,
+        ];
+    }
+
+    /**
+     * @return TypoScriptFrontendController
+     */
+    protected function getTypoScriptFrontendController(): TypoScriptFrontendController
+    {
+        return $GLOBALS['TSFE'];
+    }
+}
diff --git a/typo3/sysext/form/Classes/Domain/Condition/ConditionResolver.php b/typo3/sysext/form/Classes/Domain/Condition/ConditionResolver.php
deleted file mode 100644 (file)
index 68cfa3e..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-declare(strict_types = 1);
-namespace TYPO3\CMS\Form\Domain\Condition;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
-
-/**
- * Scope: frontend
- * **This class is NOT meant to be sub classed by developers.**
- *
- * @internal
- */
-class ConditionResolver
-{
-
-    /**
-     * @var \TYPO3\CMS\Form\Domain\Condition\ConditionContext
-     */
-    protected $conditionContext;
-
-    /**
-     * @var \Symfony\Component\ExpressionLanguage\ExpressionLanguage
-     */
-    protected $expressionLanguage;
-
-    /**
-     * @var array
-     */
-    public $expressionLanguageVariables = [];
-
-    /**
-     * @param ConditionContext $conditionContext
-     */
-    public function __construct(ConditionContext $conditionContext)
-    {
-        $this->conditionContext = $conditionContext;
-        $this->expressionLanguage = new ExpressionLanguage(null, $conditionContext->getExpressionLanguageProviders());
-        $this->expressionLanguageVariables = $conditionContext->getExpressionLanguageVariables();
-    }
-
-    /**
-     * @param string $condition
-     * @return bool
-     */
-    public function resolveCondition(string $condition): bool
-    {
-        return (bool)$this->expressionLanguage->evaluate($condition, $this->expressionLanguageVariables);
-    }
-}
index 271afcb..d195278 100644 (file)
@@ -15,7 +15,7 @@ namespace TYPO3\CMS\Form\Domain\Model\Renderable;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Form\Domain\Condition\ConditionResolver;
+use TYPO3\CMS\Core\ExpressionLanguage\Resolver;
 use TYPO3\CMS\Form\Domain\Exception\IdentifierNotValidException;
 
 /**
@@ -88,16 +88,16 @@ class RenderableVariant implements RenderableVariantInterface
     }
 
     /**
-     * @param ConditionResolver $conditionResolver
+     * @param Resolver $conditionResolver
      * @return bool
      */
-    public function conditionMatches(ConditionResolver $conditionResolver): bool
+    public function conditionMatches(Resolver $conditionResolver): bool
     {
         if (empty($this->condition)) {
             return false;
         }
 
-        return $conditionResolver->resolveCondition($this->condition);
+        return $conditionResolver->evaluate($this->condition);
     }
 
     /**
index 45316b1..9d649b9 100644 (file)
@@ -15,7 +15,7 @@ namespace TYPO3\CMS\Form\Domain\Model\Renderable;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Form\Domain\Condition\ConditionResolver;
+use TYPO3\CMS\Core\ExpressionLanguage\Resolver;
 
 /**
  * Scope: frontend
@@ -42,8 +42,8 @@ interface RenderableVariantInterface
     public function isApplied(): bool;
 
     /**
-     * @param ConditionResolver $conditionResolver
+     * @param Resolver $conditionResolver
      * @return bool
      */
-    public function conditionMatches(ConditionResolver $conditionResolver): bool;
+    public function conditionMatches(Resolver $conditionResolver): bool;
 }
index 52b973b..363b0a8 100644 (file)
@@ -19,6 +19,7 @@ namespace TYPO3\CMS\Form\Domain\Runtime;
 
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Context\Context;
+use TYPO3\CMS\Core\ExpressionLanguage\Resolver;
 use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
@@ -31,8 +32,7 @@ use TYPO3\CMS\Extbase\Mvc\Web\Request;
 use TYPO3\CMS\Extbase\Mvc\Web\Response;
 use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
 use TYPO3\CMS\Extbase\Property\Exception as PropertyException;
-use TYPO3\CMS\Form\Domain\Condition\ConditionContext;
-use TYPO3\CMS\Form\Domain\Condition\ConditionResolver;
+use TYPO3\CMS\Form\Domain\Condition\ConditionProvider;
 use TYPO3\CMS\Form\Domain\Exception\RenderingException;
 use TYPO3\CMS\Form\Domain\Finishers\FinisherContext;
 use TYPO3\CMS\Form\Domain\Finishers\FinisherInterface;
@@ -1057,15 +1057,11 @@ class FormRuntime implements RootRenderableInterface, \ArrayAccess
     }
 
     /**
-     * @return ConditionResolver
+     * @return Resolver
      */
-    protected function getConditionResolver(): ConditionResolver
+    protected function getConditionResolver(): Resolver
     {
-        /** @var \TYPO3\CMS\Form\Domain\Condition\ConditionResolver $conditionResolver */
-        $conditionResolver = $this->objectManager->get(
-            ConditionResolver::class,
-            GeneralUtility::makeInstance(ConditionContext::class, $this)
-        );
+        $conditionResolver = GeneralUtility::makeInstance(Resolver::class, GeneralUtility::makeInstance(ConditionProvider::class, $this));
         return $conditionResolver;
     }
 
index 206b315..379a98b 100644 (file)
@@ -13,8 +13,7 @@
                "sort-packages": true
        },
        "require": {
-               "typo3/cms-core": "9.4.*@dev",
-               "symfony/expression-language": "^4.1"
+               "typo3/cms-core": "9.4.*@dev"
        },
        "conflict": {
                "typo3/cms": "*"