Completed
Pull Request — master (#122)
by Alex
04:43
created

prepareBinaryExpression()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 3
crap 2
1
<?php
2
3
namespace AlgoWeb\PODataLaravel\Query;
4
5
use POData\Common\ODataConstants;
6
use POData\Providers\Expression\IExpressionProvider;
7
use POData\Providers\Metadata\ResourceType;
8
use POData\Providers\Metadata\Type\IType;
9
use POData\UriProcessor\QueryProcessor\ExpressionParser\Expressions\ExpressionType;
10
use POData\UriProcessor\QueryProcessor\ExpressionParser\Expressions\PropertyAccessExpression;
11
12
class LaravelExpressionProvider implements IExpressionProvider
13
{
14
    const ADD = '+';
15
    const CLOSE_BRACKET = ')';
16
    const COMMA = ',';
17
    const DIVIDE = '/';
18
    const SUBTRACT = '-';
19
    const EQUAL = '==';
20
    const GREATER_THAN = '>';
21
    const GREATER_THAN_OR_EQUAL = '>=';
22
    const LESS_THAN = '<';
23
    const LESS_THAN_OR_EQUAL = '<=';
24
    const LOGICAL_AND = '&&';
25
    const LOGICAL_NOT = '!';
26
    const LOGICAL_OR = '||';
27
    const MEMBER_ACCESS = '->';
28
    const MODULO = '%';
29
    const MULTIPLY = '*';
30
    const NEGATE = '-';
31
    const NOT_EQUAL = '!=';
32
    const OPEN_BRACKET = '(';
33
    const TYPE_NAMESPACE = 'POData\\Providers\\Metadata\\Type\\';
34
35
    private $functionDescriptionParsers;
36
37
    /**
38
     * The name of iterator.
39
     *
40
     * @var string
41
     */
42
    private $iteratorName;
43
    /**
44
     * The type of the resource pointed by the resource path segment.
45
     *
46
     * @var ResourceType
47
     */
48
    private $resourceType;
49 52
50
    public function __construct()
51 52
    {
52
        $this->functionDescriptionParsers[ODataConstants::STRFUN_COMPARE] = function ($params) {
53
            return 'strcmp(' . $params[0] . ', ' . $params[1] . ')';
54
        };
55
        $this->functionDescriptionParsers[ODataConstants::STRFUN_ENDSWITH] = function ($params) {
56
            return '(strcmp(substr(' . $params[0] . ', strlen(' . $params[0] . ') - strlen(' . $params[1] . ')), '
57 1
                   .$params[1] . ') === 0)';
58
        };
59 1
        $this->functionDescriptionParsers[ODataConstants::STRFUN_INDEXOF] = function ($params) {
60
            return 'strpos(' . $params[0] . ', ' . $params[1] . ')';
61
        };
62 View Code Duplication
        $this->functionDescriptionParsers[ODataConstants::STRFUN_REPLACE] = function ($params) {
63
            return 'str_replace(' . $params[1] . ', ' . $params[2] . ', ' . $params[0] . ')';
64
        };
65
        $this->functionDescriptionParsers[ODataConstants::STRFUN_STARTSWITH] = function ($params) {
66
            return '(strpos(' . $params[0] . ', ' . $params[1] . ') === 0)';
67 1
        };
68
        $this->functionDescriptionParsers[ODataConstants::STRFUN_TOLOWER] = function ($params) {
69 1
            return 'strtolower(' . $params[0] . ')';
70 1
        };
71 1
        $this->functionDescriptionParsers[ODataConstants::STRFUN_TOUPPER] = function ($params) {
72
            return 'strtoupper(' . $params[0] . ')';
73
        };
74
        $this->functionDescriptionParsers[ODataConstants::STRFUN_TRIM] = function ($params) {
75
            return 'trim(' . $params[0] . ')';
76
        };
77
        $this->functionDescriptionParsers[ODataConstants::STRFUN_SUBSTRING] = function ($params) {
78
            return count($params) == 3 ?
79
                'substr(' . $params[0] . ', ' . $params[1] . ', ' . $params[2] . ')' : 'substr(' . $params[0] . ', ' . $params[1] . ')';
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
80
        };
81 3
        $this->functionDescriptionParsers[ODataConstants::STRFUN_SUBSTRINGOF] = function ($params) {
82
            return '(strpos(' . $params[1] . ', ' . $params[0] . ') !== false)';
83
        };
84 3
        $this->functionDescriptionParsers[ODataConstants::STRFUN_CONCAT] = function ($params) {
85 1
            return $params[0] . ' . ' . $params[1];
86 2
        };
87 1
        $this->functionDescriptionParsers[ODataConstants::STRFUN_LENGTH] = function ($params) {
88 1
            return 'strlen(' . $params[0] . ')';
89 1
        };
90 1 View Code Duplication
        $this->functionDescriptionParsers[ODataConstants::GUIDFUN_EQUAL] = function ($params) {
91
            return self::TYPE_NAMESPACE . 'Guid::guidEqual(' . $params[0] . ', ' . $params[1] . ')';
92
        };
93 View Code Duplication
        $this->functionDescriptionParsers[ODataConstants::DATETIME_COMPARE] = function ($params) {
94
            return self::TYPE_NAMESPACE . 'DateTime::dateTimeCmp(' . $params[0] . ', ' . $params[1] . ')';
95
        };
96
        $this->functionDescriptionParsers[ODataConstants::DATETIME_YEAR] = function ($params) {
97
            return self::TYPE_NAMESPACE . 'DateTime::year(' . $params[0] . ')';
98
        };
99
        $this->functionDescriptionParsers[ODataConstants::DATETIME_MONTH] = function ($params) {
100
            return self::TYPE_NAMESPACE . 'DateTime::month(' . $params[0] . ')';
101 6
        };
102
        $this->functionDescriptionParsers[ODataConstants::DATETIME_DAY] = function ($params) {
103
            return self::TYPE_NAMESPACE . 'DateTime::day(' . $params[0] . ')';
104 6
        };
105 1
        $this->functionDescriptionParsers[ODataConstants::DATETIME_HOUR] = function ($params) {
106 5
            return self::TYPE_NAMESPACE . 'DateTime::hour(' . $params[0] . ')';
107 1
        };
108 4
        $this->functionDescriptionParsers[ODataConstants::DATETIME_MINUTE] = function ($params) {
109 1
            return self::TYPE_NAMESPACE . 'DateTime::minute(' . $params[0] . ')';
110 3
        };
111 1
        $this->functionDescriptionParsers[ODataConstants::DATETIME_SECOND] = function ($params) {
112 2
            return self::TYPE_NAMESPACE . 'DateTime::second(' . $params[0] . ')';
113 1
        };
114 1
        $this->functionDescriptionParsers[ODataConstants::MATHFUN_ROUND] = function ($params) {
115 1
            return 'round(' . $params[0] . ')';
116 1
        };
117
        $this->functionDescriptionParsers[ODataConstants::MATHFUN_CEILING] = function ($params) {
118
            return 'ceil(' . $params[0] . ')';
119
        };
120
        $this->functionDescriptionParsers[ODataConstants::MATHFUN_FLOOR] = function ($params) {
121
            return 'floor(' . $params[0] . ')';
122
        };
123 View Code Duplication
        $this->functionDescriptionParsers[ODataConstants::BINFUL_EQUAL] = function ($params) {
124
            return self::TYPE_NAMESPACE . 'Binary::binaryEqual(' . $params[0] . ', ' . $params[1] . ')';
125
        };
126
        $this->functionDescriptionParsers['is_null'] = function ($params) {
127 7
            return 'is_null(' . $params[0] . ')';
128
        };
129
    }
130 7
    /**
131 1
     * Get the name of the iterator.
132 6
     *
133 1
     * @return string
134 5
     */
135 1
    public function getIteratorName()
136 4
    {
137 1
        return $this->iteratorName;
138 3
    }
139 1
140 2
    /**
141 1
     * Get the resource type.
142 1
     *
143 1
     * @return object|null
144 1
     */
145
    public function getResourceType()
146
    {
147
        return $this->resourceType;
148
    }
149
150
    /**
151
     * call-back for setting the resource type.
152
     *
153
     * @param ResourceType $resourceType The resource type on which the filter
154 3
     *                                   is going to be applied
155
     */
156
    public function setResourceType(ResourceType $resourceType)
157 3
    {
158 1
        $this->iteratorName = '$' . $resourceType->getName();
159 2
        $this->resourceType = $resourceType;
160 1
    }
161 1
    /**
162 1
     * Call-back for logical expression.
163 1
     *
164
     * @param ExpressionType $expressionType The type of logical expression
165
     * @param string         $left           The left expression
166
     * @param string         $right          The left expression
167
     *
168
     * @return string
169
     */
170 View Code Duplication
    public function onLogicalExpression($expressionType, $left, $right)
171
    {
172
        $type = $this->unpackExpressionType($expressionType);
173 8
        switch ($type) {
174
            case ExpressionType::AND_LOGICAL:
175 8
                return $this->prepareBinaryExpression(self::LOGICAL_AND, $left, $right);
176 2
            case ExpressionType::OR_LOGICAL:
177 6
                return $this->prepareBinaryExpression(self::LOGICAL_OR, $left, $right);
178 1
            default:
179
                throw new \InvalidArgumentException('onLogicalExpression');
180 5
        }
181
    }
182
    /**
183
     * Call-back for arithmetic expression.
184
     *
185
     * @param ExpressionType $expressionType The type of arithmetic expression
186
     * @param string         $left           The left expression
187
     * @param string         $right          The left expression
188
     *
189 1
     * @return string
190
     */
191 1
    public function onArithmeticExpression($expressionType, $left, $right)
192 1
    {
193
        $type = $this->unpackExpressionType($expressionType);
194 1
        switch ($type) {
195 1
            case ExpressionType::MULTIPLY:
196 1
                return $this->prepareBinaryExpression(self::MULTIPLY, $left, $right);
197 1
            case ExpressionType::DIVIDE:
198 1
                return $this->prepareBinaryExpression(self::DIVIDE, $left, $right);
199 1
            case ExpressionType::MODULO:
200
                return $this->prepareBinaryExpression(self::MODULO, $left, $right);
201
            case ExpressionType::ADD:
202
                return $this->prepareBinaryExpression(self::ADD, $left, $right);
203
            case ExpressionType::SUBTRACT:
204
                return $this->prepareBinaryExpression(self::SUBTRACT, $left, $right);
205
            default:
206
                throw new \InvalidArgumentException('onArithmeticExpression');
207
        }
208
    }
209 28
    /**
210
     * Call-back for relational expression.
211 28
     *
212 1
     * @param ExpressionType $expressionType The type of relation expression
213
     * @param string         $left           The left expression
214 27
     * @param string         $right          The left expression
215 27
     *
216 1
     * @return string
217 26
     */
218 1
    public function onRelationalExpression($expressionType, $left, $right)
219 25
    {
220 1
        $type = $this->unpackExpressionType($expressionType);
221 24
        switch ($type) {
222 1
            case ExpressionType::GREATERTHAN:
223 23
                return $this->prepareBinaryExpression(self::GREATER_THAN, $left, $right);
224 1
            case ExpressionType::GREATERTHAN_OR_EQUAL:
225 22
                return $this->prepareBinaryExpression(self::GREATER_THAN_OR_EQUAL, $left, $right);
226 1
            case ExpressionType::LESSTHAN:
227 21
                return $this->prepareBinaryExpression(self::LESS_THAN, $left, $right);
228 1
            case ExpressionType::LESSTHAN_OR_EQUAL:
229 20
                return $this->prepareBinaryExpression(self::LESS_THAN_OR_EQUAL, $left, $right);
230 1
            case ExpressionType::EQUAL:
231 19
                return $this->prepareBinaryExpression(self::EQUAL, $left, $right);
232 2
            case ExpressionType::NOTEQUAL:
233 2
                return $this->prepareBinaryExpression(self::NOT_EQUAL, $left, $right);
234 17
            default:
235 1
                throw new \InvalidArgumentException('onRelationalExpression');
236 16
        }
237 1
    }
238 15
    /**
239 1
     * Call-back for unary expression.
240 14
     *
241 1
     * @param ExpressionType $expressionType The type of unary expression
242 13
     * @param string         $child          The child expression
243 1
     *
244 12
     * @return string
245 1
     */
246 11 View Code Duplication
    public function onUnaryExpression($expressionType, $child)
247 1
    {
248 10
        $type = $this->unpackExpressionType($expressionType);
249 1
        switch ($type) {
250 9
            case ExpressionType::NEGATE:
251 1
                return $this->prepareUnaryExpression(self::NEGATE, $child);
252 8
            case ExpressionType::NOT_LOGICAL:
253 1
                return $this->prepareUnaryExpression(self::LOGICAL_NOT, $child);
254 7
            default:
255 1
                throw new \InvalidArgumentException('onUnaryExpression');
256 6
        }
257 1
    }
258 5
    /**
259 1
     * Call-back for constant expression.
260 4
     *
261 1
     * @param IType $type  The type of constant
262 3
     * @param mixed $value The value of the constant
263 1
     *
264 2
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|object|integer|double|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
265 1
     */
266 1
    public function onConstantExpression(IType $type, $value)
267 1
    {
268 1
        if (is_bool($value)) {
269
            return var_export($value, true);
270
        } elseif (null === $value) {
271
            return var_export(null, true);
272
        }
273
        return $value;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $value; (object|integer|double|string|array) is incompatible with the return type declared by the interface POData\Providers\Express...r::onConstantExpression of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
274
    }
275
    /**
276
     * Call-back for property access expression.
277
     *
278
     * @param PropertyAccessExpression $expression The property access expression
279 13
     *
280
     * @return string
281
     */
282 13
    public function onPropertyAccessExpression($expression)
283
    {
284
        $parent = $expression;
285
        $variable = null;
286
        do {
287
            $variable = $parent->getResourceProperty()->getName() . self::MEMBER_ACCESS . $variable;
288
            $parent = $parent->getParent();
289
        } while ($parent != null);
290
        $variable = rtrim($variable, self::MEMBER_ACCESS);
291
        $variable = $this->getIteratorName() . self::MEMBER_ACCESS . $variable;
292 2
        return $variable;
293
    }
294 2
    /**
295
     * Call-back for function call expression.
296
     *
297
     * @param \POData\UriProcessor\QueryProcessor\FunctionDescription $functionDescription Description of the function
298
     * @param array<string>                                           $params              Parameters to the function
299
     *
300
     * @return string
301
     */
302
    public function onFunctionCallExpression($functionDescription, $params)
303
    {
304
        if (!isset($functionDescription)) {
305
            throw new \InvalidArgumentException('onFunctionCallExpression');
306
        }
307
        if (!array_key_exists($functionDescription->name, $this->functionDescriptionParsers)) {
308
            throw new \InvalidArgumentException('onFunctionCallExpression');
309
        }
310
        return $this->functionDescriptionParsers[$functionDescription->name]($params);
311
    }
312
    /**
313
     * To format binary expression.
314
     *
315
     * @param string $operator The binary operator
316
     * @param string $left     The left operand
317
     * @param string $right    The right operand
318
     *
319
     * @return string
320
     */
321
    private function prepareBinaryExpression($operator, $left, $right)
322
    {
323
        return self::OPEN_BRACKET . $left . ' ' . $operator . ' ' . $right . self::CLOSE_BRACKET;
324
    }
325
    /**
326
     * To format unary expression.
327
     *
328
     * @param string $operator The unary operator
329
     * @param string $child    The operand
330
     *
331
     * @return string
332
     */
333
    private function prepareUnaryExpression($operator, $child)
334
    {
335
        return $operator . self::OPEN_BRACKET . $child . self::CLOSE_BRACKET;
336
    }
337
338
    /**
339
     * @param $expressionType
340
     * @return mixed
341
     */
342
    private function unpackExpressionType($expressionType)
343
    {
344
        if ($expressionType instanceof ExpressionType) {
345
            $type = $expressionType->getValue();
346
        } else {
347
            $type = $expressionType;
348
        }
349
        return $type;
350
    }
351
}
352