Completed
Pull Request — master (#208)
by Enrico
03:43
created

Expression::passProperty()   C

Complexity

Conditions 11
Paths 11

Size

Total Lines 54
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
cc 11
eloc 34
nc 11
nop 1
dl 0
loc 54
ccs 0
cts 36
cp 0
crap 132
rs 6.6153
c 0
b 0
f 0

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
 * @author Patsura Dmitry https://github.com/ovr <[email protected]>
4
 */
5
6
namespace PHPSA\Compiler;
7
8
use InvalidArgumentException;
9
use phpDocumentor\Reflection\DocBlockFactory;
10
use PHPSA\Check;
11
use PHPSA\CompiledExpression;
12
use PHPSA\Compiler\Event\ExpressionBeforeCompile;
13
use PHPSA\Context;
14
use PhpParser\Node;
15
use PHPSA\Definition\ClassDefinition;
16
use PHPSA\Exception\RuntimeException;
17
use PHPSA\Variable;
18
use PHPSA\Compiler\Expression\AbstractExpressionCompiler;
19
use Webiny\Component\EventManager\EventManager;
20
21
class Expression
0 ignored issues
show
Complexity introduced by
This class has a complexity of 108 which exceeds the configured maximum of 50.

The class complexity is the sum of the complexity of all methods. A very high value is usually an indication that your class does not follow the single reponsibility principle and does more than one job.

Some resources for further reading:

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

Loading history...
22
{
23
    /**
24
     * @var Context
25
     */
26
    protected $context;
27
28
    /**
29
     * @var EventManager
30
     */
31
    protected $eventManager;
32
33
    /**
34
     * @param Context $context
35
     */
36 852
    public function __construct(Context $context, EventManager $eventManager)
37
    {
38 852
        $this->context = $context;
39 852
        $this->eventManager = $eventManager;
40 852
    }
41
42
    /**
43
     * @param $expr
44
     * @return ExpressionCompilerInterface|AbstractExpressionCompiler
45
     */
46 842
    protected function factory($expr)
47
    {
48 842
        switch (get_class($expr)) {
49
            /**
50
             * Call(s)
51
             */
52 842
            case Node\Expr\MethodCall::class:
53
                return new Expression\MethodCall();
54 842
            case Node\Expr\FuncCall::class:
55 10
                return new Expression\FunctionCall();
56 838
            case Node\Expr\StaticCall::class:
57 1
                return new Expression\StaticCall();
58
            /**
59
             * Operators
60
             */
61 837
            case Node\Expr\New_::class:
62 2
                return new Expression\Operators\NewOp();
63 837
            case Node\Expr\Instanceof_::class:
64
                return new Expression\Operators\InstanceOfOp();
65
            /**
66
             * AssignOp
67
             */
68 837
            case Node\Expr\AssignOp\Pow::class:
69 21
                return new Expression\AssignOp\Pow();
70 816
            case Node\Expr\AssignOp\Plus::class:
71 17
                return new Expression\AssignOp\Plus();
72 799
            case Node\Expr\AssignOp\Minus::class:
73 20
                return new Expression\AssignOp\Minus();
74 779
            case Node\Expr\AssignOp\Mul::class:
75 20
                return new Expression\AssignOp\Mul();
76 759
            case Node\Expr\AssignOp\Div::class:
77 18
                return new Expression\AssignOp\Div();
78 741
            case Node\Expr\AssignOp\Mod::class:
79 11
                return new Expression\AssignOp\Mod();
80 730
            case Node\Expr\AssignOp\BitwiseOr::class:
81 12
                return new Expression\AssignOp\BitwiseOr();
82 718
            case Node\Expr\AssignOp\BitwiseAnd::class:
83 12
                return new Expression\AssignOp\BitwiseAnd();
84 706
            case Node\Expr\AssignOp\BitwiseXor::class:
85 12
                return new Expression\AssignOp\BitwiseXor();
86 694
            case Node\Expr\AssignOp\Concat::class:
87 14
                return new Expression\AssignOp\Concat();
88 680
            case Node\Expr\AssignOp\ShiftLeft::class:
89 12
                return new Expression\AssignOp\ShiftLeft();
90 668
            case Node\Expr\AssignOp\ShiftRight::class:
91 12
                return new Expression\AssignOp\ShiftRight();
92
93
            /**
94
             * BinaryOp
95
             */
96 656
            case Node\Expr\BinaryOp\Identical::class:
97 28
                return new Expression\BinaryOp\Identical();
98 628
            case Node\Expr\BinaryOp\Concat::class:
99 15
                return new Expression\Operators\Concat();
100 614
            case Node\Expr\BinaryOp\NotIdentical::class:
101 14
                return new Expression\BinaryOp\NotIdentical();
102 600
            case Node\Expr\BinaryOp\Equal::class:
103 43
                return new Expression\BinaryOp\Equal();
104 571
            case Node\Expr\BinaryOp\NotEqual::class:
105 17
                return new Expression\BinaryOp\NotEqual();
106 554
            case Node\Expr\BinaryOp\Spaceship::class:
107 11
                return new Expression\BinaryOp\SpaceShip();
108 545
            case Node\Expr\BinaryOp\Coalesce::class:
109 3
                return new Expression\BinaryOp\Coalesce();
110
                
111
            /**
112
             * @link http://php.net/manual/en/language.operators.increment.php
113
             */
114 543
            case Node\Expr\PostInc::class:
115 8
                return new Expression\Operators\PostInc();
116 535
            case Node\Expr\PostDec::class:
117 8
                return new Expression\Operators\PostDec();
118 527
            case Node\Expr\PreInc::class:
119 9
                return new Expression\Operators\PreInc();
120 519
            case Node\Expr\PreDec::class:
121 9
                return new Expression\Operators\PreDec();
122
            /**
123
             * Arithmetical
124
             */
125 511
            case Node\Expr\BinaryOp\Div::class:
126 37
                return new Expression\Operators\Arithmetical\Div();
127 474
            case Node\Expr\BinaryOp\Plus::class:
128 45
                return new Expression\Operators\Arithmetical\Plus();
129 433
            case Node\Expr\BinaryOp\Minus::class:
130 24
                return new Expression\Operators\Arithmetical\Minus();
131 409
            case Node\Expr\BinaryOp\Mul::class:
132 35
                return new Expression\Operators\Arithmetical\Mul();
133 374
            case Node\Expr\BinaryOp\Mod::class:
134 35
                return new Expression\Operators\Arithmetical\Mod();
135 339
            case Node\Expr\BinaryOp\Pow::class:
136 21
                return new Expression\Operators\Arithmetical\Pow();
137
138
            /**
139
             * Bitwise
140
             * @link http://php.net/manual/ru/language.operators.bitwise.php
141
             */
142 318
            case Node\Expr\BinaryOp\BitwiseOr::class:
143 12
                return new Expression\Operators\Bitwise\BitwiseOr();
144 306
            case Node\Expr\BinaryOp\BitwiseXor::class:
145 12
                return new Expression\Operators\Bitwise\BitwiseXor();
146 294
            case Node\Expr\BinaryOp\BitwiseAnd::class:
147 12
                return new Expression\Operators\Bitwise\BitwiseAnd();
148 282
            case Node\Expr\BinaryOp\ShiftRight::class:
149 12
                return new Expression\Operators\Bitwise\ShiftRight();
150 270
            case Node\Expr\BinaryOp\ShiftLeft::class:
151 12
                return new Expression\Operators\Bitwise\ShiftLeft();
152 258
            case Node\Expr\BitwiseNot::class:
153 6
                return new Expression\Operators\Bitwise\BitwiseNot();
154
            /**
155
             * Logical
156
             */
157 253
            case Node\Expr\BinaryOp\BooleanOr::class:
158 17
                return new Expression\Operators\Logical\BooleanOr();
159 238
            case Node\Expr\BinaryOp\BooleanAnd::class:
160 15
                return new Expression\Operators\Logical\BooleanAnd();
161 225
            case Node\Expr\BooleanNot::class:
162 10
                return new Expression\Operators\Logical\BooleanNot();
163 217
            case Node\Expr\BinaryOp\LogicalAnd::class:
164 15
                return new Expression\Operators\Logical\LogicalAnd();
165 204
            case Node\Expr\BinaryOp\LogicalOr::class:
166 17
                return new Expression\Operators\Logical\LogicalOr();
167 189
            case Node\Expr\BinaryOp\LogicalXor::class:
168 17
                return new Expression\Operators\Logical\LogicalXor();
169
170
            /**
171
             * Comparison
172
             */
173 174
            case Node\Expr\BinaryOp\Greater::class:
174 13
                return new Expression\Operators\Comparison\Greater();
175 162
            case Node\Expr\BinaryOp\GreaterOrEqual::class:
176 12
                return new Expression\Operators\Comparison\GreaterOrEqual();
177 150
            case Node\Expr\BinaryOp\Smaller::class:
178 12
                return new Expression\Operators\Comparison\Smaller();
179 138
            case Node\Expr\BinaryOp\SmallerOrEqual::class:
180 12
                return new Expression\Operators\Comparison\SmallerOrEqual();
181
182
            /**
183
             * Casts
184
             */
185 126
            case Node\Expr\Cast\Array_::class:
186 3
                return new Expression\Casts\ArrayCast();
187 125
            case Node\Expr\Cast\Bool_::class:
188 8
                return new Expression\Casts\BoolCast();
189 119
            case Node\Expr\Cast\Int_::class:
190 8
                return new Expression\Casts\IntCast();
191 113
            case Node\Expr\Cast\Double::class:
192 8
                return new Expression\Casts\DoubleCast();
193 107
            case Node\Expr\Cast\Object_::class:
194 2
                return new Expression\Casts\ObjectCast();
195 106
            case Node\Expr\Cast\String_::class:
196 7
                return new Expression\Casts\StringCast();
197 100
            case Node\Expr\Cast\Unset_::class:
198 2
                return new Expression\Casts\UnsetCast();
199
200
201
            /**
202
             * Other
203
             */
204 99
            case Node\Expr\Array_::class:
205 37
                return new Expression\ArrayOp();
206 70
            case Node\Expr\Assign::class:
207 31
                return new Expression\Assign();
208 52
            case Node\Expr\AssignRef::class:
209 1
                return new Expression\AssignRef();
210 52
            case Node\Expr\Closure::class:
211
                return new Expression\Closure();
212 52
            case Node\Expr\ConstFetch::class:
213 8
                return new Expression\ConstFetch();
214 49
            case Node\Expr\ClassConstFetch::class:
215
                return new Expression\ClassConstFetch();
216 49
            case Node\Expr\PropertyFetch::class:
217
                return new Expression\PropertyFetch();
218 49
            case Node\Expr\StaticPropertyFetch::class:
219
                return new Expression\StaticPropertyFetch();
220 49
            case Node\Expr\ArrayDimFetch::class:
221
                return new Expression\ArrayDimFetch();
222 49
            case Node\Expr\UnaryMinus::class:
223 10
                return new Expression\Operators\UnaryMinus();
224 40
            case Node\Expr\UnaryPlus::class:
225 10
                return new Expression\Operators\UnaryPlus();
226 30
            case Node\Expr\Exit_::class:
227 1
                return new Expression\ExitOp();
228 29
            case Node\Expr\Isset_::class:
229 3
                return new Expression\IssetOp();
230 26
            case Node\Expr\Print_::class:
231 2
                return new Expression\PrintOp();
232 25
            case Node\Expr\Empty_::class:
233 5
                return new Expression\EmptyOp();
234 20
            case Node\Expr\Eval_::class:
235 2
                return new Expression\EvalOp();
236 18
            case Node\Expr\ShellExec::class:
237 1
                return new Expression\ShellExec();
238 17
            case Node\Expr\ErrorSuppress::class:
239 2
                return new Expression\ErrorSuppress();
240 15
            case Node\Expr\Include_::class:
241
                return new Expression\IncludeOp();
242 15
            case Node\Expr\Clone_::class:
243 1
                return new Expression\CloneOp();
244 14
            case Node\Expr\Ternary::class:
245 2
                return new Expression\Ternary();
246 12
            case Node\Expr\Yield_::class:
247
                return new Expression\YieldOp();
248 12
            case Node\Expr\YieldFrom::class:
249
                return new Expression\YieldFrom();
250 12
            case Node\Expr\Variable::class:
251 12
                return new Expression\Variable();
252
        }
253
254
        return false;
255
    }
256
257
    /**
258
     * @param object|string $expr
259
     * @throws InvalidArgumentException when $expr is not string/object/null
260
     * @throws RuntimeException when compiler class does not return a CompiledExpression
261
     * @return CompiledExpression
262
     */
263 852
    public function compile($expr)
0 ignored issues
show
Complexity introduced by
This operation has 288 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
264
    {
265 852
        if (is_string($expr)) {
266 18
            return new CompiledExpression(CompiledExpression::STRING, $expr);
267
        }
268
269 852
        if (is_null($expr)) {
270 8
            return new CompiledExpression(CompiledExpression::NULL);
271
        }
272
273 852
        if (!is_object($expr)) {
274
            throw new InvalidArgumentException('$expr must be string/object/null');
275
        }
276
277 852
        $this->eventManager->fire(
278 852
            ExpressionBeforeCompile::EVENT_NAME,
279 852
            new ExpressionBeforeCompile(
280 852
                $expr,
281 852
                $this->context
282 852
            )
283 852
        );
284
285 852
        $className = get_class($expr);
286
        switch ($className) {
287 852
            case Node\Arg::class:
288
                /**
289
                 * @todo Better compile
290
                 */
291 2
                return $this->compile($expr->value);
292
293
            /**
294
             * Expressions
295
             */
296 852
            case Node\Name::class:
297 28
                return $this->getNodeName($expr);
298 852
            case Node\Name\FullyQualified::class:
299
                return $this->getFullyQualifiedNodeName($expr);
300
301
            /**
302
             * Simple Scalar(s)
303
             */
304 852
            case \PHPSA\Node\Scalar\Nil::class:
305 25
                return new CompiledExpression(CompiledExpression::NULL);
306 851
            case Node\Scalar\LNumber::class:
307 515
                return new CompiledExpression(CompiledExpression::INTEGER, $expr->value);
308 846
            case Node\Scalar\DNumber::class:
309 212
                return new CompiledExpression(CompiledExpression::DOUBLE, $expr->value);
310 845
            case Node\Scalar\String_::class:
311 50
                return new CompiledExpression(CompiledExpression::STRING, $expr->value);
312 844
            case \PHPSA\Node\Scalar\Boolean::class:
313 241
                return new CompiledExpression(CompiledExpression::BOOLEAN, $expr->value);
314 842
            case \PHPSA\Node\Scalar\Fake::class:
315 79
                return new CompiledExpression($expr->type, $expr->value);
316
        }
317
318 842
        $expressionCompiler = $this->factory($expr);
319 842
        if (!$expressionCompiler) {
320
            $this->context->debug("Expression compiler is not implemented for {$className}");
321
            return new CompiledExpression(CompiledExpression::UNIMPLEMENTED);
322
        }
323
324 842
        $result = $expressionCompiler->pass($expr, $this->context);
325 842
        if (!$result instanceof CompiledExpression) {
326
            throw new RuntimeException('Please return CompiledExpression from ' . get_class($expressionCompiler));
327
        }
328
329 842
        return $result;
330
    }
331
332
    /**
333
     * @todo Implement - does not belong in this file
334
     *
335
     * @param Node\Stmt\Property $st
336
    public function passProperty(Node\Stmt\Property $st)
337
    {
338
        $docBlock = $st->getDocComment();
339
        if (!$docBlock) {
340
            return new CompiledExpression();
341
        }
342
343
        $phpdoc = DocBlockFactory::createInstance()->create($docBlock->getText());
344
345
        $varTags = $phpdoc->getTagsByName('var');
346
        if ($varTags) {
347
            $varTag = current($varTags);
348
349
            $typeResolver = new \phpDocumentor\Reflection\TypeResolver();
350
351
            try {
352
                $type = $typeResolver->resolve($varTag->getType());
353
            } catch (\InvalidArgumentException $e) {
354
                return new CompiledExpression();
355
            }
356
357
            if ($type) {
358
                switch (get_class($type)) {
359
                    case \phpDocumentor\Reflection\Types\Object_::class:
360
                        return new CompiledExpression(
361
                            CompiledExpression::OBJECT
362
                        );
363
                    case \phpDocumentor\Reflection\Types\Integer::class:
364
                        return new CompiledExpression(
365
                            CompiledExpression::INTEGER
366
                        );
367
                    case \phpDocumentor\Reflection\Types\String_::class:
368
                        return new CompiledExpression(
369
                            CompiledExpression::STRING
370
                        );
371
                    case \phpDocumentor\Reflection\Types\Float_::class:
372
                        return new CompiledExpression(
373
                            CompiledExpression::DOUBLE
374
                        );
375
                    case \phpDocumentor\Reflection\Types\Null_::class:
376
                        return new CompiledExpression(
377
                            CompiledExpression::NULL
378
                        );
379
                    case \phpDocumentor\Reflection\Types\Boolean::class:
380
                        return new CompiledExpression(
381
                            CompiledExpression::BOOLEAN
382
                        );
383
                }
384
            }
385
        }
386
387
        return new CompiledExpression();
388
    }
389
*/
390
391
392
    /**
393
     * @param Node\Expr\Variable $expr
394
     * @param mixed $value
395
     * @param int $type
396
     * @return CompiledExpression
397
     */
398 1
    public function declareVariable(Node\Expr\Variable $expr, $value = null, $type = CompiledExpression::UNKNOWN)
399
    {
400 1
        $variable = $this->context->getSymbol($expr->name);
401 1
        if (!$variable) {
402 1
            $variable = new Variable($expr->name, $value, $type, $this->context->getCurrentBranch());
0 ignored issues
show
Bug introduced by
It seems like $expr->name can also be of type object<PhpParser\Node\Expr>; however, PHPSA\Variable::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
403 1
            $this->context->addVariable($variable);
404 1
        }
405
406 1
        return new CompiledExpression($variable->getType(), $variable->getValue(), $variable);
407
    }
408
409
    /**
410
     * @param Node\Name\FullyQualified $expr
411
     * @return CompiledExpression
412
     */
413
    public function getFullyQualifiedNodeName(Node\Name\FullyQualified $expr)
414
    {
415
        $this->context->debug('Unimplemented FullyQualified', $expr);
416
417
        return new CompiledExpression();
418
    }
419
420
    /**
421
     * @param Node\Name $expr
422
     * @return CompiledExpression
423
     */
424 28
    public function getNodeName(Node\Name $expr)
425
    {
426 28
        $nodeString = $expr->toString();
427 28
        if ($nodeString === 'null') {
428 1
            return new CompiledExpression(CompiledExpression::NULL);
429
        }
430
431 28
        if (in_array($nodeString, ['parent'], true)) {
432
            /** @var ClassDefinition $scope */
433
            $scope = $this->context->scope;
434
            assert($scope instanceof ClassDefinition);
435
436
            if ($scope->getExtendsClass()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $scope->getExtendsClass() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
437
                $definition = $scope->getExtendsClassDefinition();
438
                if ($definition) {
439
                    return new CompiledExpression(CompiledExpression::OBJECT, $definition);
440
                }
441
            } else {
442
                $this->context->notice(
443
                    'no-parent',
444
                    'Cannot access parent:: when current class scope has no parent',
445
                    $expr
446
                );
447
            }
448
        }
449
450 28
        if (in_array($nodeString, ['self', 'static'], true)) {
451 1
            return CompiledExpression::fromZvalValue($this->context->scope);
452
        }
453
454 27
        if (defined($nodeString)) {
455 8
            return CompiledExpression::fromZvalValue(constant($expr));
456
        }
457
458 23
        return new CompiledExpression(CompiledExpression::STRING, $expr->toString());
459
    }
460
}
461