Completed
Pull Request — master (#124)
by Enrico
07:25
created

Expression::passSymbol()   D

Complexity

Conditions 16
Paths 13

Size

Total Lines 91
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 78.5

Importance

Changes 0
Metric Value
cc 16
eloc 58
nc 13
nop 1
dl 0
loc 91
ccs 27
cts 72
cp 0.375
crap 78.5
rs 4.8736
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 PHPSA\Check;
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 139 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 384
    public function __construct(Context $context, EventManager $eventManager)
36
    {
37 384
        $this->context = $context;
38 384
        $this->eventManager = $eventManager;
39 384
    }
40
41
    /**
42
     * @param $expr
43
     * @return ExpressionCompilerInterface|AbstractExpressionCompiler
44
     */
45 369
    protected function factory($expr)
46
    {
47 369
        switch (get_class($expr)) {
48
            /**
49
             * Call(s)
50
             */
51 369
            case Node\Expr\MethodCall::class:
52
                return new Expression\MethodCall();
53 369
            case Node\Expr\FuncCall::class:
54 10
                return new Expression\FunctionCall();
55 361
            case Node\Expr\StaticCall::class:
56
                return new Expression\StaticCall();
57
            /**
58
             * Operators
59
             */
60 361
            case Node\Expr\New_::class:
61 1
                return new Expression\Operators\NewOp();
62 360
            case Node\Expr\Instanceof_::class:
63
                return new Expression\Operators\InstanceOfOp();
64
            /**
65
             * AssignOp
66
             */
67 360
            case Node\Expr\AssignOp\Pow::class:
68
                return new Expression\AssignOp\Pow();
69 360
            case Node\Expr\AssignOp\Plus::class:
70
                return new Expression\AssignOp\Plus();
71 360
            case Node\Expr\AssignOp\Minus::class:
72
                return new Expression\AssignOp\Minus();
73 360
            case Node\Expr\AssignOp\Mod::class:
74
                return new Expression\AssignOp\Mod();
75 360
            case Node\Expr\AssignOp\BitwiseOr::class:
76
                return new Expression\AssignOp\BitwiseOr();
77 360
            case Node\Expr\AssignOp\BitwiseAnd::class:
78
                return new Expression\AssignOp\BitwiseAnd();
79
            /**
80
             * BinaryOp
81
             */
82 360
            case Node\Expr\BinaryOp\Identical::class:
83 28
                return new Expression\BinaryOp\Identical();
84 332
            case Node\Expr\BinaryOp\Concat::class:
85 1
                return new Expression\Operators\Concat();
86 331
            case Node\Expr\BinaryOp\NotIdentical::class:
87 14
                return new Expression\BinaryOp\NotIdentical();
88 317
            case Node\Expr\BinaryOp\Equal::class:
89 34
                return new Expression\BinaryOp\Equal();
90 283
            case Node\Expr\BinaryOp\NotEqual::class:
91 17
                return new Expression\BinaryOp\NotEqual();
92
            /**
93
             * @link http://php.net/manual/en/language.operators.increment.php
94
             */
95 266
            case Node\Expr\PostInc::class:
96 4
                return new Expression\Operators\PostInc();
97 262
            case Node\Expr\PostDec::class:
98 4
                return new Expression\Operators\PostDec();
99
            /**
100
             * Arithmetical
101
             */
102 258
            case Node\Expr\BinaryOp\Div::class:
103 37
                return new Expression\Operators\Arithmetical\Div();
104 221
            case Node\Expr\BinaryOp\Plus::class:
105 45
                return new Expression\Operators\Arithmetical\Plus();
106 176
            case Node\Expr\BinaryOp\Minus::class:
107 18
                return new Expression\Operators\Arithmetical\Minus();
108 158
            case Node\Expr\BinaryOp\Mul::class:
109 35
                return new Expression\Operators\Arithmetical\Mul();
110 123
            case Node\Expr\BinaryOp\Mod::class:
111 35
                return new Expression\Operators\Arithmetical\Mod();
112
            /**
113
             * Bitwise
114
             * @link http://php.net/manual/ru/language.operators.bitwise.php
115
             */
116 88
            case Node\Expr\BinaryOp\BitwiseOr::class:
117
                return new Expression\Operators\Bitwise\BitwiseOr();
118 88
            case Node\Expr\BinaryOp\BitwiseXor::class:
119
                return new Expression\Operators\Bitwise\BitwiseXor();
120 88
            case Node\Expr\BinaryOp\BitwiseAnd::class:
121
                return new Expression\Operators\Bitwise\BitwiseAnd();
122 88
            case Node\Expr\BinaryOp\ShiftRight::class:
123
                return new Expression\Operators\Bitwise\ShiftRight();
124 88
            case Node\Expr\BinaryOp\ShiftLeft::class:
125
                return new Expression\Operators\Bitwise\ShiftLeft();
126 88
            case Node\Expr\BitwiseNot::class:
127
                return new Expression\Operators\Bitwise\BitwiseNot();
128
            /**
129
             * Logical
130
             */
131 88
            case Node\Expr\BinaryOp\BooleanOr::class:
132 10
                return new Expression\Operators\Logical\BooleanOr();
133 78
            case Node\Expr\BinaryOp\BooleanAnd::class:
134 5
                return new Expression\Operators\Logical\BooleanAnd();
135 73
            case Node\Expr\BooleanNot::class:
136 5
                return new Expression\Operators\Logical\BooleanNot();
137
            /**
138
             * Comparison
139
             */
140 68
            case Node\Expr\BinaryOp\Greater::class:
141 12
                return new Expression\Operators\Comparison\Greater();
142 56
            case Node\Expr\BinaryOp\GreaterOrEqual::class:
143 12
                return new Expression\Operators\Comparison\GreaterOrEqual();
144 44
            case Node\Expr\BinaryOp\Smaller::class:
145 12
                return new Expression\Operators\Comparison\Smaller();
146 32
            case Node\Expr\BinaryOp\SmallerOrEqual::class:
147 12
                return new Expression\Operators\Comparison\SmallerOrEqual();
148
149
            /**
150
             * Casts
151
             */
152 20
            case Node\Expr\Cast\Array_::class:
153 1
                return new Expression\Casts\ArrayCast();
154 20
            case Node\Expr\Cast\Bool_::class:
155 1
                return new Expression\Casts\BoolCast();
156 20
            case Node\Expr\Cast\Int_::class:
157 1
                return new Expression\Casts\IntCast();
158 20
            case Node\Expr\Cast\Double::class:
159 1
                return new Expression\Casts\DoubleCast();
160 20
            case Node\Expr\Cast\Object_::class:
161 1
                return new Expression\Casts\ObjectCast();
162 20
            case Node\Expr\Cast\String_::class:
163 1
                return new Expression\Casts\StringCast();
164 20
            case Node\Expr\Cast\Unset_::class:
165 1
                return new Expression\Casts\UnsetCast();
166
167
168
            /**
169
             * Other
170
             */
171 19
            case Node\Expr\Closure::class:
172
                return new Expression\Closure();
173 19
            case Node\Expr\UnaryMinus::class:
174 9
                return new Expression\Operators\UnaryMinus();
175 10
            case Node\Expr\UnaryPlus::class:
176 9
                return new Expression\Operators\UnaryPlus();
177 1
        }
178
179 1
        return false;
180
    }
181
182
    /**
183
     * @param object|string $expr
184
     * @throws InvalidArgumentException when $expr is not string/object/null
185
     * @throws RuntimeException when compiler class does not return a CompiledExpression
186
     * @return CompiledExpression
187
     */
188 384
    public function compile($expr)
0 ignored issues
show
Complexity introduced by
This operation has 544 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...
189
    {
190 384
        if (is_string($expr)) {
191 10
            return new CompiledExpression(CompiledExpression::STRING, $expr);
192
        }
193
194 384
        if (is_null($expr)) {
195 1
            return new CompiledExpression(CompiledExpression::NULL);
196
        }
197
198 384
        if (!is_object($expr)) {
199
            throw new InvalidArgumentException('$expr must be string/object/null');
200
        }
201
202 384
        $this->eventManager->fire(
203 384
            ExpressionBeforeCompile::EVENT_NAME,
204 384
            new ExpressionBeforeCompile(
205 384
                $expr,
206 384
                $this->context
207 384
            )
208 384
        );
209
210 384
        $className = get_class($expr);
211
        switch ($className) {
212 384
            case Node\Arg::class:
213
                /**
214
                 * @todo Better compile
215
                 */
216 2
                return $this->compile($expr->value);
217 384
            case Node\Expr\PropertyFetch::class:
218
                return $this->passPropertyFetch($expr);
219 384
            case Node\Stmt\Property::class:
220
                return $this->passProperty($expr);
221 384
            case Node\Expr\ClassConstFetch::class:
222
                return $this->passConstFetch($expr);
223 384
            case Node\Expr\Assign::class:
224 10
                return $this->passSymbol($expr);
225 384
            case Node\Expr\AssignRef::class:
226 1
                return $this->passSymbolByRef($expr);
227 384
            case Node\Expr\Variable::class:
228 6
                return $this->passExprVariable($expr);
229
230
            /**
231
             * Expressions
232
             */
233 384
            case Node\Expr\Array_::class:
234 16
                return $this->getArray($expr);
235 383
            case Node\Expr\ConstFetch::class:
236 5
                return $this->constFetch($expr);
237 383
            case Node\Name::class:
238 11
                return $this->getNodeName($expr);
239 383
            case Node\Name\FullyQualified::class:
240
                return $this->getFullyQualifiedNodeName($expr);
241
242
            /**
243
             * Simple Scalar(s)
244
             */
245 383
            case \PHPSA\Node\Scalar\Nil::class:
246 8
                return new CompiledExpression(CompiledExpression::NULL);
247 382
            case Node\Scalar\LNumber::class:
248 271
                return new CompiledExpression(CompiledExpression::INTEGER, $expr->value);
249 376
            case Node\Scalar\DNumber::class:
250 132
                return new CompiledExpression(CompiledExpression::DOUBLE, $expr->value);
251 375
            case Node\Scalar\String_::class:
252 17
                return new CompiledExpression(CompiledExpression::STRING, $expr->value);
253 371
            case \PHPSA\Node\Scalar\Boolean::class:
254 60
                return new CompiledExpression(CompiledExpression::BOOLEAN, $expr->value);
255 369
            case \PHPSA\Node\Scalar\Fake::class:
256 29
                return new CompiledExpression($expr->type, $expr->value);
257
        }
258
259 369
        $expressionCompiler = $this->factory($expr);
260 369
        if (!$expressionCompiler) {
261 1
            $this->context->debug("Expression compiler is not implemented for {$className}");
262 1
            return new CompiledExpression(CompiledExpression::UNIMPLEMENTED);
263
        }
264
265 369
        $result = $expressionCompiler->pass($expr, $this->context);
266 369
        if (!$result instanceof CompiledExpression) {
267
            throw new RuntimeException('Please return CompiledExpression from ' . get_class($expressionCompiler));
268
        }
269
270 369
        return $result;
271
    }
272
273
    /**
274
     * @todo Implement
275
     *
276
     * @param Node\Stmt\Property $stmt
277
     * @return CompiledExpression
278
     */
279
    public function passProperty(Node\Stmt\Property $stmt)
280
    {
281
        $docBlock = $stmt->getDocComment();
282
        if (!$docBlock) {
283
            $this->context->notice(
284
                'missing-docblock',
285
                sprintf('Missing docblock for $%s property', $stmt->props[0]->name),
286
                $stmt
287
            );
288
289
            return new CompiledExpression();
290
        }
291
292
        $phpdoc = new \phpDocumentor\Reflection\DocBlock($docBlock->getText());
293
294
        $varTags = $phpdoc->getTagsByName('var');
295
        if (!empty($varTags)) {
296
            /** @var \phpDocumentor\Reflection\DocBlock\Tag\VarTag $varTag */
297
            $varTag = current($varTags);
298
299
            $typeResolver = new \phpDocumentor\Reflection\TypeResolver();
300
301
            try {
302
                $type = $typeResolver->resolve($varTag->getType());
303
            } catch (\InvalidArgumentException $e) {
304
                return new CompiledExpression();
305
            }
306
307
            if ($type) {
308
                switch (get_class($type)) {
309
                    case \phpDocumentor\Reflection\Types\Object_::class:
310
                        return new CompiledExpression(
311
                            CompiledExpression::OBJECT
312
                        );
313
                    case \phpDocumentor\Reflection\Types\Integer::class:
314
                        return new CompiledExpression(
315
                            CompiledExpression::INTEGER
316
                        );
317
                    case \phpDocumentor\Reflection\Types\String_::class:
318
                        return new CompiledExpression(
319
                            CompiledExpression::STRING
320
                        );
321
                    case \phpDocumentor\Reflection\Types\Float_::class:
322
                        return new CompiledExpression(
323
                            CompiledExpression::DOUBLE
324
                        );
325
                    case \phpDocumentor\Reflection\Types\Null_::class:
326
                        return new CompiledExpression(
327
                            CompiledExpression::NULL
328
                        );
329
                    case \phpDocumentor\Reflection\Types\Boolean::class:
330
                        return new CompiledExpression(
331
                            CompiledExpression::BOOLEAN
332
                        );
333
                }
334
            }
335
        }
336
337
        return new CompiledExpression();
338
    }
339
340
    /**
341
     * @param Node\Expr\Variable $expr
342
     * @return CompiledExpression
343
     */
344 1
    public function declareVariable(Node\Expr\Variable $expr)
345
    {
346 1
        $variable = $this->context->getSymbol($expr->name);
347 1
        if (!$variable) {
348
            $variable = new Variable($expr->name, null, CompiledExpression::UNKNOWN, $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...
349
            $this->context->addVariable($variable);
350
        }
351
352 1
        return new CompiledExpression($variable->getType(), $variable->getValue(), $variable);
353
    }
354
355
    /**
356
     * @param Node\Name\FullyQualified $expr
357
     * @return CompiledExpression
358
     */
359
    public function getFullyQualifiedNodeName(Node\Name\FullyQualified $expr)
360
    {
361
        $this->context->debug('Unimplemented FullyQualified', $expr);
362
363
        return new CompiledExpression;
364
    }
365
366
    /**
367
     * @param Node\Name $expr
368
     * @return CompiledExpression
369
     */
370 11
    public function getNodeName(Node\Name $expr)
371
    {
372 11
        $nodeString = $expr->toString();
373 11
        if ($nodeString === 'null') {
374 1
            return new CompiledExpression(CompiledExpression::NULL);
375
        }
376
377 10
        if (in_array($nodeString, ['parent'], true)) {
378
            /** @var ClassDefinition $scope */
379
            $scope = $this->context->scope;
380
            assert($scope instanceof ClassDefinition);
381
382
            if ($scope->getExtendsClass() !== null) {
383
                $definition = $scope->getExtendsClassDefinition();
384
                if ($definition) {
385
                    return new CompiledExpression(CompiledExpression::OBJECT, $definition);
386
                }
387
            } else {
388
                $this->context->notice(
389
                    'no-parent',
390
                    'Cannot access parent:: when current class scope has no parent',
391
                    $expr
392
                );
393
            }
394
        }
395
396 10
        if (in_array($nodeString, ['self', 'static'], true)) {
397
            return CompiledExpression::fromZvalValue($this->context->scope);
398
        }
399
400 10
        if (defined($nodeString)) {
401 1
            return CompiledExpression::fromZvalValue(constant($expr));
402
        }
403
404 10
        return new CompiledExpression(CompiledExpression::STRING, $expr->toString());
405
    }
406
407
    /**
408
     * @param Node\Expr\PropertyFetch $expr
409
     * @return CompiledExpression
410
     */
411
    protected function passPropertyFetch(Node\Expr\PropertyFetch $expr)
412
    {
413
        $propertNameCE = $this->compile($expr->name);
414
415
        $scopeExpression = $this->compile($expr->var);
416
        if ($scopeExpression->isObject()) {
417
            $scopeExpressionValue = $scopeExpression->getValue();
418
            if ($scopeExpressionValue instanceof ClassDefinition) {
419
                $propertyName = $propertNameCE->isString() ? $propertNameCE->getValue() : false;
420
                if ($propertyName) {
421
                    if ($scopeExpressionValue->hasProperty($propertyName, true)) {
422
                        $property = $scopeExpressionValue->getProperty($propertyName, true);
423
                        return $this->compile($property);
424
                    } else {
425
                        $this->context->notice(
426
                            'undefined-property',
427
                            sprintf(
428
                                'Property %s does not exist in %s scope',
429
                                $propertyName,
430
                                $scopeExpressionValue->getName()
431
                            ),
432
                            $expr
433
                        );
434
                    }
435
                }
436
            }
437
438
            return new CompiledExpression(CompiledExpression::UNKNOWN);
439
        } elseif (!$scopeExpression->canBeObject()) {
440
            return new CompiledExpression(CompiledExpression::UNKNOWN);
441
        }
442
443
        $this->context->notice(
444
            'property-fetch-on-non-object',
445
            "It's not possible to fetch a property on a non-object",
446
            $expr,
447
            Check::CHECK_BETA
448
        );
449
450
        return new CompiledExpression(CompiledExpression::UNKNOWN);
451
    }
452
453
    /**
454
     * @param Node\Expr\ClassConstFetch $expr
455
     * @return CompiledExpression
456
     */
457
    protected function passConstFetch(Node\Expr\ClassConstFetch $expr)
458
    {
459
        $leftCE = $this->compile($expr->class);
460
        if ($leftCE->isObject()) {
461
            $leftCEValue = $leftCE->getValue();
462
            if ($leftCEValue instanceof ClassDefinition) {
463
                if (!$leftCEValue->hasConst($expr->name, true)) {
464
                    $this->context->notice(
465
                        'undefined-const',
466
                        sprintf('Constant %s does not exist in %s scope', $expr->name, $expr->class),
467
                        $expr
468
                    );
469
                    return new CompiledExpression(CompiledExpression::UNKNOWN);
470
                }
471
472
                return new CompiledExpression();
473
            }
474
        }
475
476
        $this->context->debug('Unknown const fetch', $expr);
477
        return new CompiledExpression();
478
    }
479
480
    /**
481
     * @param Node\Expr\Assign $expr
482
     * @return CompiledExpression
483
     */
484 10
    protected function passSymbol(Node\Expr\Assign $expr)
0 ignored issues
show
Complexity introduced by
This operation has 240 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...
485
    {
486 10
        $compiledExpression = $this->compile($expr->expr);
487
488 10
        if ($expr->var instanceof Node\Expr\List_) {
489
            $isCorrectType = false;
490
491
            switch ($compiledExpression->getType()) {
492
                case CompiledExpression::ARR:
493
                    $isCorrectType = true;
494
                    break;
495
            }
496
497
            if (!empty($expr->var->vars)) {
498
                foreach ($expr->var->vars as $key => $var) {
499
                    if ($var instanceof Node\Expr\Variable) {
500
                        $name = $expr->var->name;
0 ignored issues
show
Bug introduced by
The property name does not seem to exist in PhpParser\Node\Expr\List_.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
501
502
                        $symbol = $this->context->getSymbol($name);
503
                        if (!$symbol) {
504
                            $symbol = new Variable(
505
                                $name,
0 ignored issues
show
Bug introduced by
It seems like $name defined by $expr->var->name on line 500 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...
506
                                null,
507
                                CompiledExpression::UNKNOWN,
508
                                $this->context->getCurrentBranch()
509
                            );
510
                            $this->context->addVariable($symbol);
511
                        }
512
513
                        if (!$isCorrectType) {
514
                            $symbol->modify(CompiledExpression::NULL, null);
515
                        }
516
517
                        $symbol->incSets();
518
                    }
519
                }
520
            }
521
522
            return new CompiledExpression();
523
        }
524
525
526 10
        if ($expr->var instanceof Node\Expr\Variable) {
527 10
            $compiledExpressionName = $this->compile($expr->var->name);
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $compiledExpressionName exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
528 10
            switch ($compiledExpressionName->getType()) {
529 10
                case CompiledExpression::STRING:
530 10
                    break;
531
                default:
532
                    $this->context->debug('Unexpected type of Variable name after compile');
533
                    return new CompiledExpression();
534 10
            }
535
536 10
            $symbol = $this->context->getSymbol($compiledExpressionName->getValue());
537 10
            if ($symbol) {
538 2
                $symbol->modify($compiledExpression->getType(), $compiledExpression->getValue());
539 2
                $this->context->modifyReferencedVariables(
540 2
                    $symbol,
541 2
                    $compiledExpression->getType(),
542 2
                    $compiledExpression->getValue()
543 2
                );
544 2
            } else {
545 10
                $symbol = new Variable(
546 10
                    $compiledExpressionName->getValue(),
547 10
                    $compiledExpression->getValue(),
548 10
                    $compiledExpression->getType(),
549 10
                    $this->context->getCurrentBranch()
550 10
                );
551 10
                $this->context->addVariable($symbol);
552
            }
553
554 10
            $symbol->incSets();
555 10
            return $compiledExpression;
556
        }
557
558
        if ($expr->var instanceof Node\Expr\PropertyFetch) {
559
            $compiledExpression = $this->compile($expr->var->var);
560
            if ($compiledExpression->getType() == CompiledExpression::OBJECT) {
561
                $objectDefinition = $compiledExpression->getValue();
562
                if ($objectDefinition instanceof ClassDefinition) {
563
                    if (is_string($expr->var->name)) {
564
                        if ($objectDefinition->hasProperty($expr->var->name)) {
565
                            return $this->compile($objectDefinition->getProperty($expr->var->name));
566
                        }
567
                    }
568
                }
569
            }
570
        }
571
572
        $this->context->debug('Unknown how to pass symbol');
573
        return new CompiledExpression();
574
    }
575
576
    /**
577
     * @param Node\Expr\AssignRef $expr
578
     * @return CompiledExpression
579
     */
580 1
    protected function passSymbolByRef(Node\Expr\AssignRef $expr)
581
    {
582 1
        if ($expr->var instanceof Node\Expr\Variable) {
583 1
            $name = $expr->var->name;
584
585 1
            $compiledExpression = $this->compile($expr->expr);
586
587 1
            $symbol = $this->context->getSymbol($name);
588 1
            if ($symbol) {
589
                $symbol->modify($compiledExpression->getType(), $compiledExpression->getValue());
590
            } else {
591 1
                $symbol = new Variable(
592 1
                    $name,
0 ignored issues
show
Bug introduced by
It seems like $name defined by $expr->var->name on line 583 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...
593 1
                    $compiledExpression->getValue(),
594 1
                    $compiledExpression->getType(),
595 1
                    $this->context->getCurrentBranch()
596 1
                );
597 1
                $this->context->addVariable($symbol);
598
            }
599
600 1
            if ($expr->expr instanceof Node\Expr\Variable) {
601 1
                $rightVarName = $expr->expr->name;
602
603 1
                $rightSymbol = $this->context->getSymbol($rightVarName);
604 1
                if ($rightSymbol) {
605 1
                    $rightSymbol->incUse();
606 1
                    $symbol->setReferencedTo($rightSymbol);
607 1
                } else {
608
                    $this->context->debug('Cannot fetch variable by name: ' . $rightVarName);
609
                }
610 1
            }
611
612 1
            $symbol->incSets();
613 1
            return $compiledExpression;
614
        }
615
616
        $this->context->debug('Unknown how to pass symbol by ref');
617
        return new CompiledExpression();
618
    }
619
620
    /**
621
     * @param Node\Expr\Variable $expr
622
     * @return CompiledExpression
623
     */
624 6
    protected function passExprVariable(Node\Expr\Variable $expr)
625
    {
626 6
        $variable = $this->context->getSymbol($expr->name);
627 6
        if ($variable) {
628 6
            $variable->incGets();
629 6
            return new CompiledExpression($variable->getType(), $variable->getValue(), $variable);
630
        }
631
632
        $this->context->notice(
633
            'undefined-variable',
634
            sprintf('You are trying to use an undefined variable $%s', $expr->name),
635
            $expr
636
        );
637
638
        return new CompiledExpression();
639
    }
640
641
    /**
642
     * Compile Array_ expression to CompiledExpression
643
     *
644
     * @param Node\Expr\Array_ $expr
645
     * @return CompiledExpression
646
     */
647 16
    protected function getArray(Node\Expr\Array_ $expr)
648
    {
649 16
        if ($expr->items === []) {
650 8
            return new CompiledExpression(CompiledExpression::ARR, []);
651
        }
652
653 8
        $resultArray = [];
654
655 8
        foreach ($expr->items as $item) {
656 8
            $compiledValueResult = $this->compile($item->value);
657 8
            if ($item->key) {
658 1
                $compiledKeyResult = $this->compile($item->key);
659 1
                switch ($compiledKeyResult->getType()) {
660 1
                    case CompiledExpression::INTEGER:
661 1
                    case CompiledExpression::DOUBLE:
662 1
                    case CompiledExpression::BOOLEAN:
663 1
                    case CompiledExpression::NULL:
664 1
                    case CompiledExpression::STRING:
665 1
                        $resultArray[$compiledKeyResult->getValue()] = $compiledValueResult->getValue();
666 1
                        break;
667
                    default:
668
                        $this->context->debug("Type {$compiledKeyResult->getType()} is not supported for key value");
669
                        return new CompiledExpression(CompiledExpression::ARR);
670
                        break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
671 1
                }
672 1
            } else {
673 7
                $resultArray[] = $compiledValueResult->getValue();
674
            }
675 8
        }
676
677 8
        return new CompiledExpression(CompiledExpression::ARR, $resultArray);
678
    }
679
680
    /**
681
     * Convert const fetch expr to CompiledExpression
682
     *
683
     * @param Node\Expr\ConstFetch $expr
684
     * @return CompiledExpression
685
     */
686 5
    protected function constFetch(Node\Expr\ConstFetch $expr)
687
    {
688 5
        if ($expr->name instanceof Node\Name) {
689 5
            if ($expr->name->parts[0] === 'true') {
690 4
                return new CompiledExpression(CompiledExpression::BOOLEAN, true);
691
            }
692
693 2
            if ($expr->name->parts[0] === 'false') {
694
                return new CompiledExpression(CompiledExpression::BOOLEAN, false);
695
            }
696 2
        }
697
698
        /**
699
         * @todo Implement check
700
         */
701 2
        return $this->compile($expr->name);
702
    }
703
}
704