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

PHPExpressionProvider   C

Complexity

Total Complexity 61

Size/Duplication

Total Lines 355
Duplicated Lines 24.23 %

Coupling/Cohesion

Components 2
Dependencies 4

Importance

Changes 0
Metric Value
wmc 61
lcom 2
cbo 4
dl 86
loc 355
rs 6.018
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getIteratorName() 0 4 1
A setResourceType() 0 4 1
A onLogicalExpression() 13 13 3
B onArithmeticExpression() 22 22 6
C onRelationalExpression() 25 25 7
A onUnaryExpression() 13 13 3
A onConstantExpression() 10 10 3
C onPropertyAccessExpression() 0 30 7
D onFunctionCallExpression() 3 83 27
A _prepareBinaryExpression() 0 5 1
A _prepareUnaryExpression() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PHPExpressionProvider often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PHPExpressionProvider, and based on these observations, apply Extract Interface, too.

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