PHPExpressionProvider::onFunctionCallExpression()   D
last analyzed

Complexity

Conditions 27
Paths 27

Size

Total Lines 84
Code Lines 56

Duplication

Lines 4
Ratio 4.76 %

Importance

Changes 0
Metric Value
dl 4
loc 84
rs 4.7908
c 0
b 0
f 0
cc 27
eloc 56
nc 27
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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