Completed
Pull Request — master (#208)
by Enrico
14:34 queued 09:49
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\CompiledExpression;
11
use PHPSA\Compiler\Event\ExpressionBeforeCompile;
12
use PHPSA\Context;
13
use PhpParser\Node;
14
use PHPSA\Definition\ClassDefinition;
15
use PHPSA\Exception\RuntimeException;
16
use PHPSA\Variable;
17
use PHPSA\Compiler\Expression\AbstractExpressionCompiler;
18
use Webiny\Component\EventManager\EventManager;
19
20
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...
21
{
22
    /**
23
     * @var Context
24
     */
25
    protected $context;
26
27
    /**
28
     * @var EventManager
29
     */
30
    protected $eventManager;
31
32
    /**
33
     * @param Context $context
34
     */
35 857
    public function __construct(Context $context, EventManager $eventManager)
36
    {
37 857
        $this->context = $context;
38 857
        $this->eventManager = $eventManager;
39 857
    }
40
41
    /**
42
     * @param $expr
43
     * @return ExpressionCompilerInterface|AbstractExpressionCompiler
44
     */
45 847
    protected function factory($expr)
46
    {
47 847
        switch (get_class($expr)) {
48
            /**
49
             * Call(s)
50
             */
51 847
            case Node\Expr\MethodCall::class:
52
                return new Expression\MethodCall();
53 847
            case Node\Expr\FuncCall::class:
54 10
                return new Expression\FunctionCall();
55 843
            case Node\Expr\StaticCall::class:
56 1
                return new Expression\StaticCall();
57
            /**
58
             * Operators
59
             */
60 842
            case Node\Expr\New_::class:
61 2
                return new Expression\Operators\NewOp();
62 842
            case Node\Expr\Instanceof_::class:
63
                return new Expression\Operators\InstanceOfOp();
64
            /**
65
             * AssignOp
66
             */
67 842
            case Node\Expr\AssignOp\Pow::class:
68 21
                return new Expression\AssignOp\Pow();
69 821
            case Node\Expr\AssignOp\Plus::class:
70 17
                return new Expression\AssignOp\Plus();
71 804
            case Node\Expr\AssignOp\Minus::class:
72 20
                return new Expression\AssignOp\Minus();
73 784
            case Node\Expr\AssignOp\Mul::class:
74 20
                return new Expression\AssignOp\Mul();
75 764
            case Node\Expr\AssignOp\Div::class:
76 18
                return new Expression\AssignOp\Div();
77 746
            case Node\Expr\AssignOp\Mod::class:
78 11
                return new Expression\AssignOp\Mod();
79 735
            case Node\Expr\AssignOp\BitwiseOr::class:
80 12
                return new Expression\AssignOp\BitwiseOr();
81 723
            case Node\Expr\AssignOp\BitwiseAnd::class:
82 12
                return new Expression\AssignOp\BitwiseAnd();
83 711
            case Node\Expr\AssignOp\BitwiseXor::class:
84 12
                return new Expression\AssignOp\BitwiseXor();
85 699
            case Node\Expr\AssignOp\Concat::class:
86 14
                return new Expression\AssignOp\Concat();
87 685
            case Node\Expr\AssignOp\ShiftLeft::class:
88 12
                return new Expression\AssignOp\ShiftLeft();
89 673
            case Node\Expr\AssignOp\ShiftRight::class:
90 12
                return new Expression\AssignOp\ShiftRight();
91
92
            /**
93
             * BinaryOp
94
             */
95 661
            case Node\Expr\BinaryOp\Identical::class:
96 30
                return new Expression\BinaryOp\Identical();
97 633
            case Node\Expr\BinaryOp\Concat::class:
98 15
                return new Expression\Operators\Concat();
99 619
            case Node\Expr\BinaryOp\NotIdentical::class:
100 16
                return new Expression\BinaryOp\NotIdentical();
101 605
            case Node\Expr\BinaryOp\Equal::class:
102 45
                return new Expression\BinaryOp\Equal();
103 576
            case Node\Expr\BinaryOp\NotEqual::class:
104 19
                return new Expression\BinaryOp\NotEqual();
105 559
            case Node\Expr\BinaryOp\Spaceship::class:
106 11
                return new Expression\BinaryOp\SpaceShip();
107 550
            case Node\Expr\BinaryOp\Coalesce::class:
108 3
                return new Expression\BinaryOp\Coalesce();
109
                
110
            /**
111
             * @link http://php.net/manual/en/language.operators.increment.php
112
             */
113 548
            case Node\Expr\PostInc::class:
114 8
                return new Expression\Operators\PostInc();
115 540
            case Node\Expr\PostDec::class:
116 8
                return new Expression\Operators\PostDec();
117 532
            case Node\Expr\PreInc::class:
118 9
                return new Expression\Operators\PreInc();
119 524
            case Node\Expr\PreDec::class:
120 9
                return new Expression\Operators\PreDec();
121
            /**
122
             * Arithmetical
123
             */
124 516
            case Node\Expr\BinaryOp\Div::class:
125 37
                return new Expression\Operators\Arithmetical\Div();
126 479
            case Node\Expr\BinaryOp\Plus::class:
127 45
                return new Expression\Operators\Arithmetical\Plus();
128 438
            case Node\Expr\BinaryOp\Minus::class:
129 24
                return new Expression\Operators\Arithmetical\Minus();
130 414
            case Node\Expr\BinaryOp\Mul::class:
131 37
                return new Expression\Operators\Arithmetical\Mul();
132 379
            case Node\Expr\BinaryOp\Mod::class:
133 35
                return new Expression\Operators\Arithmetical\Mod();
134 344
            case Node\Expr\BinaryOp\Pow::class:
135 21
                return new Expression\Operators\Arithmetical\Pow();
136
137
            /**
138
             * Bitwise
139
             * @link http://php.net/manual/ru/language.operators.bitwise.php
140
             */
141 323
            case Node\Expr\BinaryOp\BitwiseOr::class:
142 12
                return new Expression\Operators\Bitwise\BitwiseOr();
143 311
            case Node\Expr\BinaryOp\BitwiseXor::class:
144 12
                return new Expression\Operators\Bitwise\BitwiseXor();
145 299
            case Node\Expr\BinaryOp\BitwiseAnd::class:
146 12
                return new Expression\Operators\Bitwise\BitwiseAnd();
147 287
            case Node\Expr\BinaryOp\ShiftRight::class:
148 12
                return new Expression\Operators\Bitwise\ShiftRight();
149 275
            case Node\Expr\BinaryOp\ShiftLeft::class:
150 12
                return new Expression\Operators\Bitwise\ShiftLeft();
151 263
            case Node\Expr\BitwiseNot::class:
152 6
                return new Expression\Operators\Bitwise\BitwiseNot();
153
            /**
154
             * Logical
155
             */
156 258
            case Node\Expr\BinaryOp\BooleanOr::class:
157 17
                return new Expression\Operators\Logical\BooleanOr();
158 243
            case Node\Expr\BinaryOp\BooleanAnd::class:
159 16
                return new Expression\Operators\Logical\BooleanAnd();
160 230
            case Node\Expr\BooleanNot::class:
161 11
                return new Expression\Operators\Logical\BooleanNot();
162 222
            case Node\Expr\BinaryOp\LogicalAnd::class:
163 15
                return new Expression\Operators\Logical\LogicalAnd();
164 209
            case Node\Expr\BinaryOp\LogicalOr::class:
165 17
                return new Expression\Operators\Logical\LogicalOr();
166 194
            case Node\Expr\BinaryOp\LogicalXor::class:
167 17
                return new Expression\Operators\Logical\LogicalXor();
168
169
            /**
170
             * Comparison
171
             */
172 179
            case Node\Expr\BinaryOp\Greater::class:
173 14
                return new Expression\Operators\Comparison\Greater();
174 167
            case Node\Expr\BinaryOp\GreaterOrEqual::class:
175 13
                return new Expression\Operators\Comparison\GreaterOrEqual();
176 155
            case Node\Expr\BinaryOp\Smaller::class:
177 13
                return new Expression\Operators\Comparison\Smaller();
178 143
            case Node\Expr\BinaryOp\SmallerOrEqual::class:
179 13
                return new Expression\Operators\Comparison\SmallerOrEqual();
180
181
            /**
182
             * Casts
183
             */
184 131
            case Node\Expr\Cast\Array_::class:
185 3
                return new Expression\Casts\ArrayCast();
186 130
            case Node\Expr\Cast\Bool_::class:
187 8
                return new Expression\Casts\BoolCast();
188 124
            case Node\Expr\Cast\Int_::class:
189 8
                return new Expression\Casts\IntCast();
190 118
            case Node\Expr\Cast\Double::class:
191 8
                return new Expression\Casts\DoubleCast();
192 112
            case Node\Expr\Cast\Object_::class:
193 2
                return new Expression\Casts\ObjectCast();
194 111
            case Node\Expr\Cast\String_::class:
195 7
                return new Expression\Casts\StringCast();
196 105
            case Node\Expr\Cast\Unset_::class:
197 2
                return new Expression\Casts\UnsetCast();
198
199
200
            /**
201
             * Other
202
             */
203 104
            case Node\Expr\Array_::class:
204 38
                return new Expression\ArrayOp();
205 75
            case Node\Expr\Assign::class:
206 34
                return new Expression\Assign();
207 56
            case Node\Expr\AssignRef::class:
208 1
                return new Expression\AssignRef();
209 56
            case Node\Expr\Closure::class:
210
                return new Expression\Closure();
211 56
            case Node\Expr\ConstFetch::class:
212 10
                return new Expression\ConstFetch();
213 52
            case Node\Expr\ClassConstFetch::class:
214
                return new Expression\ClassConstFetch();
215 52
            case Node\Expr\PropertyFetch::class:
216
                return new Expression\PropertyFetch();
217 52
            case Node\Expr\StaticPropertyFetch::class:
218
                return new Expression\StaticPropertyFetch();
219 52
            case Node\Expr\ArrayDimFetch::class:
220
                return new Expression\ArrayDimFetch();
221 52
            case Node\Expr\UnaryMinus::class:
222 10
                return new Expression\Operators\UnaryMinus();
223 43
            case Node\Expr\UnaryPlus::class:
224 10
                return new Expression\Operators\UnaryPlus();
225 33
            case Node\Expr\Exit_::class:
226 2
                return new Expression\ExitOp();
227 31
            case Node\Expr\Isset_::class:
228 3
                return new Expression\IssetOp();
229 28
            case Node\Expr\Print_::class:
230 2
                return new Expression\PrintOp();
231 27
            case Node\Expr\Empty_::class:
232 5
                return new Expression\EmptyOp();
233 22
            case Node\Expr\Eval_::class:
234 2
                return new Expression\EvalOp();
235 20
            case Node\Expr\ShellExec::class:
236 1
                return new Expression\ShellExec();
237 19
            case Node\Expr\ErrorSuppress::class:
238 2
                return new Expression\ErrorSuppress();
239 17
            case Node\Expr\Include_::class:
240
                return new Expression\IncludeOp();
241 17
            case Node\Expr\Clone_::class:
242 1
                return new Expression\CloneOp();
243 16
            case Node\Expr\Ternary::class:
244 2
                return new Expression\Ternary();
245 14
            case Node\Expr\Yield_::class:
246
                return new Expression\YieldOp();
247 14
            case Node\Expr\YieldFrom::class:
248
                return new Expression\YieldFrom();
249 14
            case Node\Expr\Variable::class:
250 14
                return new Expression\Variable();
251
        }
252
253
        return false;
254
    }
255
256
    /**
257
     * @param object|string $expr
258
     * @throws InvalidArgumentException when $expr is not string/object/null
259
     * @throws RuntimeException when compiler class does not return a CompiledExpression
260
     * @return CompiledExpression
261
     */
262 857
    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...
263
    {
264 857
        if (is_string($expr)) {
265 21
            return new CompiledExpression(CompiledExpression::STRING, $expr);
266
        }
267
268 857
        if (is_null($expr)) {
269 9
            return new CompiledExpression(CompiledExpression::NULL);
270
        }
271
272 857
        if (!is_object($expr)) {
273
            throw new InvalidArgumentException('$expr must be string/object/null');
274
        }
275
276 857
        $this->eventManager->fire(
277 857
            ExpressionBeforeCompile::EVENT_NAME,
278 857
            new ExpressionBeforeCompile(
279 857
                $expr,
280 857
                $this->context
281 857
            )
282 857
        );
283
284 857
        $className = get_class($expr);
285
        switch ($className) {
286 857
            case Node\Arg::class:
287
                /**
288
                 * @todo Better compile
289
                 */
290 2
                return $this->compile($expr->value);
291
292
            /**
293
             * Expressions
294
             */
295 857
            case Node\Name::class:
296 30
                return $this->getNodeName($expr);
297 857
            case Node\Name\FullyQualified::class:
298
                return $this->getFullyQualifiedNodeName($expr);
299
300
            /**
301
             * Simple Scalar(s)
302
             */
303 857
            case \PHPSA\Node\Scalar\Nil::class:
304 25
                return new CompiledExpression(CompiledExpression::NULL);
305 856
            case Node\Scalar\LNumber::class:
306 519
                return new CompiledExpression(CompiledExpression::INTEGER, $expr->value);
307 851
            case Node\Scalar\DNumber::class:
308 212
                return new CompiledExpression(CompiledExpression::DOUBLE, $expr->value);
309 850
            case Node\Scalar\String_::class:
310 52
                return new CompiledExpression(CompiledExpression::STRING, $expr->value);
311 849
            case \PHPSA\Node\Scalar\Boolean::class:
312 241
                return new CompiledExpression(CompiledExpression::BOOLEAN, $expr->value);
313 847
            case \PHPSA\Node\Scalar\Fake::class:
314 79
                return new CompiledExpression($expr->type, $expr->value);
315
        }
316
317 847
        $expressionCompiler = $this->factory($expr);
318 847
        if (!$expressionCompiler) {
319
            $this->context->debug("Expression compiler is not implemented for {$className}");
320
            return new CompiledExpression(CompiledExpression::UNIMPLEMENTED);
321
        }
322
323 847
        $result = $expressionCompiler->pass($expr, $this->context);
324 847
        if (!$result instanceof CompiledExpression) {
325
            throw new RuntimeException('Please return CompiledExpression from ' . get_class($expressionCompiler));
326
        }
327
328 847
        return $result;
329
    }
330
331
    /**
332
     * @todo Implement - does not belong in this file
333
     *
334
     * @param Node\Stmt\Property $st
335
    public function passProperty(Node\Stmt\Property $st)
336
    {
337
        $docBlock = $st->getDocComment();
338
        if (!$docBlock) {
339
            return new CompiledExpression();
340
        }
341
342
        $phpdoc = DocBlockFactory::createInstance()->create($docBlock->getText());
343
344
        $varTags = $phpdoc->getTagsByName('var');
345
        if ($varTags) {
346
            $varTag = current($varTags);
347
348
            $typeResolver = new \phpDocumentor\Reflection\TypeResolver();
349
350
            try {
351
                $type = $typeResolver->resolve($varTag->getType());
352
            } catch (\InvalidArgumentException $e) {
353
                return new CompiledExpression();
354
            }
355
356
            if ($type) {
357
                switch (get_class($type)) {
358
                    case \phpDocumentor\Reflection\Types\Object_::class:
359
                        return new CompiledExpression(
360
                            CompiledExpression::OBJECT
361
                        );
362
                    case \phpDocumentor\Reflection\Types\Integer::class:
363
                        return new CompiledExpression(
364
                            CompiledExpression::INTEGER
365
                        );
366
                    case \phpDocumentor\Reflection\Types\String_::class:
367
                        return new CompiledExpression(
368
                            CompiledExpression::STRING
369
                        );
370
                    case \phpDocumentor\Reflection\Types\Float_::class:
371
                        return new CompiledExpression(
372
                            CompiledExpression::DOUBLE
373
                        );
374
                    case \phpDocumentor\Reflection\Types\Null_::class:
375
                        return new CompiledExpression(
376
                            CompiledExpression::NULL
377
                        );
378
                    case \phpDocumentor\Reflection\Types\Boolean::class:
379
                        return new CompiledExpression(
380
                            CompiledExpression::BOOLEAN
381
                        );
382
                }
383
            }
384
        }
385
386
        return new CompiledExpression();
387
    }
388
*/
389
390
391
    /**
392
     * @param Node\Expr\Variable $expr
393
     * @param mixed $value
394
     * @param int $type
395
     * @return CompiledExpression
396
     */
397 1
    public function declareVariable(Node\Expr\Variable $expr, $value = null, $type = CompiledExpression::UNKNOWN)
398
    {
399 1
        $variable = $this->context->getSymbol($expr->name);
400 1
        if (!$variable) {
401 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...
402 1
            $this->context->addVariable($variable);
403 1
        }
404
405 1
        return new CompiledExpression($variable->getType(), $variable->getValue(), $variable);
406
    }
407
408
    /**
409
     * @param Node\Name\FullyQualified $expr
410
     * @return CompiledExpression
411
     */
412
    public function getFullyQualifiedNodeName(Node\Name\FullyQualified $expr)
413
    {
414
        $this->context->debug('Unimplemented FullyQualified', $expr);
415
416
        return new CompiledExpression();
417
    }
418
419
    /**
420
     * @param Node\Name $expr
421
     * @return CompiledExpression
422
     */
423 30
    public function getNodeName(Node\Name $expr)
424
    {
425 30
        $nodeString = $expr->toString();
426 30
        if ($nodeString === 'null') {
427 1
            return new CompiledExpression(CompiledExpression::NULL);
428
        }
429
430 30
        if (in_array($nodeString, ['parent'], true)) {
431
            /** @var ClassDefinition $scope */
432
            $scope = $this->context->scope;
433
            assert($scope instanceof ClassDefinition);
434
435
            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...
436
                $definition = $scope->getExtendsClassDefinition();
437
                if ($definition) {
438
                    return new CompiledExpression(CompiledExpression::OBJECT, $definition);
439
                }
440
            } else {
441
                $this->context->notice(
442
                    'no-parent',
443
                    'Cannot access parent:: when current class scope has no parent',
444
                    $expr
445
                );
446
            }
447
        }
448
449 30
        if (in_array($nodeString, ['self', 'static'], true)) {
450 1
            return CompiledExpression::fromZvalValue($this->context->scope);
451
        }
452
453 29
        if (defined($nodeString)) {
454 10
            return CompiledExpression::fromZvalValue(constant($expr));
455
        }
456
457 23
        return new CompiledExpression(CompiledExpression::STRING, $expr->toString());
458
    }
459
}
460