Completed
Push — master ( a6347e...54c663 )
by Дмитрий
18:15 queued 15:43
created

Expression::passCastBoolean()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 18
ccs 0
cts 14
cp 0
rs 8.8571
cc 6
eloc 13
nc 6
nop 1
crap 42
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\Context;
12
use PhpParser\Node;
13
use PHPSA\Definition\ClassDefinition;
14
use PHPSA\Exception\RuntimeException;
15
use PHPSA\Variable;
16
use PHPSA\Compiler\Expression\AbstractExpressionCompiler;
17
18
class Expression
0 ignored issues
show
Complexity introduced by
This class has a complexity of 150 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...
19
{
20
    /**
21
     * @var Context
22
     */
23
    protected $context;
24
25
    /**
26
     * @param Context $context
27
     */
28 363
    public function __construct(Context $context)
29
    {
30 363
        $this->context = $context;
31 363
    }
32
33
    /**
34
     * @param $expr
35
     * @return ExpressionCompilerInterface|AbstractExpressionCompiler
36
     */
37 353
    protected function factory($expr)
38
    {
39 353
        switch (get_class($expr)) {
40
            /**
41
             * Call(s)
42
             */
43 353
            case Node\Expr\MethodCall::class:
44
                return new Expression\MethodCall();
45 353
            case Node\Expr\FuncCall::class:
46
                return new Expression\FunctionCall();
47 353
            case Node\Expr\StaticCall::class:
48
                return new Expression\StaticCall();
49
            /**
50
             * Operators
51
             */
52 353
            case Node\Expr\New_::class:
53
                return new Expression\Operators\NewOp();
54 353
            case Node\Expr\Instanceof_::class:
55
                return new Expression\Operators\InstanceOfOp();
56
            /**
57
             * Assign
58
             */
59 353
            case Node\Expr\AssignOp\Pow::class:
60
                return new Expression\AssignOp\Pow();
61 353
            case Node\Expr\AssignOp\Plus::class:
62
                return new Expression\AssignOp\Plus();
63 353
            case Node\Expr\AssignOp\Minus::class:
64
                return new Expression\AssignOp\Minus();
65 353
            case Node\Expr\AssignOp\Mod::class:
66
                return new Expression\AssignOp\Mod();
67 353
            case Node\Expr\AssignOp\BitwiseOr::class:
68
                return new Expression\AssignOp\BitwiseOr();
69 353
            case Node\Expr\AssignOp\BitwiseAnd::class:
70
                return new Expression\AssignOp\BitwiseAnd();
71
            /**
72
             * BinaryOp
73
             */
74 353
            case Node\Expr\BinaryOp\Identical::class:
75 28
                return new Expression\BinaryOp\Identical();
76 325
            case Node\Expr\BinaryOp\Concat::class:
77
                return new Expression\Operators\Contact();
78 325
            case Node\Expr\BinaryOp\NotIdentical::class:
79 14
                return new Expression\BinaryOp\NotIdentical();
80 311
            case Node\Expr\BinaryOp\Equal::class:
81 34
                return new Expression\BinaryOp\Equal();
82 277
            case Node\Expr\BinaryOp\NotEqual::class:
83 17
                return new Expression\BinaryOp\NotEqual();
84
            /**
85
             * @link http://php.net/manual/en/language.operators.increment.php
86
             */
87 260
            case Node\Expr\PostInc::class:
88 4
                return new Expression\Operators\PostInc();
89 256
            case Node\Expr\PostDec::class:
90 4
                return new Expression\Operators\PostDec();
91
            /**
92
             * Arithmetical
93
             */
94 252
            case Node\Expr\BinaryOp\Div::class:
95 37
                return new Expression\Operators\Arithmetical\Div();
96 215
            case Node\Expr\BinaryOp\Plus::class:
97 41
                return new Expression\Operators\Arithmetical\Plus();
98 174
            case Node\Expr\BinaryOp\Minus::class:
99 18
                return new Expression\Operators\Arithmetical\Minus();
100 156
            case Node\Expr\BinaryOp\Mul::class:
101 35
                return new Expression\Operators\Arithmetical\Mul();
102 121
            case Node\Expr\BinaryOp\Mod::class:
103 35
                return new Expression\Operators\Arithmetical\Mod();
104
            /**
105
             * Bitwise
106
             * @link http://php.net/manual/ru/language.operators.bitwise.php
107
             */
108 86
            case Node\Expr\BinaryOp\BitwiseOr::class:
109
                return new Expression\Operators\Bitwise\BitwiseOr();
110 86
            case Node\Expr\BinaryOp\BitwiseXor::class:
111
                return new Expression\Operators\Bitwise\BitwiseXor();
112 86
            case Node\Expr\BinaryOp\BitwiseAnd::class:
113
                return new Expression\Operators\Bitwise\BitwiseAnd();
114 86
            case Node\Expr\BinaryOp\ShiftRight::class:
115
                return new Expression\Operators\Bitwise\ShiftRight();
116 86
            case Node\Expr\BinaryOp\ShiftLeft::class:
117
                return new Expression\Operators\Bitwise\ShiftLeft();
118 86
            case Node\Expr\BitwiseNot::class:
119
                return new Expression\Operators\Bitwise\BitwiseNot();
120
            /**
121
             * Logical
122
             */
123 86
            case Node\Expr\BinaryOp\BooleanOr::class:
124 10
                return new Expression\Operators\Logical\BooleanOr();
125 76
            case Node\Expr\BinaryOp\BooleanAnd::class:
126 5
                return new Expression\Operators\Logical\BooleanAnd();
127 71
            case Node\Expr\BooleanNot::class:
128 5
                return new Expression\Operators\Logical\BooleanNot();
129
            /**
130
             * Comparison
131
             */
132 66
            case Node\Expr\BinaryOp\Greater::class:
133 12
                return new Expression\Operators\Comparison\Greater();
134 54
            case Node\Expr\BinaryOp\GreaterOrEqual::class:
135 12
                return new Expression\Operators\Comparison\GreaterOrEqual();
136 42
            case Node\Expr\BinaryOp\Smaller::class:
137 12
                return new Expression\Operators\Comparison\Smaller();
138 30
            case Node\Expr\BinaryOp\SmallerOrEqual::class:
139 12
                return new Expression\Operators\Comparison\SmallerOrEqual();
140
            /**
141
             * Another
142
             */
143 18
            case Node\Expr\UnaryMinus::class:
144 9
                return new Expression\Operators\UnaryMinus();
145 9
            case Node\Expr\UnaryPlus::class:
146 9
                return new Expression\Operators\UnaryPlus();
147
        }
148
149
        return false;
150
    }
151
152
    /**
153
     * @param object|string $expr
154
     * @return CompiledExpression
155
     */
156 363
    public function compile($expr)
0 ignored issues
show
Complexity introduced by
This operation has 640 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...
157
    {
158 363
        if (is_string($expr)) {
159
            return new CompiledExpression(CompiledExpression::STRING, $expr);
160
        }
161
162 363
        if (is_null($expr)) {
163
            return new CompiledExpression(CompiledExpression::NULL);
164
        }
165
166 363
        if (!is_object($expr)) {
167
            throw new InvalidArgumentException('$expr must be string/object/null');
168
        }
169
170 363
        $className = get_class($expr);
171
        switch ($className) {
172 363
            case 'PhpParser\Node\Expr\PropertyFetch':
173
                return $this->passPropertyFetch($expr);
174 363
            case 'PhpParser\Node\Stmt\Property':
175
                return $this->passProperty($expr);
176 363
            case 'PhpParser\Node\Expr\ClassConstFetch':
177
                return $this->passConstFetch($expr);
178 363
            case 'PhpParser\Node\Expr\Assign':
179
                return $this->passSymbol($expr);
180 363
            case 'PhpParser\Node\Expr\AssignRef':
181
                return $this->passSymbolByRef($expr);
182 363
            case 'PhpParser\Node\Expr\Variable':
183
                return $this->passExprVariable($expr);
184
            /**
185
             * Cast operators
186
             */
187 363
            case 'PhpParser\Node\Expr\Cast\Bool_':
188
                return $this->passCastBoolean($expr);
189 363
            case 'PhpParser\Node\Expr\Cast\Int_':
190
                return $this->passCastInt($expr);
191 363
            case 'PhpParser\Node\Expr\Cast\Double':
192
                return $this->passCastFloat($expr);
193 363
            case 'PhpParser\Node\Expr\Cast\String_':
194
                return $this->passCastString($expr);
195 363
            case 'PhpParser\Node\Expr\Cast\Unset_':
196
                return $this->passCastUnset($expr);
197
            /**
198
             * Expressions
199
             */
200 363
            case 'PhpParser\Node\Expr\Array_':
201 11
                return $this->getArray($expr);
202 362
            case 'PhpParser\Node\Expr\ConstFetch':
203
                return $this->constFetch($expr);
204 362
            case 'PhpParser\Node\Name':
205
                return $this->getNodeName($expr);
206
            /**
207
             * Simple Scalar(s)
208
             */
209 362
            case 'PHPSA\Node\Scalar\Nil':
210 8
                return new CompiledExpression(CompiledExpression::NULL);
211 361
            case 'PhpParser\Node\Scalar\LNumber':
212 256
                return new CompiledExpression(CompiledExpression::INTEGER, $expr->value);
213 358
            case 'PhpParser\Node\Scalar\DNumber':
214 131
                return new CompiledExpression(CompiledExpression::DOUBLE, $expr->value);
215 357
            case 'PhpParser\Node\Scalar\String_':
216 8
                return new CompiledExpression(CompiledExpression::STRING, $expr->value);
217 355
            case 'PHPSA\Node\Scalar\Boolean':
218 60
                return new CompiledExpression(CompiledExpression::BOOLEAN, $expr->value);
219 353
            case 'PHPSA\Node\Scalar\Fake':
220 29
                return new CompiledExpression($expr->type, $expr->value);
221
        }
222
223 353
        $expressionCompiler = $this->factory($expr);
224 353
        if (!$expressionCompiler) {
225
            $this->context->debug("Expression compiler is not implemented for {$className}");
226
            return new CompiledExpression(CompiledExpression::UNIMPLEMENTED);
227
        }
228
229 353
        $result = $expressionCompiler->pass($expr, $this->context);
230 353
        if (!$result instanceof CompiledExpression) {
231
            throw new RuntimeException('Please return CompiledExpression from ' . get_class($expressionCompiler));
232
        }
233
234 353
        return $result;
235
    }
236
237
    /**
238
     * @todo Implement
239
     *
240
     * @param Node\Stmt\Property $st
241
     * @return CompiledExpression
242
     */
243
    public function passProperty(Node\Stmt\Property $st)
0 ignored issues
show
Unused Code introduced by
The parameter $st is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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...
244
    {
245
        return new CompiledExpression();
246
    }
247
248
    /**
249
     * @param Node\Expr\Variable $expr
250
     * @return CompiledExpression
251
     */
252
    public function declareVariable(Node\Expr\Variable $expr)
253
    {
254
        $variable = $this->context->getSymbol($expr->name);
255
        if ($variable) {
256
            $variable->incGets();
257
            return new CompiledExpression($variable->getType(), $variable->getValue(), $variable);
258
        }
259
260
        $symbol = 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...
261
        $this->context->addVariable($symbol);
262
263
        return new CompiledExpression;
264
    }
265
266
    /**
267
     * @param Node\Name $expr
268
     * @return CompiledExpression
269
     */
270
    public function getNodeName(Node\Name $expr)
271
    {
272
        if ($expr->toString() === 'null') {
273
            return new CompiledExpression(CompiledExpression::NULL);
274
        }
275
276
        if (in_array($expr, ['parent'], true)) {
277
            /** @var ClassDefinition $scope */
278
            $scope = $this->context->scope;
279
            assert($scope instanceof ClassDefinition);
280
281
            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...
282
                $definition = $scope->getExtendsClassDefinition();
283
                if ($definition) {
284
                    return new CompiledExpression(CompiledExpression::OBJECT, $definition);
285
                }
286
            } else {
287
                $this->context->notice(
288
                    'no-parent',
289
                    'Cannot access parent:: when current class scope has no parent',
290
                    $expr
291
                );
292
            }
293
        }
294
295
        if (in_array($expr, ['self', 'static'], true)) {
296
            return CompiledExpression::fromZvalValue($this->context->scope);
297
        }
298
299
        if (defined($expr)) {
300
            return CompiledExpression::fromZvalValue(constant($expr));
301
        }
302
303
        return new CompiledExpression(CompiledExpression::STRING, $expr->toString());
304
    }
305
306
    /**
307
     * (bool) {$expr}
308
     *
309
     * @param Node\Expr\Cast\Bool_ $expr
310
     * @return CompiledExpression
311
     */
312
    protected function passCastBoolean(Node\Expr\Cast\Bool_ $expr)
313
    {
314
        $expression = new Expression($this->context);
315
        $compiledExpression = $expression->compile($expr->expr);
316
317
        switch ($compiledExpression->getType()) {
318
            case CompiledExpression::BOOLEAN:
319
                $this->context->notice('stupid-cast', "You are trying to cast 'boolean' to 'boolean'", $expr);
320
                return $compiledExpression;
321
            case CompiledExpression::DOUBLE:
322
            case CompiledExpression::INTEGER:
323
            case CompiledExpression::NUMBER:
324
            case CompiledExpression::STRING:
325
                return new CompiledExpression(CompiledExpression::BOOLEAN, (bool) $compiledExpression->getValue());
326
        }
327
328
        return new CompiledExpression();
329
    }
330
331
    /**
332
     * (int) {$expr}
333
     *
334
     * @param Node\Expr\Cast\Int_ $expr
335
     * @return CompiledExpression
336
     */
337
    protected function passCastInt(Node\Expr\Cast\Int_ $expr)
338
    {
339
        $expression = new Expression($this->context);
340
        $compiledExpression = $expression->compile($expr->expr);
341
342
        switch ($compiledExpression->getType()) {
343
            case CompiledExpression::INTEGER:
344
                $this->context->notice('stupid-cast', "You are trying to cast 'int' to 'int'", $expr);
345
                return $compiledExpression;
346
            case CompiledExpression::BOOLEAN:
347
            case CompiledExpression::DOUBLE:
348
            case CompiledExpression::NUMBER:
349
            case CompiledExpression::STRING:
350
                return new CompiledExpression(CompiledExpression::INTEGER, (int) $compiledExpression->getValue());
351
        }
352
353
        return new CompiledExpression();
354
    }
355
356
    /**
357
     * (float) {$expr}
358
     *
359
     * @param Node\Expr\Cast\Double $expr
360
     * @return CompiledExpression
361
     */
362
    protected function passCastFloat(Node\Expr\Cast\Double $expr)
363
    {
364
        $expression = new Expression($this->context);
365
        $compiledExpression = $expression->compile($expr->expr);
366
367
        switch ($compiledExpression->getType()) {
368
            case CompiledExpression::DOUBLE:
369
                $this->context->notice('stupid-cast', "You are trying to cast 'float' to 'float'", $expr);
370
                return $compiledExpression;
371
            case CompiledExpression::BOOLEAN:
372
            case CompiledExpression::INTEGER:
373
            case CompiledExpression::NUMBER:
374
            case CompiledExpression::STRING:
375
                return new CompiledExpression(CompiledExpression::DOUBLE, (float) $compiledExpression->getValue());
376
        }
377
378
        return new CompiledExpression();
379
    }
380
381
    /**
382
     * (string) {$expr}
383
     *
384
     * @param Node\Expr\Cast\String_ $expr
385
     * @return CompiledExpression
386
     */
387
    protected function passCastString(Node\Expr\Cast\String_ $expr)
388
    {
389
        $expression = new Expression($this->context);
390
        $compiledExpression = $expression->compile($expr->expr);
391
392
        switch ($compiledExpression->getType()) {
393
            case CompiledExpression::STRING:
394
                $this->context->notice('stupid-cast', "You are trying to cast 'string' to 'string'", $expr);
395
                return $compiledExpression;
396
            case CompiledExpression::BOOLEAN:
397
            case CompiledExpression::INTEGER:
398
            case CompiledExpression::NUMBER:
399
            case CompiledExpression::DOUBLE:
400
                return new CompiledExpression(CompiledExpression::DOUBLE, (string) $compiledExpression->getValue());
401
        }
402
403
        return new CompiledExpression();
404
    }
405
406
    /**
407
     * (unset) {$expr}
408
     *
409
     * @param Node\Expr\Cast\Unset_ $expr
410
     * @return CompiledExpression
411
     */
412
    protected function passCastUnset(Node\Expr\Cast\Unset_ $expr)
413
    {
414
        $expression = new Expression($this->context);
415
        $compiledExpression = $expression->compile($expr->expr);
416
417
        switch ($compiledExpression->getType()) {
418
            case CompiledExpression::NULL:
419
                $this->context->notice('stupid-cast', "You are trying to cast 'unset' to 'null'", $expr);
420
                return $compiledExpression;
421
        }
422
423
        return new CompiledExpression(CompiledExpression::NULL, null);
424
    }
425
426
    /**
427
     * @param Node\Expr\PropertyFetch $expr
428
     * @return CompiledExpression
429
     */
430
    protected function passPropertyFetch(Node\Expr\PropertyFetch $expr)
431
    {
432
        $propertNameCE = $this->compile($expr->name);
433
434
        $scopeExpression = $this->compile($expr->var);
435
        if ($scopeExpression->isObject()) {
436
            $scopeExpressionValue = $scopeExpression->getValue();
437
            if ($scopeExpressionValue instanceof ClassDefinition) {
438
                $propertyName = $propertNameCE->isString() ? $propertNameCE->getValue() : false;
439
                if ($propertyName) {
440
                    if ($scopeExpressionValue->hasProperty($propertyName, true)) {
441
                        $property = $scopeExpressionValue->getProperty($propertyName, true);
442
                        return $this->compile($property);
443
                    } else {
444
                        $this->context->notice(
445
                            'undefined-property',
446
                            sprintf(
447
                                'Property %s does not exist in %s scope',
448
                                $propertyName,
449
                                $scopeExpressionValue->getName()
450
                            ),
451
                            $expr
452
                        );
453
                    }
454
                }
455
            }
456
        } elseif (!$scopeExpression->canBeObject()) {
457
            return new CompiledExpression(CompiledExpression::UNKNOWN);
458
        }
459
460
        $this->context->notice(
461
            'property-fetch-on-non-object',
462
            "It's not possible to fetch property on not object",
463
            $expr,
464
            Check::CHECK_BETA
465
        );
466
467
        return new CompiledExpression(CompiledExpression::UNKNOWN);
468
    }
469
470
    /**
471
     * @param Node\Expr\ClassConstFetch $expr
472
     * @return CompiledExpression
473
     */
474
    protected function passConstFetch(Node\Expr\ClassConstFetch $expr)
475
    {
476
        $leftCE = $this->compile($expr->class);
477
        if ($leftCE->isObject()) {
478
            $leftCEValue = $leftCE->getValue();
479
            if ($leftCEValue instanceof ClassDefinition) {
480
                if (!$leftCEValue->hasConst($expr->name)) {
481
                    $this->context->notice(
482
                        'undefined-const',
483
                        sprintf('Constant %s does not exist in %s scope', $expr->name, $expr->class),
484
                        $expr
485
                    );
486
                    return new CompiledExpression(CompiledExpression::UNKNOWN);
487
                }
488
489
                return new CompiledExpression();
490
            }
491
        }
492
493
        $this->context->debug('Unknown const fetch');
494
        return new CompiledExpression();
495
    }
496
497
    /**
498
     * @param Node\Expr\Assign $expr
499
     * @return CompiledExpression
500
     */
501
    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...
502
    {
503
        $expression = new Expression($this->context);
504
        $compiledExpression = $expression->compile($expr->expr);
505
506
        if ($expr->var instanceof Node\Expr\List_) {
507
            $isCorrectType = false;
508
509
            switch ($compiledExpression->getType()) {
510
                case CompiledExpression::ARR:
511
                    $isCorrectType = true;
512
                    break;
513
            }
514
515
            if ($expr->var->vars) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $expr->var->vars of type PhpParser\Node\Expr[] 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...
516
                foreach ($expr->var->vars as $key => $var) {
517
                    if ($var instanceof Node\Expr\Variable) {
518
                        if (!isset($expr->var->name)) {
519
                            var_dump($var);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($var); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
520
                        }
521
                        $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...
522
523
                        $symbol = $this->context->getSymbol($name);
524
                        if (!$symbol) {
525
                            $symbol = new Variable(
526
                                $name,
527
                                null,
528
                                CompiledExpression::UNKNOWN,
529
                                $this->context->getCurrentBranch()
530
                            );
531
                            $this->context->addVariable($symbol);
532
                        }
533
534
                        if (!$isCorrectType) {
535
                            $symbol->modify(CompiledExpression::NULL, null);
536
                        }
537
538
                        $symbol->incSets();
539
                    }
540
                }
541
            }
542
543
            return new CompiledExpression();
544
        }
545
546
        if ($expr->var instanceof Node\Expr\Variable) {
547
            $name = $expr->var->name;
548
549
            $symbol = $this->context->getSymbol($name);
550
            if ($symbol) {
551
                $symbol->modify($compiledExpression->getType(), $compiledExpression->getValue());
552
            } else {
553
                $symbol = new Variable(
554
                    $name,
555
                    $compiledExpression->getValue(),
556
                    $compiledExpression->getType(),
557
                    $this->context->getCurrentBranch()
558
                );
559
                $this->context->addVariable($symbol);
560
            }
561
562
            $symbol->incSets();
563
            return $compiledExpression;
564
        }
565
566
        if ($expr->var instanceof Node\Expr\PropertyFetch) {
567
            $compiledExpression = $this->compile($expr->var->var);
568
            if ($compiledExpression->getType() == CompiledExpression::OBJECT) {
569
                $objectDefinition = $compiledExpression->getValue();
570
                if ($objectDefinition instanceof ClassDefinition) {
571
                    if (is_string($expr->var->name)) {
572
                        if ($objectDefinition->hasProperty($expr->var->name)) {
573
                            return $this->compile($objectDefinition->getProperty($expr->var->name));
574
                        }
575
                    }
576
                }
577
            }
578
        }
579
580
        $this->context->debug('Unknown how to pass symbol');
581
        return new CompiledExpression();
582
    }
583
584
    /**
585
     * @param Node\Expr\AssignRef $expr
586
     * @return CompiledExpression
587
     */
588
    protected function passSymbolByRef(Node\Expr\AssignRef $expr)
589
    {
590
        if ($expr->var instanceof \PhpParser\Node\Expr\List_) {
591
            return new CompiledExpression();
592
        }
593
594
        if ($expr->var instanceof Node\Expr\Variable) {
595
            $name = $expr->var->name;
596
597
            $expression = new Expression($this->context);
598
            $compiledExpression = $expression->compile($expr->expr);
599
600
            $symbol = $this->context->getSymbol($name);
601
            if ($symbol) {
602
                $symbol->modify($compiledExpression->getType(), $compiledExpression->getValue());
603
604
                if ($expr->expr instanceof Node\Expr\Variable) {
605
                    $rightVarName = $expr->expr->name;
606
607
                    $rightSymbol = $this->context->getSymbol($rightVarName);
608
                    if ($rightSymbol) {
609
                        $rightSymbol->incUse();
610
                        $symbol->setReferencedTo($rightSymbol);
611
                    } else {
612
                        $this->context->debug('Cannot fetch variable by name: ' . $rightVarName);
613
                    }
614
                }
615
616
                $this->context->debug('Unknown how to pass referenced to symbol: ' . get_class($expr->expr));
617
            } else {
618
                $symbol = new Variable(
619
                    $name,
0 ignored issues
show
Bug introduced by
It seems like $name defined by $expr->var->name on line 595 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...
620
                    $compiledExpression->getValue(),
621
                    $compiledExpression->getType(),
622
                    $this->context->getCurrentBranch()
623
                );
624
                $this->context->addVariable($symbol);
625
            }
626
627
            $symbol->incSets();
628
            return $compiledExpression;
629
        }
630
631
        $this->context->debug('Unknown how to pass symbol by ref');
632
        return new CompiledExpression();
633
    }
634
635
    /**
636
     * @param Node\Expr\Variable $expr
637
     * @return CompiledExpression
638
     */
639
    protected function passExprVariable(Node\Expr\Variable $expr)
640
    {
641
        $variable = $this->context->getSymbol($expr->name);
642
        if ($variable) {
643
            $variable->incGets();
644
            return new CompiledExpression($variable->getType(), $variable->getValue(), $variable);
645
        }
646
647
        $this->context->notice(
648
            'undefined-variable',
649
            sprintf('You trying to use undefined variable $%s', $expr->name),
650
            $expr
651
        );
652
653
        return new CompiledExpression();
654
    }
655
656
    /**
657
     * Compile Array_ expression to CompiledExpression
658
     *
659
     * @param Node\Expr\Array_ $expr
660
     * @return CompiledExpression
661
     */
662 11
    protected function getArray(Node\Expr\Array_ $expr)
663
    {
664 11
        if ($expr->items === array()) {
665 8
            return new CompiledExpression(CompiledExpression::ARR, array());
666
        }
667
668 3
        $resultArray = array();
669
670 3
        foreach ($expr->items as $item) {
671 3
            $compiledValueResult = $this->compile($item->value);
672 3
            if ($item->key) {
673 1
                $compiledKeyResult = $this->compile($item->key);
674 1
                switch ($compiledKeyResult->getType()) {
675 1
                    case CompiledExpression::INTEGER:
676 1
                    case CompiledExpression::DOUBLE:
677 1
                    case CompiledExpression::BOOLEAN:
678 1
                    case CompiledExpression::NULL:
679 1
                    case CompiledExpression::STRING:
680 1
                        $resultArray[$compiledKeyResult->getValue()] = $compiledValueResult->getValue();
681 1
                        break;
682
                    default:
683
                        $this->context->debug("Type {$compiledKeyResult->getType()} is not supported for key value");
684
                        return new CompiledExpression(CompiledExpression::ARR);
685
                        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...
686 1
                }
687 1
            } else {
688 2
                $resultArray[] = $compiledValueResult->getValue();
689
            }
690 3
        }
691
692 3
        return new CompiledExpression(CompiledExpression::ARR, $resultArray);
693
    }
694
695
    /**
696
     * Convert const fetch expr to CompiledExpression
697
     *
698
     * @param Node\Expr\ConstFetch $expr
699
     * @return CompiledExpression
700
     */
701
    protected function constFetch(Node\Expr\ConstFetch $expr)
702
    {
703
        if ($expr->name instanceof Node\Name) {
704
            if ($expr->name->parts[0] === 'true') {
705
                return new CompiledExpression(CompiledExpression::BOOLEAN, true);
706
            }
707
708
            if ($expr->name->parts[0] === 'false') {
709
                return new CompiledExpression(CompiledExpression::BOOLEAN, false);
710
            }
711
        }
712
713
        /**
714
         * @todo Implement check
715
         */
716
717
        $expression = new Expression($this->context);
718
        $compiledExpr = $expression->compile($expr->name);
719
720
        return $compiledExpr;
721
    }
722
}
723