Completed
Pull Request — master (#196)
by Enrico
06:55
created

Expression::compile()   C

Complexity

Conditions 18
Paths 18

Size

Total Lines 74
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 41
CRAP Score 18.6747

Importance

Changes 0
Metric Value
cc 18
eloc 46
nc 18
nop 1
dl 0
loc 74
ccs 41
cts 47
cp 0.8723
crap 18.6747
rs 5.3391
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 135 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 849
    public function __construct(Context $context, EventManager $eventManager)
37
    {
38 849
        $this->context = $context;
39 849
        $this->eventManager = $eventManager;
40 849
    }
41
42
    /**
43
     * @param $expr
44
     * @return ExpressionCompilerInterface|AbstractExpressionCompiler
45
     */
46 828
    protected function factory($expr)
47
    {
48 828
        switch (get_class($expr)) {
49
            /**
50
             * Call(s)
51
             */
52 828
            case Node\Expr\MethodCall::class:
53
                return new Expression\MethodCall();
54 828
            case Node\Expr\FuncCall::class:
55 10
                return new Expression\FunctionCall();
56 824
            case Node\Expr\StaticCall::class:
57 1
                return new Expression\StaticCall();
58
            /**
59
             * Operators
60
             */
61 823
            case Node\Expr\New_::class:
62 2
                return new Expression\Operators\NewOp();
63 823
            case Node\Expr\Instanceof_::class:
64
                return new Expression\Operators\InstanceOfOp();
65
            /**
66
             * AssignOp
67
             */
68 823
            case Node\Expr\AssignOp\Pow::class:
69 21
                return new Expression\AssignOp\Pow();
70 802
            case Node\Expr\AssignOp\Plus::class:
71 17
                return new Expression\AssignOp\Plus();
72 785
            case Node\Expr\AssignOp\Minus::class:
73 20
                return new Expression\AssignOp\Minus();
74 765
            case Node\Expr\AssignOp\Mul::class:
75 20
                return new Expression\AssignOp\Mul();
76 745
            case Node\Expr\AssignOp\Div::class:
77 18
                return new Expression\AssignOp\Div();
78 727
            case Node\Expr\AssignOp\Mod::class:
79 11
                return new Expression\AssignOp\Mod();
80 716
            case Node\Expr\AssignOp\BitwiseOr::class:
81 12
                return new Expression\AssignOp\BitwiseOr();
82 704
            case Node\Expr\AssignOp\BitwiseAnd::class:
83 12
                return new Expression\AssignOp\BitwiseAnd();
84 692
            case Node\Expr\AssignOp\BitwiseXor::class:
85 12
                return new Expression\AssignOp\BitwiseXor();
86 680
            case Node\Expr\AssignOp\Concat::class:
87 14
                return new Expression\AssignOp\Concat();
88 666
            case Node\Expr\AssignOp\ShiftLeft::class:
89 12
                return new Expression\AssignOp\ShiftLeft();
90 654
            case Node\Expr\AssignOp\ShiftRight::class:
91 12
                return new Expression\AssignOp\ShiftRight();
92
93
            /**
94
             * BinaryOp
95
             */
96 642
            case Node\Expr\BinaryOp\Identical::class:
97 28
                return new Expression\BinaryOp\Identical();
98 614
            case Node\Expr\BinaryOp\Concat::class:
99 15
                return new Expression\Operators\Concat();
100 600
            case Node\Expr\BinaryOp\NotIdentical::class:
101 14
                return new Expression\BinaryOp\NotIdentical();
102 586
            case Node\Expr\BinaryOp\Equal::class:
103 42
                return new Expression\BinaryOp\Equal();
104 552
            case Node\Expr\BinaryOp\NotEqual::class:
105 17
                return new Expression\BinaryOp\NotEqual();
106 535
            case Node\Expr\BinaryOp\Spaceship::class:
107 11
                return new Expression\BinaryOp\SpaceShip();
108 526
            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 524
            case Node\Expr\PostInc::class:
115 8
                return new Expression\Operators\PostInc();
116 516
            case Node\Expr\PostDec::class:
117 8
                return new Expression\Operators\PostDec();
118 508
            case Node\Expr\PreInc::class:
119 8
                return new Expression\Operators\PreInc();
120 500
            case Node\Expr\PreDec::class:
121 8
                return new Expression\Operators\PreDec();
122
            /**
123
             * Arithmetical
124
             */
125 492
            case Node\Expr\BinaryOp\Div::class:
126 37
                return new Expression\Operators\Arithmetical\Div();
127 455
            case Node\Expr\BinaryOp\Plus::class:
128 45
                return new Expression\Operators\Arithmetical\Plus();
129 412
            case Node\Expr\BinaryOp\Minus::class:
130 24
                return new Expression\Operators\Arithmetical\Minus();
131 388
            case Node\Expr\BinaryOp\Mul::class:
132 35
                return new Expression\Operators\Arithmetical\Mul();
133 353
            case Node\Expr\BinaryOp\Mod::class:
134 35
                return new Expression\Operators\Arithmetical\Mod();
135 318
            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 297
            case Node\Expr\BinaryOp\BitwiseOr::class:
143 12
                return new Expression\Operators\Bitwise\BitwiseOr();
144 285
            case Node\Expr\BinaryOp\BitwiseXor::class:
145 12
                return new Expression\Operators\Bitwise\BitwiseXor();
146 273
            case Node\Expr\BinaryOp\BitwiseAnd::class:
147 12
                return new Expression\Operators\Bitwise\BitwiseAnd();
148 261
            case Node\Expr\BinaryOp\ShiftRight::class:
149 12
                return new Expression\Operators\Bitwise\ShiftRight();
150 249
            case Node\Expr\BinaryOp\ShiftLeft::class:
151 12
                return new Expression\Operators\Bitwise\ShiftLeft();
152 237
            case Node\Expr\BitwiseNot::class:
153 5
                return new Expression\Operators\Bitwise\BitwiseNot();
154
            /**
155
             * Logical
156
             */
157 232
            case Node\Expr\BinaryOp\BooleanOr::class:
158 17
                return new Expression\Operators\Logical\BooleanOr();
159 217
            case Node\Expr\BinaryOp\BooleanAnd::class:
160 15
                return new Expression\Operators\Logical\BooleanAnd();
161 204
            case Node\Expr\BooleanNot::class:
162 9
                return new Expression\Operators\Logical\BooleanNot();
163 196
            case Node\Expr\BinaryOp\LogicalAnd::class:
164 15
                return new Expression\Operators\Logical\LogicalAnd();
165 183
            case Node\Expr\BinaryOp\LogicalOr::class:
166 17
                return new Expression\Operators\Logical\LogicalOr();
167 168
            case Node\Expr\BinaryOp\LogicalXor::class:
168 17
                return new Expression\Operators\Logical\LogicalXor();
169
170
            /**
171
             * Comparison
172
             */
173 153
            case Node\Expr\BinaryOp\Greater::class:
174 12
                return new Expression\Operators\Comparison\Greater();
175 141
            case Node\Expr\BinaryOp\GreaterOrEqual::class:
176 12
                return new Expression\Operators\Comparison\GreaterOrEqual();
177 129
            case Node\Expr\BinaryOp\Smaller::class:
178 12
                return new Expression\Operators\Comparison\Smaller();
179 117
            case Node\Expr\BinaryOp\SmallerOrEqual::class:
180 12
                return new Expression\Operators\Comparison\SmallerOrEqual();
181
182
            /**
183
             * Casts
184
             */
185 105
            case Node\Expr\Cast\Array_::class:
186 3
                return new Expression\Casts\ArrayCast();
187 104
            case Node\Expr\Cast\Bool_::class:
188 8
                return new Expression\Casts\BoolCast();
189 98
            case Node\Expr\Cast\Int_::class:
190 8
                return new Expression\Casts\IntCast();
191 92
            case Node\Expr\Cast\Double::class:
192 8
                return new Expression\Casts\DoubleCast();
193 86
            case Node\Expr\Cast\Object_::class:
194 2
                return new Expression\Casts\ObjectCast();
195 85
            case Node\Expr\Cast\String_::class:
196 7
                return new Expression\Casts\StringCast();
197 79
            case Node\Expr\Cast\Unset_::class:
198 2
                return new Expression\Casts\UnsetCast();
199
200
201
            /**
202
             * Other
203
             */
204 78
            case Node\Expr\Array_::class:
205 36
                return new Expression\ArrayOp();
206 50
            case Node\Expr\Closure::class:
207
                return new Expression\Closure();
208 50
            case Node\Expr\ConstFetch::class:
209 7
                return new Expression\ConstFetch();
210 47
            case Node\Expr\ClassConstFetch::class:
211
                return new Expression\ClassConstFetch();
212 47
            case Node\Expr\PropertyFetch::class:
213
                return new Expression\PropertyFetch();
214 47
            case Node\Expr\UnaryMinus::class:
215 9
                return new Expression\Operators\UnaryMinus();
216 38
            case Node\Expr\UnaryPlus::class:
217 9
                return new Expression\Operators\UnaryPlus();
218 29
            case Node\Expr\Exit_::class:
219 1
                return new Expression\ExitOp();
220 28
            case Node\Expr\Isset_::class:
221 3
                return new Expression\IssetOp();
222 25
            case Node\Expr\Print_::class:
223 2
                return new Expression\PrintOp();
224 24
            case Node\Expr\Empty_::class:
225 5
                return new Expression\EmptyOp();
226 19
            case Node\Expr\Eval_::class:
227 2
                return new Expression\EvalOp();
228 17
            case Node\Expr\ErrorSuppress::class:
229 2
                return new Expression\ErrorSuppress();
230 15
            case Node\Expr\Clone_::class:
231 1
                return new Expression\CloneOp();
232 14
            case Node\Expr\Ternary::class:
233 2
                return new Expression\Ternary();
234 12
            case Node\Expr\Variable::class:
235 12
                return new Expression\Variable();
236
        }
237
238
        return false;
239
    }
240
241
    /**
242
     * @param object|string $expr
243
     * @throws InvalidArgumentException when $expr is not string/object/null
244
     * @throws RuntimeException when compiler class does not return a CompiledExpression
245
     * @return CompiledExpression
246
     */
247 849
    public function compile($expr)
0 ignored issues
show
Complexity introduced by
This operation has 384 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...
248
    {
249 849
        if (is_string($expr)) {
250 16
            return new CompiledExpression(CompiledExpression::STRING, $expr);
251
        }
252
253 849
        if (is_null($expr)) {
254 8
            return new CompiledExpression(CompiledExpression::NULL);
255
        }
256
257 849
        if (!is_object($expr)) {
258
            throw new InvalidArgumentException('$expr must be string/object/null');
259
        }
260
261 849
        $this->eventManager->fire(
262 849
            ExpressionBeforeCompile::EVENT_NAME,
263 849
            new ExpressionBeforeCompile(
264 849
                $expr,
265 849
                $this->context
266 849
            )
267 849
        );
268
269 849
        $className = get_class($expr);
270
        switch ($className) {
271 849
            case Node\Arg::class:
272
                /**
273
                 * @todo Better compile
274
                 */
275 2
                return $this->compile($expr->value);
276 849
            case Node\Stmt\Property::class:
277
                return $this->passProperty($expr);
278 849
            case Node\Expr\Assign::class:
279 29
                return $this->passSymbol($expr);
280 849
            case Node\Expr\AssignRef::class:
281 1
                return $this->passSymbolByRef($expr);
282
283
            /**
284
             * Expressions
285
             */
286 849
            case Node\Name::class:
287 27
                return $this->getNodeName($expr);
288 849
            case Node\Name\FullyQualified::class:
289
                return $this->getFullyQualifiedNodeName($expr);
290
291
            /**
292
             * Simple Scalar(s)
293
             */
294 849
            case \PHPSA\Node\Scalar\Nil::class:
295 25
                return new CompiledExpression(CompiledExpression::NULL);
296 848
            case Node\Scalar\LNumber::class:
297 512
                return new CompiledExpression(CompiledExpression::INTEGER, $expr->value);
298 832
            case Node\Scalar\DNumber::class:
299 212
                return new CompiledExpression(CompiledExpression::DOUBLE, $expr->value);
300 831
            case Node\Scalar\String_::class:
301 50
                return new CompiledExpression(CompiledExpression::STRING, $expr->value);
302 830
            case \PHPSA\Node\Scalar\Boolean::class:
303 241
                return new CompiledExpression(CompiledExpression::BOOLEAN, $expr->value);
304 828
            case \PHPSA\Node\Scalar\Fake::class:
305 79
                return new CompiledExpression($expr->type, $expr->value);
306
        }
307
308 828
        $expressionCompiler = $this->factory($expr);
309 828
        if (!$expressionCompiler) {
310
            $this->context->debug("Expression compiler is not implemented for {$className}");
311
            return new CompiledExpression(CompiledExpression::UNIMPLEMENTED);
312
        }
313
314 828
        $result = $expressionCompiler->pass($expr, $this->context);
315 828
        if (!$result instanceof CompiledExpression) {
316
            throw new RuntimeException('Please return CompiledExpression from ' . get_class($expressionCompiler));
317
        }
318
319 828
        return $result;
320
    }
321
322
    /**
323
     * @todo Implement - needs to be a new analyzer for var docblock
324
     *
325
     * @param Node\Stmt\Property $st
326
     * @return CompiledExpression
327
     */
328
    public function passProperty(Node\Stmt\Property $st)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $st. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
329
    {
330
        $docBlock = $st->getDocComment();
331
        if (!$docBlock) {
332
            return new CompiledExpression();
333
        }
334
335
        $phpdoc = DocBlockFactory::createInstance()->create($docBlock->getText());
336
337
        $varTags = $phpdoc->getTagsByName('var');
338
        if ($varTags) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $varTags of type phpDocumentor\Reflection\DocBlock\Tag[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
339
            /** @var \phpDocumentor\Reflection\DocBlock\Tag\VarTag $varTag */
340
            $varTag = current($varTags);
341
342
            $typeResolver = new \phpDocumentor\Reflection\TypeResolver();
343
344
            try {
345
                $type = $typeResolver->resolve($varTag->getType());
346
            } catch (\InvalidArgumentException $e) {
347
                return new CompiledExpression();
348
            }
349
350
            if ($type) {
351
                switch (get_class($type)) {
352
                    case \phpDocumentor\Reflection\Types\Object_::class:
353
                        return new CompiledExpression(
354
                            CompiledExpression::OBJECT
355
                        );
356
                    case \phpDocumentor\Reflection\Types\Integer::class:
357
                        return new CompiledExpression(
358
                            CompiledExpression::INTEGER
359
                        );
360
                    case \phpDocumentor\Reflection\Types\String_::class:
361
                        return new CompiledExpression(
362
                            CompiledExpression::STRING
363
                        );
364
                    case \phpDocumentor\Reflection\Types\Float_::class:
365
                        return new CompiledExpression(
366
                            CompiledExpression::DOUBLE
367
                        );
368
                    case \phpDocumentor\Reflection\Types\Null_::class:
369
                        return new CompiledExpression(
370
                            CompiledExpression::NULL
371
                        );
372
                    case \phpDocumentor\Reflection\Types\Boolean::class:
373
                        return new CompiledExpression(
374
                            CompiledExpression::BOOLEAN
375
                        );
376
                }
377
            }
378
        }
379
380
        return new CompiledExpression();
381
    }
382
383
    /**
384
     * @param Node\Expr\Variable $expr
385
     * @param mixed $value
386
     * @param int $type
387
     * @return CompiledExpression
388
     */
389 1
    public function declareVariable(Node\Expr\Variable $expr, $value = null, $type = CompiledExpression::UNKNOWN)
390
    {
391 1
        $variable = $this->context->getSymbol($expr->name);
392 1
        if (!$variable) {
393 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...
394 1
            $this->context->addVariable($variable);
395 1
        }
396
397 1
        return new CompiledExpression($variable->getType(), $variable->getValue(), $variable);
398
    }
399
400
    /**
401
     * @param Node\Name\FullyQualified $expr
402
     * @return CompiledExpression
403
     */
404
    public function getFullyQualifiedNodeName(Node\Name\FullyQualified $expr)
405
    {
406
        $this->context->debug('Unimplemented FullyQualified', $expr);
407
408
        return new CompiledExpression;
409
    }
410
411
    /**
412
     * @param Node\Name $expr
413
     * @return CompiledExpression
414
     */
415 27
    public function getNodeName(Node\Name $expr)
416
    {
417 27
        $nodeString = $expr->toString();
418 27
        if ($nodeString === 'null') {
419 1
            return new CompiledExpression(CompiledExpression::NULL);
420
        }
421
422 27
        if (in_array($nodeString, ['parent'], true)) {
423
            /** @var ClassDefinition $scope */
424
            $scope = $this->context->scope;
425
            assert($scope instanceof ClassDefinition);
426
427
            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...
428
                $definition = $scope->getExtendsClassDefinition();
429
                if ($definition) {
430
                    return new CompiledExpression(CompiledExpression::OBJECT, $definition);
431
                }
432
            } else {
433
                $this->context->notice(
434
                    'no-parent',
435
                    'Cannot access parent:: when current class scope has no parent',
436
                    $expr
437
                );
438
            }
439
        }
440
441 27
        if (in_array($nodeString, ['self', 'static'], true)) {
442 1
            return CompiledExpression::fromZvalValue($this->context->scope);
443
        }
444
445 26
        if (defined($nodeString)) {
446 7
            return CompiledExpression::fromZvalValue(constant($expr));
447
        }
448
449 23
        return new CompiledExpression(CompiledExpression::STRING, $expr->toString());
450
    }
451
452
    /**
453
     * @param Node\Expr\Assign $expr
454
     * @return CompiledExpression
455
     */
456 29
    protected function passSymbol(Node\Expr\Assign $expr)
0 ignored issues
show
Complexity introduced by
This operation has 216 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...
457
    {
458 29
        $compiledExpression = $this->compile($expr->expr);
459
460 29
        if ($expr->var instanceof Node\Expr\List_) {
461 1
            $isCorrectType = $compiledExpression->isArray();
462
463 1
            foreach ($expr->var->vars as $key => $var) {
464 1
                if (!$var instanceof Node\Expr\Variable) {
465 1
                    continue;
466
                }
467
468 1
                if ($var->name instanceof Node\Expr\Variable) {
469 1
                    $this->compileVariableDeclaration($this->compile($var->name), new CompiledExpression());
470 1
                    continue;
471
                }
472
473 1
                $symbol = $this->context->getSymbol($var->name);
474 1
                if (!$symbol) {
475 1
                    $symbol = new Variable(
476 1
                        $var->name,
0 ignored issues
show
Bug introduced by
It seems like $var->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...
477 1
                        null,
478 1
                        CompiledExpression::UNKNOWN,
479 1
                        $this->context->getCurrentBranch()
480 1
                    );
481 1
                    $this->context->addVariable($symbol);
482 1
                }
483
484 1
                if (!$isCorrectType) {
485
                    $symbol->modify(CompiledExpression::NULL, null);
486
                }
487
488 1
                $symbol->incSets();
489 1
            }
490
491 1
            return new CompiledExpression();
492
        }
493
494 29
        if ($expr->var instanceof Node\Expr\Variable) {
495 29
            $this->compileVariableDeclaration($this->compile($expr->var->name), $compiledExpression);
496
497 29
            return $compiledExpression;
498
        }
499
500 2
        if ($expr->var instanceof Node\Expr\PropertyFetch) {
501 2
            $compiledExpression = $this->compile($expr->var->var);
502 2
            if ($compiledExpression->getType() == CompiledExpression::OBJECT) {
503 2
                $objectDefinition = $compiledExpression->getValue();
504 2
                if ($objectDefinition instanceof ClassDefinition) {
505 2
                    if (is_string($expr->var->name)) {
506 2
                        if ($objectDefinition->hasProperty($expr->var->name)) {
507
                            return $this->compile($objectDefinition->getProperty($expr->var->name));
508
                        }
509 2
                    }
510 2
                }
511 2
            }
512 2
        }
513
514 2
        $this->context->debug('Unknown how to pass symbol');
515 2
        return new CompiledExpression();
516
    }
517
518 29
    protected function compileVariableDeclaration(CompiledExpression $variableName, CompiledExpression $value)
519
    {
520 29
        switch ($variableName->getType()) {
521 29
            case CompiledExpression::STRING:
522 29
                break;
523
            default:
524
                $this->context->debug('Unexpected type of Variable name after compile');
525
                return new CompiledExpression();
526 29
        }
527
528 29
        $symbol = $this->context->getSymbol($variableName->getValue());
529 29
        if ($symbol) {
530 3
            $symbol->modify($value->getType(), $value->getValue());
531 3
            $this->context->modifyReferencedVariables(
532 3
                $symbol,
533 3
                $value->getType(),
534 3
                $value->getValue()
535 3
            );
536 3
        } else {
537 29
            $symbol = new Variable(
538 29
                $variableName->getValue(),
539 29
                $value->getValue(),
540 29
                $value->getType(),
541 29
                $this->context->getCurrentBranch()
542 29
            );
543 29
            $this->context->addVariable($symbol);
544
        }
545
546 29
        $symbol->incSets();
547 29
    }
548
549
    /**
550
     * @param Node\Expr\AssignRef $expr
551
     * @return CompiledExpression
552
     */
553 1
    protected function passSymbolByRef(Node\Expr\AssignRef $expr)
554
    {
555 1
        if ($expr->var instanceof Node\Expr\Variable) {
556 1
            $name = $expr->var->name;
557
558 1
            $compiledExpression = $this->compile($expr->expr);
559
560 1
            $symbol = $this->context->getSymbol($name);
561 1
            if ($symbol) {
562
                $symbol->modify($compiledExpression->getType(), $compiledExpression->getValue());
563
            } else {
564 1
                $symbol = new Variable(
565 1
                    $name,
0 ignored issues
show
Bug introduced by
It seems like $name defined by $expr->var->name on line 556 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...
566 1
                    $compiledExpression->getValue(),
567 1
                    $compiledExpression->getType(),
568 1
                    $this->context->getCurrentBranch()
569 1
                );
570 1
                $this->context->addVariable($symbol);
571
            }
572
573 1
            if ($expr->expr instanceof Node\Expr\Variable) {
574 1
                $rightVarName = $expr->expr->name;
575
576 1
                $rightSymbol = $this->context->getSymbol($rightVarName);
577 1
                if ($rightSymbol) {
578 1
                    $rightSymbol->incUse();
579 1
                    $symbol->setReferencedTo($rightSymbol);
580 1
                } else {
581
                    $this->context->debug('Cannot fetch variable by name: ' . $rightVarName);
582
                }
583 1
            }
584
585 1
            $symbol->incSets();
586 1
            return $compiledExpression;
587
        }
588
589
        $this->context->debug('Unknown how to pass symbol by ref');
590
        return new CompiledExpression();
591
    }
592
}
593