1<?php
2declare(strict_types = 1);
3namespace TYPO3\CMS\Core\ExpressionLanguage\FunctionsProvider;
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
18use Symfony\Component\ExpressionLanguage\ExpressionFunction;
19use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
20use TYPO3\CMS\Core\Configuration\Features;
21use TYPO3\CMS\Core\Context\Context;
22use TYPO3\CMS\Core\Utility\ArrayUtility;
23use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
24use TYPO3\CMS\Core\Utility\GeneralUtility;
25use TYPO3\CMS\Core\Utility\StringUtility;
26use TYPO3\CMS\Core\Utility\VersionNumberUtility;
27
28/**
29 * Class DefaultFunctionsProvider
30 * @internal
31 */
32class DefaultFunctionsProvider implements ExpressionFunctionProviderInterface
33{
34    /**
35     * @return ExpressionFunction[] An array of Function instances
36     */
37    public function getFunctions()
38    {
39        return [
40            $this->getIpFunction(),
41            $this->getCompatVersionFunction(),
42            $this->getLikeFunction(),
43            $this->getEnvFunction(),
44            $this->getDateFunction(),
45            $this->getFeatureToggleFunction(),
46            $this->getTraverseArrayFunction(),
47        ];
48    }
49
50    protected function getIpFunction(): ExpressionFunction
51    {
52        return new ExpressionFunction(
53            'ip',
54            function () {
55                // Not implemented, we only use the evaluator
56            },
57            function ($arguments, $str) {
58                if ($str === 'devIP') {
59                    $str = trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] ?? '');
60                }
61                return (bool)GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $str);
62            }
63        );
64    }
65
66    protected function getCompatVersionFunction(): ExpressionFunction
67    {
68        return new ExpressionFunction(
69            'compatVersion',
70            function () {
71                // Not implemented, we only use the evaluator
72            },
73            function ($arguments, $str) {
74                return VersionNumberUtility::convertVersionNumberToInteger(TYPO3_branch) >=
75                   VersionNumberUtility::convertVersionNumberToInteger($str);
76            }
77        );
78    }
79
80    protected function getLikeFunction(): ExpressionFunction
81    {
82        return new ExpressionFunction('like', function () {
83            // Not implemented, we only use the evaluator
84        }, function ($arguments, $haystack, $needle) {
85            $result = StringUtility::searchStringWildcard((string)$haystack, (string)$needle);
86            return $result;
87        });
88    }
89
90    protected function getEnvFunction(): ExpressionFunction
91    {
92        return ExpressionFunction::fromPhp('getenv');
93    }
94
95    protected function getDateFunction(): ExpressionFunction
96    {
97        return new ExpressionFunction('date', function () {
98            // Not implemented, we only use the evaluator
99        }, function ($arguments, $format) {
100            return GeneralUtility::makeInstance(Context::class)
101                ->getAspect('date')->getDateTime()->format($format);
102        });
103    }
104
105    protected function getFeatureToggleFunction(): ExpressionFunction
106    {
107        return new ExpressionFunction('feature', function () {
108            // Not implemented, we only use the evaluator
109        }, function ($arguments, $featureName) {
110            return GeneralUtility::makeInstance(Features::class)
111                ->isFeatureEnabled($featureName);
112        });
113    }
114
115    public function getTraverseArrayFunction(): ExpressionFunction
116    {
117        return new ExpressionFunction('traverse', function () {
118            // Not implemented, we only use the evaluator
119        }, function ($arguments, $array, $path) {
120            if (!is_array($array) || !is_string($path) || $path === '') {
121                return '';
122            }
123            try {
124                return ArrayUtility::getValueByPath($array, $path);
125            } catch (MissingArrayPathException $e) {
126                return '';
127            }
128        });
129    }
130}
131