Test Failed
Push — master ( ddc20f...f135a5 )
by Alex
03:56
created

LaravelExpressionProvider::onLogicalExpression()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

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