Completed
Push — master ( 6cda03...6ca798 )
by Christopher
05:56
created

MySQLExpressionProvider::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace POData\Providers\Expression;
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
12
class MySQLExpressionProvider 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
34
    /**
35
     * The type of the resource pointed by the resource path segment.
36
     *
37
     * @var ResourceType
38
     */
39
    private $resourceType;
40
41
    private $entityMapping;
42
43
    /**
44
     * Constructs new instance of MySQLExpressionProvider.
45
     */
46
    public function __construct()
47
    {
48
        $this->entityMapping = array();
49
    }
50
51
    /**
52
     * Get the name of the iterator.
53
     *
54
     * @return string
55
     */
56
    public function getIteratorName()
57
    {
58
        return null;
59
    }
60
61
    /**
62
     * call-back for setting the resource type.
63
     *
64
     * @param ResourceType $resourceType The resource type on which the filter is going to be applied
65
     */
66
    public function setResourceType(ResourceType $resourceType)
67
    {
68
        $this->resourceType = $resourceType;
69
    }
70
71
    /**
72
     * Call-back for logical expression.
73
     *
74
     * @param ExpressionType $expressionType The type of logical expression
75
     * @param string         $left           The left expression
76
     * @param string         $right          The left expression
77
     *
78
     * @return string
79
     */
80 View Code Duplication
    public function onLogicalExpression($expressionType, $left, $right)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
81
    {
82
        switch ($expressionType) {
83
            case ExpressionType::AND_LOGICAL:
84
                return $this->_prepareBinaryExpression(self::LOGICAL_AND, $left, $right);
85
86
            case ExpressionType::OR_LOGICAL:
87
                return $this->_prepareBinaryExpression(self::LOGICAL_OR, $left, $right);
88
89
            default:
90
                throw new \InvalidArgumentException('onLogicalExpression');
91
        }
92
    }
93
94
    /**
95
     * Call-back for arithmetic expression.
96
     *
97
     * @param ExpressionType $expressionType The type of arithmetic expression
98
     * @param string         $left           The left expression
99
     * @param string         $right          The left expression
100
     *
101
     * @return string
102
     */
103 View Code Duplication
    public function onArithmeticExpression($expressionType, $left, $right)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
104
    {
105
        switch ($expressionType) {
106
            case ExpressionType::MULTIPLY:
107
                return $this->_prepareBinaryExpression(self::MULTIPLY, $left, $right);
108
109
            case ExpressionType::DIVIDE:
110
                return $this->_prepareBinaryExpression(self::DIVIDE, $left, $right);
111
112
            case ExpressionType::MODULO:
113
                return $this->_prepareBinaryExpression(self::MODULO, $left, $right);
114
115
            case ExpressionType::ADD:
116
                return $this->_prepareBinaryExpression(self::ADD, $left, $right);
117
118
            case ExpressionType::SUBTRACT:
119
                return $this->_prepareBinaryExpression(self::SUBTRACT, $left, $right);
120
121
            default:
122
                throw new \InvalidArgumentException('onArithmeticExpression');
123
        }
124
    }
125
126
    /**
127
     * Call-back for relational expression.
128
     *
129
     * @param ExpressionType $expressionType The type of relation expression
130
     * @param string         $left           The left expression
131
     * @param string         $right          The left expression
132
     *
133
     * @return string
134
     */
135 View Code Duplication
    public function onRelationalExpression($expressionType, $left, $right)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
136
    {
137
        switch ($expressionType) {
138
            case ExpressionType::GREATERTHAN:
139
                return $this->_prepareBinaryExpression(self::GREATER_THAN, $left, $right);
140
141
            case ExpressionType::GREATERTHAN_OR_EQUAL:
142
                return $this->_prepareBinaryExpression(self::GREATER_THAN_OR_EQUAL, $left, $right);
143
144
            case ExpressionType::LESSTHAN:
145
                return $this->_prepareBinaryExpression(self::LESS_THAN, $left, $right);
146
147
            case ExpressionType::LESSTHAN_OR_EQUAL:
148
                return $this->_prepareBinaryExpression(self::LESS_THAN_OR_EQUAL, $left, $right);
149
150
            case ExpressionType::EQUAL:
151
                return $this->_prepareBinaryExpression(self::EQUAL, $left, $right);
152
153
            case ExpressionType::NOTEQUAL:
154
                return $this->_prepareBinaryExpression(self::NOT_EQUAL, $left, $right);
155
156
            default:
157
                throw new \InvalidArgumentException('onRelationalExpression');
158
        }
159
    }
160
161
    /**
162
     * Call-back for unary expression.
163
     *
164
     * @param ExpressionType $expressionType The type of unary expression
165
     * @param string         $child          The child expression
166
     *
167
     * @return string
168
     */
169 View Code Duplication
    public function onUnaryExpression($expressionType, $child)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
    {
171
        switch ($expressionType) {
172
            case ExpressionType::NEGATE:
173
                return $this->_prepareUnaryExpression(self::NEGATE, $child);
174
175
            case ExpressionType::NOT_LOGICAL:
176
                return $this->_prepareUnaryExpression(self::LOGICAL_NOT, $child);
177
178
            default:
179
                throw new \InvalidArgumentException('onUnaryExpression');
180
        }
181
    }
182
183
    /**
184
     * Call-back for constant expression.
185
     *
186
     * @param IType $type  The type of constant
187
     * @param mixed $value The value of the constant
188
     *
189
     * @return string
190
     */
191 View Code Duplication
    public function onConstantExpression(IType $type, $value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
192
    {
193
        if (is_bool($value)) {
194
            return var_export($value, true);
195
        } elseif (is_null($value)) {
196
            return var_export(null, true);
197
        }
198
199
        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...
200
    }
201
202
    /**
203
     * Call-back for property access expression.
204
     *
205
     * @param PropertyAccessExpression $expression The property access expression
206
     *
207
     * @return string
208
     */
209
    public function onPropertyAccessExpression($expression)
210
    {
211
        if (null == $expression) {
212
            throw new \InvalidArgumentException('onPropertyAccessExpression - expression null');
213
        }
214
        if (!($expression instanceof PropertyAccessExpression)) {
215
            throw new \InvalidArgumentException('onPropertyAccessExpression - expression is incorrect type');
216
        }
217
        if (null == $this->resourceType) {
218
            throw new \InvalidArgumentException('onPropertyAccessExpression - resourceType null');
219
        }
220
        if (null == $this->resourceType->getName()) {
221
            throw new \InvalidArgumentException('onPropertyAccessExpression - resourceType has no name');
222
        }
223
        if (null == $expression->getResourceProperty()) {
224
            throw new \InvalidArgumentException('onPropertyAccessExpression - expression has no resource property');
225
        }
226
        $parent = $expression;
227
        $variable = null;
0 ignored issues
show
Unused Code introduced by
$variable is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
228
        $entityTypeName = $this->resourceType->getName();
229
        $propertyName = $parent->getResourceProperty()->getName();
230
        if (is_array($this->entityMapping)) {
231
            if (array_key_exists($entityTypeName, $this->entityMapping)) {
232
                if (array_key_exists($propertyName, $this->entityMapping[$entityTypeName])) {
233
                    return $this->entityMapping[$entityTypeName][$propertyName];
234
                }
235
            }
236
        }
237
238
        return $propertyName;
239
    }
240
241
    /**
242
     * Call-back for function call expression.
243
     *
244
     * @param FunctionDescription $functionDescription Description of the function
245
     * @param array<string>       $params              Parameters to the function
246
     *
247
     * @return string
248
     */
249
    public function onFunctionCallExpression($functionDescription, $params)
250
    {
251
        switch ($functionDescription->name) {
252
            case ODataConstants::STRFUN_COMPARE:
253
                return "STRCMP($params[0], $params[1])";
254
255
            case ODataConstants::STRFUN_ENDSWITH:
256
                return "(STRCMP($params[1],RIGHT($params[0],LENGTH($params[1]))) = 0)";
257
258
            case ODataConstants::STRFUN_INDEXOF:
259
                return "INSTR($params[0], $params[1]) - 1";
260
261
            case ODataConstants::STRFUN_REPLACE:
262
                return "REPLACE($params[0],$params[1],$params[2])";
263
264
            case ODataConstants::STRFUN_STARTSWITH:
265
                return "(STRCMP($params[1],LEFT($params[0],LENGTH($params[1]))) = 0)";
266
267
            case ODataConstants::STRFUN_TOLOWER:
268
                return "LOWER($params[0])";
269
270
            case ODataConstants::STRFUN_TOUPPER:
271
                return "UPPER($params[0])";
272
273
            case ODataConstants::STRFUN_TRIM:
274
                return "TRIM($params[0])";
275
276 View Code Duplication
            case ODataConstants::STRFUN_SUBSTRING:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
277
                return count($params) == 3 ?
278
                    "SUBSTRING($params[0], $params[1] + 1, $params[2])" : "SUBSTRING($params[0], $params[1] + 1)";
279
280
            case ODataConstants::STRFUN_SUBSTRINGOF:
281
                return "(LOCATE($params[0], $params[1]) > 0)";
282
283
            case ODataConstants::STRFUN_CONCAT:
284
                return "CONCAT($params[0],$params[1])";
285
286
            case ODataConstants::STRFUN_LENGTH:
287
                return "LENGTH($params[0])";
288
289
            case ODataConstants::GUIDFUN_EQUAL:
290
                return "STRCMP($params[0], $params[1])";
291
292
            case ODataConstants::DATETIME_COMPARE:
293
                return "DATETIMECMP($params[0]; $params[1])";
294
295
            case ODataConstants::DATETIME_YEAR:
296
                return 'EXTRACT(YEAR from ' . $params[0] . ')';
297
298
            case ODataConstants::DATETIME_MONTH:
299
                return 'EXTRACT(MONTH from ' . $params[0] . ')';
300
301
            case ODataConstants::DATETIME_DAY:
302
                return 'EXTRACT(DAY from ' . $params[0] . ')';
303
304
            case ODataConstants::DATETIME_HOUR:
305
                return 'EXTRACT(HOUR from ' . $params[0] . ')';
306
307
            case ODataConstants::DATETIME_MINUTE:
308
                return 'EXTRACT(MINUTE from ' . $params[0] . ')';
309
310
            case ODataConstants::DATETIME_SECOND:
311
                return 'EXTRACT(SECOND from ' . $params[0] . ')';
312
313
            case ODataConstants::MATHFUN_ROUND:
314
                return "ROUND($params[0])";
315
316
            case ODataConstants::MATHFUN_CEILING:
317
                return "CEIL($params[0])";
318
319
            case ODataConstants::MATHFUN_FLOOR:
320
                return "FLOOR($params[0])";
321
322
            case ODataConstants::BINFUL_EQUAL:
323
                return "($params[0] = $params[1])";
324
325
            case 'is_null':
326
                return "is_null($params[0])";
327
328
            default:
329
                throw new \InvalidArgumentException('onFunctionCallExpression');
330
        }
331
    }
332
333
    /**
334
     * To format binary expression.
335
     *
336
     * @param string $operator The binary operator
337
     * @param string $left     The left operand
338
     * @param string $right    The right operand
339
     *
340
     * @return string
341
     */
342
    private function _prepareBinaryExpression($operator, $left, $right)
343
    {
344
        //DATETIMECMP
345
        if (0 == substr_compare($left, 'DATETIMECMP', 0, 11)) {
346
            $str = explode(';', $left, 2);
347
            $str[0] = str_replace('DATETIMECMP', '', $str[0]);
348
349
            return self::OPEN_BRACKET
350
                .$str[0] . ' ' . $operator
351
                .' ' . $str[1] . self::CLOSE_BRACKET;
352
        }
353
354
        return self::OPEN_BRACKET . $left . ' ' . $operator . ' ' . $right . self::CLOSE_BRACKET;
355
    }
356
357
    /**
358
     * To format unary expression.
359
     *
360
     * @param string $operator The unary operator
361
     * @param string $child    The operand
362
     *
363
     * @return string
364
     */
365
    private function _prepareUnaryExpression($operator, $child)
366
    {
367
        return $operator . self::OPEN_BRACKET . $child . self::CLOSE_BRACKET;
368
    }
369
}
370