LaravelExpressionProvider   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 323
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 33
eloc 146
c 4
b 0
f 0
dl 0
loc 323
rs 9.76
ccs 131
cts 131
cp 1

13 Methods

Rating   Name   Duplication   Size   Complexity  
A onPropertyAccessExpression() 0 11 2
A onConstantExpression() 0 8 3
A setResourceType() 0 4 1
A getResourceType() 0 3 1
A getIteratorName() 0 3 1
A onUnaryExpression() 0 9 3
A onArithmeticExpression() 0 17 6
A onLogicalExpression() 0 11 3
A prepareUnaryExpression() 0 3 1
A onFunctionCallExpression() 0 6 2
B onRelationalExpression() 0 17 7
A prepareBinaryExpression() 0 3 1
A __construct() 0 79 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AlgoWeb\PODataLaravel\Query;
6
7
use POData\Common\ODataConstants;
8
use POData\Providers\Expression\IExpressionProvider;
9
use POData\Providers\Metadata\ResourceType;
10
use POData\Providers\Metadata\Type\IType;
11
use POData\UriProcessor\QueryProcessor\ExpressionParser\Expressions\ExpressionType;
12
use POData\UriProcessor\QueryProcessor\ExpressionParser\Expressions\PropertyAccessExpression;
13
use POData\UriProcessor\QueryProcessor\FunctionDescription;
14
15
class LaravelExpressionProvider implements IExpressionProvider
16
{
17
    const ADD                   = '+';
18
    const CLOSE_BRACKET         = ')';
19
    const COMMA                 = ',';
20
    const DIVIDE                = '/';
21
    const SUBTRACT              = '-';
22
    const EQUAL                 = '==';
23
    const GREATER_THAN          = '>';
24
    const GREATER_THAN_OR_EQUAL = '>=';
25
    const LESS_THAN             = '<';
26
    const LESS_THAN_OR_EQUAL    = '<=';
27
    const LOGICAL_AND           = '&&';
28
    const LOGICAL_NOT           = '!';
29
    const LOGICAL_OR            = '||';
30
    const MEMBER_ACCESS         = '->';
31
    const MODULO                = '%';
32
    const MULTIPLY              = '*';
33
    const NEGATE                = '-';
34
    const NOT_EQUAL             = '!=';
35
    const OPEN_BRACKET          = '(';
36
    const TYPE_NAMESPACE        = 'POData\\Providers\\Metadata\\Type\\';
37
38
    /** @var array<string, callable> */
39
    private $functionDescriptionParsers;
40
41
    /**
42
     * The name of iterator.
43
     *
44
     * @var string
45
     */
46
    private $iteratorName;
47
    /**
48
     * The type of the resource pointed by the resource path segment.
49 52
     *
50
     * @var ResourceType
51 52
     */
52
    private $resourceType;
53
54
    public function __construct()
55
    {
56
        $this->functionDescriptionParsers[ODataConstants::STRFUN_COMPARE] = function ($params) {
57 1
            return 'strcmp(' . $params[0] . ', ' . $params[1] . ')';
58
        };
59 1
        $this->functionDescriptionParsers[ODataConstants::STRFUN_ENDSWITH] = function ($params) {
60
            return '(strcmp(substr(' . $params[0] . ', strlen(' . $params[0] . ') - strlen(' . $params[1] . ')), '
61
                   . $params[1] . ') === 0)';
62
        };
63
        $this->functionDescriptionParsers[ODataConstants::STRFUN_INDEXOF] = function ($params) {
64
            return 'strpos(' . $params[0] . ', ' . $params[1] . ')';
65
        };
66
        $this->functionDescriptionParsers[ODataConstants::STRFUN_REPLACE] = function ($params) {
67 1
            return 'str_replace(' . $params[1] . ', ' . $params[2] . ', ' . $params[0] . ')';
68
        };
69 1
        $this->functionDescriptionParsers[ODataConstants::STRFUN_STARTSWITH] = function ($params) {
70 1
            return '(strpos(' . $params[0] . ', ' . $params[1] . ') === 0)';
71 1
        };
72
        $this->functionDescriptionParsers[ODataConstants::STRFUN_TOLOWER] = function ($params) {
73
            return 'strtolower(' . $params[0] . ')';
74
        };
75
        $this->functionDescriptionParsers[ODataConstants::STRFUN_TOUPPER] = function ($params) {
76
            return 'strtoupper(' . $params[0] . ')';
77
        };
78
        $this->functionDescriptionParsers[ODataConstants::STRFUN_TRIM] = function ($params) {
79
            return 'trim(' . $params[0] . ')';
80
        };
81 3
        $this->functionDescriptionParsers[ODataConstants::STRFUN_SUBSTRING] = function ($params) {
82
            return count($params) == 3
83
                ? 'substr(' . $params[0] . ', ' . $params[1] . ', ' . $params[2] . ')'
84 3
                : 'substr(' . $params[0] . ', ' . $params[1] . ')';
85 1
        };
86 2
        $this->functionDescriptionParsers[ODataConstants::STRFUN_SUBSTRINGOF] = function ($params) {
87 1
            return '(strpos(' . $params[1] . ', ' . $params[0] . ') !== false)';
88 1
        };
89 1
        $this->functionDescriptionParsers[ODataConstants::STRFUN_CONCAT] = function ($params) {
90 1
            return $params[0] . ' . ' . $params[1];
91
        };
92
        $this->functionDescriptionParsers[ODataConstants::STRFUN_LENGTH] = function ($params) {
93
            return 'strlen(' . $params[0] . ')';
94
        };
95
        $this->functionDescriptionParsers[ODataConstants::GUIDFUN_EQUAL] = function ($params) {
96
            return self::TYPE_NAMESPACE . 'Guid::guidEqual(' . $params[0] . ', ' . $params[1] . ')';
97
        };
98
        $this->functionDescriptionParsers[ODataConstants::DATETIME_COMPARE] = function ($params) {
99
            return self::TYPE_NAMESPACE . 'DateTime::dateTimeCmp(' . $params[0] . ', ' . $params[1] . ')';
100
        };
101 6
        $this->functionDescriptionParsers[ODataConstants::DATETIME_YEAR] = function ($params) {
102
            return self::TYPE_NAMESPACE . 'DateTime::year(' . $params[0] . ')';
103
        };
104 6
        $this->functionDescriptionParsers[ODataConstants::DATETIME_MONTH] = function ($params) {
105 1
            return self::TYPE_NAMESPACE . 'DateTime::month(' . $params[0] . ')';
106 5
        };
107 1
        $this->functionDescriptionParsers[ODataConstants::DATETIME_DAY] = function ($params) {
108 4
            return self::TYPE_NAMESPACE . 'DateTime::day(' . $params[0] . ')';
109 1
        };
110 3
        $this->functionDescriptionParsers[ODataConstants::DATETIME_HOUR] = function ($params) {
111 1
            return self::TYPE_NAMESPACE . 'DateTime::hour(' . $params[0] . ')';
112 2
        };
113 1
        $this->functionDescriptionParsers[ODataConstants::DATETIME_MINUTE] = function ($params) {
114 1
            return self::TYPE_NAMESPACE . 'DateTime::minute(' . $params[0] . ')';
115 1
        };
116 1
        $this->functionDescriptionParsers[ODataConstants::DATETIME_SECOND] = function ($params) {
117
            return self::TYPE_NAMESPACE . 'DateTime::second(' . $params[0] . ')';
118
        };
119
        $this->functionDescriptionParsers[ODataConstants::MATHFUN_ROUND] = function ($params) {
120
            return 'round(' . $params[0] . ')';
121
        };
122
        $this->functionDescriptionParsers[ODataConstants::MATHFUN_CEILING] = function ($params) {
123
            return 'ceil(' . $params[0] . ')';
124
        };
125
        $this->functionDescriptionParsers[ODataConstants::MATHFUN_FLOOR] = function ($params) {
126
            return 'floor(' . $params[0] . ')';
127 7
        };
128
        $this->functionDescriptionParsers[ODataConstants::BINFUL_EQUAL] = function ($params) {
129
            return self::TYPE_NAMESPACE . 'Binary::binaryEqual(' . $params[0] . ', ' . $params[1] . ')';
130 7
        };
131 1
        $this->functionDescriptionParsers['is_null'] = function ($params) {
132 6
            return 'is_null(' . $params[0] . ')';
133 1
        };
134 5
    }
135 1
    /**
136 4
     * Get the name of the iterator.
137 1
     *
138 3
     * @return string
139 1
     */
140 2
    public function getIteratorName()
141 1
    {
142 1
        return $this->iteratorName;
143 1
    }
144 1
145
    /**
146
     * Get the resource type.
147
     *
148
     * @return ResourceType|null
149
     */
150
    public function getResourceType()
151
    {
152
        return $this->resourceType;
153
    }
154 3
155
    /**
156
     * call-back for setting the resource type.
157 3
     *
158 1
     * @param ResourceType $resourceType The resource type on which the filter
159 2
     *                                   is going to be applied
160 1
     */
161 1
    public function setResourceType(ResourceType $resourceType): void
162 1
    {
163 1
        $this->iteratorName = '$' . $resourceType->getName();
164
        $this->resourceType = $resourceType;
165
    }
166
    /**
167
     * Call-back for logical expression.
168
     *
169
     * @param ExpressionType $expressionType The type of logical expression
170
     * @param string         $left           The left expression
171
     * @param string         $right          The left expression
172
     *
173 8
     * @return string
174
     */
175 8
    public function onLogicalExpression(ExpressionType $expressionType, $left, $right)
176 2
    {
177 6
        $left  = strval($left);
178 1
        $right = strval($right);
179
        switch ($expressionType) {
180 5
            case ExpressionType::AND_LOGICAL():
181
                return $this->prepareBinaryExpression(self::LOGICAL_AND, $left, $right);
182
            case ExpressionType::OR_LOGICAL():
183
                return $this->prepareBinaryExpression(self::LOGICAL_OR, $left, $right);
184
            default:
185
                throw new \InvalidArgumentException('onLogicalExpression');
186
        }
187
    }
188
    /**
189 1
     * Call-back for arithmetic expression.
190
     *
191 1
     * @param ExpressionType $expressionType The type of arithmetic expression
192 1
     * @param string         $left           The left expression
193
     * @param string         $right          The left expression
194 1
     *
195 1
     * @return string
196 1
     */
197 1
    public function onArithmeticExpression(ExpressionType $expressionType, $left, $right)
198 1
    {
199 1
        $left  = strval($left);
200
        $right = strval($right);
201
        switch ($expressionType) {
202
            case ExpressionType::MULTIPLY():
203
                return $this->prepareBinaryExpression(self::MULTIPLY, $left, $right);
204
            case ExpressionType::DIVIDE():
205
                return $this->prepareBinaryExpression(self::DIVIDE, $left, $right);
206
            case ExpressionType::MODULO():
207
                return $this->prepareBinaryExpression(self::MODULO, $left, $right);
208
            case ExpressionType::ADD():
209 28
                return $this->prepareBinaryExpression(self::ADD, $left, $right);
210
            case ExpressionType::SUBTRACT():
211 28
                return $this->prepareBinaryExpression(self::SUBTRACT, $left, $right);
212 1
            default:
213
                throw new \InvalidArgumentException('onArithmeticExpression');
214 27
        }
215 27
    }
216 1
    /**
217 26
     * Call-back for relational expression.
218 1
     *
219 25
     * @param ExpressionType $expressionType The type of relation expression
220 1
     * @param string         $left           The left expression
221 24
     * @param string         $right          The left expression
222 1
     *
223 23
     * @return string
224 1
     */
225 22
    public function onRelationalExpression(ExpressionType $expressionType, $left, $right)
226 1
    {
227 21
        switch ($expressionType) {
228 1
            case ExpressionType::GREATERTHAN():
229 20
                return $this->prepareBinaryExpression(self::GREATER_THAN, strval($left), strval($right));
230 1
            case ExpressionType::GREATERTHAN_OR_EQUAL():
231 19
                return $this->prepareBinaryExpression(self::GREATER_THAN_OR_EQUAL, strval($left), strval($right));
232 2
            case ExpressionType::LESSTHAN():
233 2
                return $this->prepareBinaryExpression(self::LESS_THAN, strval($left), strval($right));
234 17
            case ExpressionType::LESSTHAN_OR_EQUAL():
235 1
                return $this->prepareBinaryExpression(self::LESS_THAN_OR_EQUAL, strval($left), strval($right));
236 16
            case ExpressionType::EQUAL():
237 1
                return $this->prepareBinaryExpression(self::EQUAL, strval($left), strval($right));
238 15
            case ExpressionType::NOTEQUAL():
239 1
                return $this->prepareBinaryExpression(self::NOT_EQUAL, strval($left), strval($right));
240 14
            default:
241 1
                throw new \InvalidArgumentException('onRelationalExpression');
242 13
        }
243 1
    }
244 12
    /**
245 1
     * Call-back for unary expression.
246 11
     *
247 1
     * @param ExpressionType $expressionType The type of unary expression
248 10
     * @param string         $child          The child expression
249 1
     *
250 9
     * @return string
251 1
     */
252 8
    public function onUnaryExpression(ExpressionType $expressionType, $child)
253 1
    {
254 7
        switch ($expressionType) {
255 1
            case ExpressionType::NEGATE():
256 6
                return $this->prepareUnaryExpression(self::NEGATE, strval($child));
257 1
            case ExpressionType::NOT_LOGICAL():
258 5
                return $this->prepareUnaryExpression(self::LOGICAL_NOT, strval($child));
259 1
            default:
260 4
                throw new \InvalidArgumentException('onUnaryExpression');
261 1
        }
262 3
    }
263 1
    /**
264 2
     * Call-back for constant expression.
265 1
     *
266 1
     * @param IType $type  The type of constant
267 1
     * @param mixed $value The value of the constant
268 1
     *
269
     * @return string
270
     */
271
    public function onConstantExpression(IType $type, $value)
272
    {
273
        if (is_bool($value)) {
274
            return var_export($value, true);
275
        } elseif (null === $value) {
276
            return var_export(null, true);
277
        }
278
        return $value;
279 13
    }
280
    /**
281
     * Call-back for property access expression.
282 13
     *
283
     * @param PropertyAccessExpression $expression The property access expression
284
     *
285
     * @return string
286
     */
287
    public function onPropertyAccessExpression(PropertyAccessExpression $expression): string
288
    {
289
        $parent   = $expression;
290
        $variable = null;
291
        do {
292 2
            $variable = $parent->getResourceProperty()->getName() . self::MEMBER_ACCESS . $variable;
293
            $parent   = $parent->getParent();
294 2
        } while ($parent != null);
295
        $variable = rtrim($variable, self::MEMBER_ACCESS);
296
        $variable = $this->getIteratorName() . self::MEMBER_ACCESS . $variable;
297
        return $variable;
298
    }
299
    /**
300
     * Call-back for function call expression.
301
     *
302
     * @param \POData\UriProcessor\QueryProcessor\FunctionDescription $functionDescription Description of the function
303
     * @param array<string>                                           $params              Parameters to the function
304
     *
305
     * @return string
306
     */
307
    public function onFunctionCallExpression($functionDescription, $params)
308
    {
309
        if (!array_key_exists($functionDescription->name, $this->functionDescriptionParsers)) {
310
            throw new \InvalidArgumentException('onFunctionCallExpression');
311
        }
312
        return $this->functionDescriptionParsers[$functionDescription->name]($params);
313
    }
314
    /**
315
     * To format binary expression.
316
     *
317
     * @param string $operator The binary operator
318
     * @param string $left     The left operand
319
     * @param string $right    The right operand
320
     *
321
     * @return string
322
     */
323
    private function prepareBinaryExpression(string $operator, string $left, string $right)
324
    {
325
        return self::OPEN_BRACKET . $left . ' ' . $operator . ' ' . $right . self::CLOSE_BRACKET;
326
    }
327
    /**
328
     * To format unary expression.
329
     *
330
     * @param string $operator The unary operator
331
     * @param string $child    The operand
332
     *
333
     * @return string
334
     */
335
    private function prepareUnaryExpression(string $operator, string $child)
336
    {
337
        return $operator . self::OPEN_BRACKET . $child . self::CLOSE_BRACKET;
338
    }
339
}
340