1<?php
2
3declare(strict_types=1);
4
5/*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18namespace TYPO3\CMS\Core\ExpressionLanguage;
19
20use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
21use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
22use TYPO3\CMS\Core\Utility\GeneralUtility;
23
24/**
25 * Class Resolver
26 */
27class Resolver
28{
29    /**
30     * @var ProviderInterface
31     */
32    protected $provider;
33
34    /**
35     * @var \Symfony\Component\ExpressionLanguage\ExpressionLanguage
36     */
37    protected $expressionLanguage;
38
39    /**
40     * @var array
41     */
42    public $expressionLanguageVariables = [];
43
44    /**
45     * @param string $context
46     * @param array $variables
47     */
48    public function __construct(string $context, array $variables)
49    {
50        $functionProviderInstances = [];
51        $providers = GeneralUtility::makeInstance(ProviderConfigurationLoader::class)->getExpressionLanguageProviders()[$context] ?? [];
52        // Always add default provider
53        array_unshift($providers, DefaultProvider::class);
54        $providers = array_unique($providers);
55        $functionProviders = [];
56        $generalVariables = [];
57        foreach ($providers as $provider) {
58            /** @var ProviderInterface $providerInstance */
59            $providerInstance = GeneralUtility::makeInstance($provider);
60            $functionProviders[] = $providerInstance->getExpressionLanguageProviders();
61            $generalVariables[] = $providerInstance->getExpressionLanguageVariables();
62        }
63        $functionProviders = array_merge(...$functionProviders);
64        $generalVariables = array_replace_recursive(...$generalVariables);
65        $this->expressionLanguageVariables = array_replace_recursive($generalVariables, $variables);
66        foreach ($functionProviders as $functionProvider) {
67            /** @var ExpressionFunctionProviderInterface[] $functionProviderInstances */
68            $functionProviderInstances[] = GeneralUtility::makeInstance($functionProvider);
69        }
70        $this->expressionLanguage = new ExpressionLanguage(null, $functionProviderInstances);
71    }
72
73    /**
74     * Evaluate an expression.
75     *
76     * @param string $condition The expression to parse
77     * @return bool
78     */
79    public function evaluate(string $condition): bool
80    {
81        // The TypoScript [ELSE] condition is not known by the Symfony Expression Language
82        // and must not be evaluated. If/else logic is handled in TypoScriptParser.
83        if (strtoupper($condition) === 'ELSE') {
84            return false;
85        }
86
87        return (bool)$this->expressionLanguage->evaluate($condition, $this->expressionLanguageVariables);
88    }
89
90    /**
91     * Compiles an expression source code.
92     *
93     * @param string $condition The expression to compile
94     * @return string
95     */
96    public function compile(string $condition): string
97    {
98        return (string)$this->expressionLanguage->compile($condition, array_keys($this->expressionLanguageVariables));
99    }
100}
101