Completed
Push — master ( 558809...774145 )
by Дмитрий
03:36
created

Expression::factory()   D

Complexity

Conditions 59
Paths 59

Size

Total Lines 163
Code Lines 119

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 116
CRAP Score 59.1285

Importance

Changes 0
Metric Value
cc 59
eloc 119
c 0
b 0
f 0
nc 59
nop 1
dl 0
loc 163
ccs 116
cts 120
cp 0.9667
crap 59.1285
rs 4.1818

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 152 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 725
    public function __construct(Context $context, EventManager $eventManager)
36
    {
37 725
        $this->context = $context;
38 725
        $this->eventManager = $eventManager;
39 725
    }
40
41
    /**
42
     * @param $expr
43
     * @return ExpressionCompilerInterface|AbstractExpressionCompiler
44
     */
45 709
    protected function factory($expr)
46
    {
47 709
        switch (get_class($expr)) {
48
            /**
49
             * Call(s)
50
             */
51 709
            case Node\Expr\MethodCall::class:
52
                return new Expression\MethodCall();
53 709
            case Node\Expr\FuncCall::class:
54 10
                return new Expression\FunctionCall();
55 701
            case Node\Expr\StaticCall::class:
56
                return new Expression\StaticCall();
57
            /**
58
             * Operators
59
             */
60 701
            case Node\Expr\New_::class:
61 1
                return new Expression\Operators\NewOp();
62 700
            case Node\Expr\Instanceof_::class:
63
                return new Expression\Operators\InstanceOfOp();
64
            /**
65
             * AssignOp
66
             */
67 700
            case Node\Expr\AssignOp\Pow::class:
68 21
                return new Expression\AssignOp\Pow();
69 679
            case Node\Expr\AssignOp\Plus::class:
70 17
                return new Expression\AssignOp\Plus();
71 662
            case Node\Expr\AssignOp\Minus::class:
72 20
                return new Expression\AssignOp\Minus();
73 642
            case Node\Expr\AssignOp\Mul::class:
74 20
                return new Expression\AssignOp\Mul();
75 622
            case Node\Expr\AssignOp\Div::class:
76 18
                return new Expression\AssignOp\Div();
77 604
            case Node\Expr\AssignOp\Mod::class:
78 11
                return new Expression\AssignOp\Mod();
79 593
            case Node\Expr\AssignOp\BitwiseOr::class:
80 12
                return new Expression\AssignOp\BitwiseOr();
81 581
            case Node\Expr\AssignOp\BitwiseAnd::class:
82 12
                return new Expression\AssignOp\BitwiseAnd();
83 569
            case Node\Expr\AssignOp\BitwiseXor::class:
84 12
                return new Expression\AssignOp\BitwiseXor();
85 557
            case Node\Expr\AssignOp\Concat::class:
86 14
                return new Expression\AssignOp\Concat();
87 543
            case Node\Expr\AssignOp\ShiftLeft::class:
88 12
                return new Expression\AssignOp\ShiftLeft();
89 531
            case Node\Expr\AssignOp\ShiftRight::class:
90 12
                return new Expression\AssignOp\ShiftRight();
91
92
            /**
93
             * BinaryOp
94
             */
95 519
            case Node\Expr\BinaryOp\Identical::class:
96 28
                return new Expression\BinaryOp\Identical();
97 491
            case Node\Expr\BinaryOp\Concat::class:
98 1
                return new Expression\Operators\Concat();
99 490
            case Node\Expr\BinaryOp\NotIdentical::class:
100 14
                return new Expression\BinaryOp\NotIdentical();
101 476
            case Node\Expr\BinaryOp\Equal::class:
102 34
                return new Expression\BinaryOp\Equal();
103 442
            case Node\Expr\BinaryOp\NotEqual::class:
104 17
                return new Expression\BinaryOp\NotEqual();
105
            /**
106
             * @link http://php.net/manual/en/language.operators.increment.php
107
             */
108 425
            case Node\Expr\PostInc::class:
109 8
                return new Expression\Operators\PostInc();
110 417
            case Node\Expr\PostDec::class:
111 8
                return new Expression\Operators\PostDec();
112 409
            case Node\Expr\PreInc::class:
113 8
                return new Expression\Operators\PreInc();
114 401
            case Node\Expr\PreDec::class:
115 8
                return new Expression\Operators\PreDec();
116
            /**
117
             * Arithmetical
118
             */
119 393
            case Node\Expr\BinaryOp\Div::class:
120 37
                return new Expression\Operators\Arithmetical\Div();
121 356
            case Node\Expr\BinaryOp\Plus::class:
122 45
                return new Expression\Operators\Arithmetical\Plus();
123 311
            case Node\Expr\BinaryOp\Minus::class:
124 18
                return new Expression\Operators\Arithmetical\Minus();
125 293
            case Node\Expr\BinaryOp\Mul::class:
126 35
                return new Expression\Operators\Arithmetical\Mul();
127 258
            case Node\Expr\BinaryOp\Mod::class:
128 35
                return new Expression\Operators\Arithmetical\Mod();
129 223
            case Node\Expr\BinaryOp\Pow::class:
130 21
                return new Expression\Operators\Arithmetical\Pow();
131
132
            /**
133
             * Bitwise
134
             * @link http://php.net/manual/ru/language.operators.bitwise.php
135
             */
136 202
            case Node\Expr\BinaryOp\BitwiseOr::class:
137 12
                return new Expression\Operators\Bitwise\BitwiseOr();
138 190
            case Node\Expr\BinaryOp\BitwiseXor::class:
139 12
                return new Expression\Operators\Bitwise\BitwiseXor();
140 178
            case Node\Expr\BinaryOp\BitwiseAnd::class:
141 12
                return new Expression\Operators\Bitwise\BitwiseAnd();
142 166
            case Node\Expr\BinaryOp\ShiftRight::class:
143 12
                return new Expression\Operators\Bitwise\ShiftRight();
144 154
            case Node\Expr\BinaryOp\ShiftLeft::class:
145 12
                return new Expression\Operators\Bitwise\ShiftLeft();
146 142
            case Node\Expr\BitwiseNot::class:
147 5
                return new Expression\Operators\Bitwise\BitwiseNot();
148
            /**
149
             * Logical
150
             */
151 137
            case Node\Expr\BinaryOp\BooleanOr::class:
152 10
                return new Expression\Operators\Logical\BooleanOr();
153 127
            case Node\Expr\BinaryOp\BooleanAnd::class:
154 5
                return new Expression\Operators\Logical\BooleanAnd();
155 122
            case Node\Expr\BooleanNot::class:
156 5
                return new Expression\Operators\Logical\BooleanNot();
157 117
            case Node\Expr\BinaryOp\LogicalAnd::class:
158 15
                return new Expression\Operators\Logical\LogicalAnd();
159 102
            case Node\Expr\BinaryOp\LogicalOr::class:
160 17
                return new Expression\Operators\Logical\LogicalOr();
161 85
            case Node\Expr\BinaryOp\LogicalXor::class:
162 17
                return new Expression\Operators\Logical\LogicalXor();
163
164
            /**
165
             * Comparison
166
             */
167 68
            case Node\Expr\BinaryOp\Greater::class:
168 12
                return new Expression\Operators\Comparison\Greater();
169 56
            case Node\Expr\BinaryOp\GreaterOrEqual::class:
170 12
                return new Expression\Operators\Comparison\GreaterOrEqual();
171 44
            case Node\Expr\BinaryOp\Smaller::class:
172 12
                return new Expression\Operators\Comparison\Smaller();
173 32
            case Node\Expr\BinaryOp\SmallerOrEqual::class:
174 12
                return new Expression\Operators\Comparison\SmallerOrEqual();
175
176
            /**
177
             * Casts
178
             */
179 20
            case Node\Expr\Cast\Array_::class:
180 1
                return new Expression\Casts\ArrayCast();
181 20
            case Node\Expr\Cast\Bool_::class:
182 1
                return new Expression\Casts\BoolCast();
183 20
            case Node\Expr\Cast\Int_::class:
184 1
                return new Expression\Casts\IntCast();
185 20
            case Node\Expr\Cast\Double::class:
186 1
                return new Expression\Casts\DoubleCast();
187 20
            case Node\Expr\Cast\Object_::class:
188 1
                return new Expression\Casts\ObjectCast();
189 20
            case Node\Expr\Cast\String_::class:
190 1
                return new Expression\Casts\StringCast();
191 20
            case Node\Expr\Cast\Unset_::class:
192 1
                return new Expression\Casts\UnsetCast();
193
194
195
            /**
196
             * Other
197
             */
198 19
            case Node\Expr\Closure::class:
199
                return new Expression\Closure();
200 19
            case Node\Expr\UnaryMinus::class:
201 9
                return new Expression\Operators\UnaryMinus();
202 10
            case Node\Expr\UnaryPlus::class:
203 9
                return new Expression\Operators\UnaryPlus();
204 1
        }
205
206 1
        return false;
207
    }
208
209
    /**
210
     * @param object|string $expr
211
     * @return CompiledExpression
212
     */
213 725
    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...
214
    {
215 725
        if (is_string($expr)) {
216 11
            return new CompiledExpression(CompiledExpression::STRING, $expr);
217
        }
218
219 725
        if (is_null($expr)) {
220 8
            return new CompiledExpression(CompiledExpression::NULL);
221
        }
222
223 725
        if (!is_object($expr)) {
224
            throw new InvalidArgumentException('$expr must be string/object/null');
225
        }
226
227 725
        $this->eventManager->fire(
228 725
            ExpressionBeforeCompile::EVENT_NAME,
229 725
            new ExpressionBeforeCompile(
230 725
                $expr,
231 725
                $this->context
232 725
            )
233 725
        );
234
235 725
        $className = get_class($expr);
236
        switch ($className) {
237 725
            case Node\Arg::class:
238
                /**
239
                 * @todo Better compile
240
                 */
241 2
                return $this->compile($expr->value);
242 725
            case Node\Expr\PropertyFetch::class:
243
                return $this->passPropertyFetch($expr);
244 725
            case Node\Stmt\Property::class:
245
                return $this->passProperty($expr);
246 725
            case Node\Expr\ClassConstFetch::class:
247
                return $this->passConstFetch($expr);
248 725
            case Node\Expr\Assign::class:
249 11
                return $this->passSymbol($expr);
250 725
            case Node\Expr\AssignRef::class:
251 1
                return $this->passSymbolByRef($expr);
252 725
            case Node\Expr\Variable::class:
253 7
                return $this->passExprVariable($expr);
254
255
            /**
256
             * Expressions
257
             */
258 725
            case Node\Expr\Array_::class:
259 23
                return $this->getArray($expr);
260 724
            case Node\Expr\ConstFetch::class:
261 5
                return $this->constFetch($expr);
262 724
            case Node\Name::class:
263 11
                return $this->getNodeName($expr);
264 724
            case Node\Name\FullyQualified::class:
265
                return $this->getFullyQualifiedNodeName($expr);
266
267
            /**
268
             * Simple Scalar(s)
269
             */
270 724
            case \PHPSA\Node\Scalar\Nil::class:
271 21
                return new CompiledExpression(CompiledExpression::NULL);
272 723
            case Node\Scalar\LNumber::class:
273 458
                return new CompiledExpression(CompiledExpression::INTEGER, $expr->value);
274 717
            case Node\Scalar\DNumber::class:
275 197
                return new CompiledExpression(CompiledExpression::DOUBLE, $expr->value);
276 716
            case Node\Scalar\String_::class:
277 26
                return new CompiledExpression(CompiledExpression::STRING, $expr->value);
278 711
            case \PHPSA\Node\Scalar\Boolean::class:
279 214
                return new CompiledExpression(CompiledExpression::BOOLEAN, $expr->value);
280 709
            case \PHPSA\Node\Scalar\Fake::class:
281 69
                return new CompiledExpression($expr->type, $expr->value);
282
        }
283
284 709
        $expressionCompiler = $this->factory($expr);
285 709
        if (!$expressionCompiler) {
286 1
            $this->context->debug("Expression compiler is not implemented for {$className}");
287 1
            return new CompiledExpression(CompiledExpression::UNIMPLEMENTED);
288
        }
289
290 709
        $result = $expressionCompiler->pass($expr, $this->context);
291 709
        if (!$result instanceof CompiledExpression) {
292
            throw new RuntimeException('Please return CompiledExpression from ' . get_class($expressionCompiler));
293
        }
294
295 709
        return $result;
296
    }
297
298
    /**
299
     * @todo Implement
300
     *
301
     * @param Node\Stmt\Property $st
302
     * @return CompiledExpression
303
     */
304
    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...
305
    {
306
        $docBlock = $st->getDocComment();
307
        if (!$docBlock) {
308
            $this->context->notice(
309
                'missing-docblock',
310
                sprintf('Missing docblock for $%s property', $st->props[0]->name),
311
                $st
312
            );
313
314
            return new CompiledExpression();
315
        }
316
317
        $phpdoc = new \phpDocumentor\Reflection\DocBlock($docBlock->getText());
318
319
        $varTags = $phpdoc->getTagsByName('var');
320
        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...
321
            /** @var \phpDocumentor\Reflection\DocBlock\Tag\VarTag $varTag */
322
            $varTag = current($varTags);
323
324
            $typeResolver = new \phpDocumentor\Reflection\TypeResolver();
325
326
            try {
327
                $type = $typeResolver->resolve($varTag->getType());
328
            } catch (\InvalidArgumentException $e) {
329
                return new CompiledExpression();
330
            }
331
332
            if ($type) {
333
                switch (get_class($type)) {
334
                    case \phpDocumentor\Reflection\Types\Object_::class:
335
                        return new CompiledExpression(
336
                            CompiledExpression::OBJECT
337
                        );
338
                    case \phpDocumentor\Reflection\Types\Integer::class:
339
                        return new CompiledExpression(
340
                            CompiledExpression::INTEGER
341
                        );
342
                    case \phpDocumentor\Reflection\Types\String_::class:
343
                        return new CompiledExpression(
344
                            CompiledExpression::STRING
345
                        );
346
                    case \phpDocumentor\Reflection\Types\Float_::class:
347
                        return new CompiledExpression(
348
                            CompiledExpression::DOUBLE
349
                        );
350
                    case \phpDocumentor\Reflection\Types\Null_::class:
351
                        return new CompiledExpression(
352
                            CompiledExpression::NULL
353
                        );
354
                    case \phpDocumentor\Reflection\Types\Boolean::class:
355
                        return new CompiledExpression(
356
                            CompiledExpression::BOOLEAN
357
                        );
358
                }
359
            }
360
        }
361
362
        return new CompiledExpression();
363
    }
364
365
    /**
366
     * @param Node\Expr\Variable $expr
367
     * @return CompiledExpression
368
     */
369 1
    public function declareVariable(Node\Expr\Variable $expr)
370
    {
371 1
        $variable = $this->context->getSymbol($expr->name);
372 1
        if (!$variable) {
373
            $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...
374
            $this->context->addVariable($variable);
375
        }
376
377 1
        return new CompiledExpression($variable->getType(), $variable->getValue(), $variable);
378
    }
379
380
    /**
381
     * @param Node\Name\FullyQualified $expr
382
     * @return CompiledExpression
383
     */
384
    public function getFullyQualifiedNodeName(Node\Name\FullyQualified $expr)
385
    {
386
        $this->context->debug('Unimplemented FullyQualified', $expr);
387
388
        return new CompiledExpression;
389
    }
390
391
    /**
392
     * @param Node\Name $expr
393
     * @return CompiledExpression
394
     */
395 11
    public function getNodeName(Node\Name $expr)
396
    {
397 11
        $nodeString = $expr->toString();
398 11
        if ($nodeString === 'null') {
399 1
            return new CompiledExpression(CompiledExpression::NULL);
400
        }
401
402 10
        if (in_array($nodeString, ['parent'], true)) {
403
            /** @var ClassDefinition $scope */
404
            $scope = $this->context->scope;
405
            assert($scope instanceof ClassDefinition);
406
407
            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...
408
                $definition = $scope->getExtendsClassDefinition();
409
                if ($definition) {
410
                    return new CompiledExpression(CompiledExpression::OBJECT, $definition);
411
                }
412
            } else {
413
                $this->context->notice(
414
                    'no-parent',
415
                    'Cannot access parent:: when current class scope has no parent',
416
                    $expr
417
                );
418
            }
419
        }
420
421 10
        if (in_array($nodeString, ['self', 'static'], true)) {
422
            return CompiledExpression::fromZvalValue($this->context->scope);
423
        }
424
425 10
        if (defined($nodeString)) {
426 1
            return CompiledExpression::fromZvalValue(constant($expr));
427
        }
428
429 10
        return new CompiledExpression(CompiledExpression::STRING, $expr->toString());
430
    }
431
432
    /**
433
     * @param Node\Expr\PropertyFetch $expr
434
     * @return CompiledExpression
435
     */
436
    protected function passPropertyFetch(Node\Expr\PropertyFetch $expr)
437
    {
438
        $propertNameCE = $this->compile($expr->name);
439
440
        $scopeExpression = $this->compile($expr->var);
441
        if ($scopeExpression->isObject()) {
442
            $scopeExpressionValue = $scopeExpression->getValue();
443
            if ($scopeExpressionValue instanceof ClassDefinition) {
444
                $propertyName = $propertNameCE->isString() ? $propertNameCE->getValue() : false;
445
                if ($propertyName) {
446
                    if ($scopeExpressionValue->hasProperty($propertyName, true)) {
447
                        $property = $scopeExpressionValue->getProperty($propertyName, true);
448
                        return $this->compile($property);
449
                    } else {
450
                        $this->context->notice(
451
                            'undefined-property',
452
                            sprintf(
453
                                'Property %s does not exist in %s scope',
454
                                $propertyName,
455
                                $scopeExpressionValue->getName()
456
                            ),
457
                            $expr
458
                        );
459
                    }
460
                }
461
            }
462
463
            return new CompiledExpression(CompiledExpression::UNKNOWN);
464
        } elseif (!$scopeExpression->canBeObject()) {
465
            return new CompiledExpression(CompiledExpression::UNKNOWN);
466
        }
467
468
        $this->context->notice(
469
            'property-fetch-on-non-object',
470
            "It's not possible to fetch property on not object",
471
            $expr,
472
            Check::CHECK_BETA
473
        );
474
475
        return new CompiledExpression(CompiledExpression::UNKNOWN);
476
    }
477
478
    /**
479
     * @param Node\Expr\ClassConstFetch $expr
480
     * @return CompiledExpression
481
     */
482
    protected function passConstFetch(Node\Expr\ClassConstFetch $expr)
483
    {
484
        $leftCE = $this->compile($expr->class);
485
        if ($leftCE->isObject()) {
486
            $leftCEValue = $leftCE->getValue();
487
            if ($leftCEValue instanceof ClassDefinition) {
488
                if (!$leftCEValue->hasConst($expr->name, true)) {
489
                    $this->context->notice(
490
                        'undefined-const',
491
                        sprintf('Constant %s does not exist in %s scope', $expr->name, $expr->class),
492
                        $expr
493
                    );
494
                    return new CompiledExpression(CompiledExpression::UNKNOWN);
495
                }
496
497
                return new CompiledExpression();
498
            }
499
        }
500
501
        $this->context->debug('Unknown const fetch', $expr);
502
        return new CompiledExpression();
503
    }
504
505
    /**
506
     * @param Node\Expr\Assign $expr
507
     * @return CompiledExpression
508
     */
509 11
    protected function passSymbol(Node\Expr\Assign $expr)
0 ignored issues
show
Complexity introduced by
This operation has 360 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...
510
    {
511 11
        $compiledExpression = $this->compile($expr->expr);
512
513 11
        if ($expr->var instanceof Node\Expr\List_) {
514
            $isCorrectType = false;
515
516
            switch ($compiledExpression->getType()) {
517
                case CompiledExpression::ARR:
518
                    $isCorrectType = true;
519
                    break;
520
            }
521
522
            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...
523
                foreach ($expr->var->vars as $key => $var) {
524
                    if ($var instanceof Node\Expr\Variable) {
525
                        if (!isset($expr->var->name)) {
526
                            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...
527
                        }
528
                        $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...
529
530
                        $symbol = $this->context->getSymbol($name);
531
                        if (!$symbol) {
532
                            $symbol = new Variable(
533
                                $name,
534
                                null,
535
                                CompiledExpression::UNKNOWN,
536
                                $this->context->getCurrentBranch()
537
                            );
538
                            $this->context->addVariable($symbol);
539
                        }
540
541
                        if (!$isCorrectType) {
542
                            $symbol->modify(CompiledExpression::NULL, null);
543
                        }
544
545
                        $symbol->incSets();
546
                    }
547
                }
548
            }
549
550
            return new CompiledExpression();
551
        }
552
553
554 11
        if ($expr->var instanceof Node\Expr\Variable) {
555 11
            $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...
556 11
            switch ($compiledExpressionName->getType()) {
557 11
                case CompiledExpression::STRING:
558 11
                    break;
559
                default:
560
                    $this->context->debug('Unexpected type of Variable name after compile');
561
                    return new CompiledExpression();
562 11
            }
563
564 11
            $symbol = $this->context->getSymbol($compiledExpressionName->getValue());
565 11
            if ($symbol) {
566 2
                $symbol->modify($compiledExpression->getType(), $compiledExpression->getValue());
567 2
                $this->context->modifyReferencedVariables(
568 2
                    $symbol,
569 2
                    $compiledExpression->getType(),
570 2
                    $compiledExpression->getValue()
571 2
                );
572 2
            } else {
573 11
                $symbol = new Variable(
574 11
                    $compiledExpressionName->getValue(),
575 11
                    $compiledExpression->getValue(),
576 11
                    $compiledExpression->getType(),
577 11
                    $this->context->getCurrentBranch()
578 11
                );
579 11
                $this->context->addVariable($symbol);
580
            }
581
582 11
            $symbol->incSets();
583 11
            return $compiledExpression;
584
        }
585
586
        if ($expr->var instanceof Node\Expr\PropertyFetch) {
587
            $compiledExpression = $this->compile($expr->var->var);
588
            if ($compiledExpression->getType() == CompiledExpression::OBJECT) {
589
                $objectDefinition = $compiledExpression->getValue();
590
                if ($objectDefinition instanceof ClassDefinition) {
591
                    if (is_string($expr->var->name)) {
592
                        if ($objectDefinition->hasProperty($expr->var->name)) {
593
                            return $this->compile($objectDefinition->getProperty($expr->var->name));
594
                        }
595
                    }
596
                }
597
            }
598
        }
599
600
        $this->context->debug('Unknown how to pass symbol');
601
        return new CompiledExpression();
602
    }
603
604
    /**
605
     * @param Node\Expr\AssignRef $expr
606
     * @return CompiledExpression
607
     */
608 1
    protected function passSymbolByRef(Node\Expr\AssignRef $expr)
609
    {
610 1
        if ($expr->var instanceof Node\Expr\Variable) {
611 1
            $name = $expr->var->name;
612
613 1
            $compiledExpression = $this->compile($expr->expr);
614
615 1
            $symbol = $this->context->getSymbol($name);
616 1
            if ($symbol) {
617
                $symbol->modify($compiledExpression->getType(), $compiledExpression->getValue());
618
            } else {
619 1
                $symbol = new Variable(
620 1
                    $name,
0 ignored issues
show
Bug introduced by
It seems like $name defined by $expr->var->name on line 611 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...
621 1
                    $compiledExpression->getValue(),
622 1
                    $compiledExpression->getType(),
623 1
                    $this->context->getCurrentBranch()
624 1
                );
625 1
                $this->context->addVariable($symbol);
626
            }
627
628 1
            if ($expr->expr instanceof Node\Expr\Variable) {
629 1
                $rightVarName = $expr->expr->name;
630
631 1
                $rightSymbol = $this->context->getSymbol($rightVarName);
632 1
                if ($rightSymbol) {
633 1
                    $rightSymbol->incUse();
634 1
                    $symbol->setReferencedTo($rightSymbol);
635 1
                } else {
636
                    $this->context->debug('Cannot fetch variable by name: ' . $rightVarName);
637
                }
638 1
            }
639
640 1
            $symbol->incSets();
641 1
            return $compiledExpression;
642
        }
643
644
        $this->context->debug('Unknown how to pass symbol by ref');
645
        return new CompiledExpression();
646
    }
647
648
    /**
649
     * @param Node\Expr\Variable $expr
650
     * @return CompiledExpression
651
     */
652 7
    protected function passExprVariable(Node\Expr\Variable $expr)
653
    {
654 7
        $variable = $this->context->getSymbol($expr->name);
655 7
        if ($variable) {
656 7
            $variable->incGets();
657 7
            return new CompiledExpression($variable->getType(), $variable->getValue(), $variable);
658
        }
659
660
        $this->context->notice(
661
            'undefined-variable',
662
            sprintf('You trying to use undefined variable $%s', $expr->name),
663
            $expr
664
        );
665
666
        return new CompiledExpression();
667
    }
668
669
    /**
670
     * Compile Array_ expression to CompiledExpression
671
     *
672
     * @param Node\Expr\Array_ $expr
673
     * @return CompiledExpression
674
     */
675 23
    protected function getArray(Node\Expr\Array_ $expr)
676
    {
677 23
        if ($expr->items === []) {
678 14
            return new CompiledExpression(CompiledExpression::ARR, []);
679
        }
680
681 9
        $resultArray = [];
682
683 9
        foreach ($expr->items as $item) {
684 9
            $compiledValueResult = $this->compile($item->value);
685 9
            if ($item->key) {
686 2
                $compiledKeyResult = $this->compile($item->key);
687 2
                switch ($compiledKeyResult->getType()) {
688 2
                    case CompiledExpression::INTEGER:
689 2
                    case CompiledExpression::DOUBLE:
690 2
                    case CompiledExpression::BOOLEAN:
691 2
                    case CompiledExpression::NULL:
692 2
                    case CompiledExpression::STRING:
693 2
                        $resultArray[$compiledKeyResult->getValue()] = $compiledValueResult->getValue();
694 2
                        break;
695
                    default:
696
                        $this->context->debug("Type {$compiledKeyResult->getType()} is not supported for key value");
697
                        return new CompiledExpression(CompiledExpression::ARR);
698
                        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...
699 2
                }
700 2
            } else {
701 7
                $resultArray[] = $compiledValueResult->getValue();
702
            }
703 9
        }
704
705 9
        return new CompiledExpression(CompiledExpression::ARR, $resultArray);
706
    }
707
708
    /**
709
     * Convert const fetch expr to CompiledExpression
710
     *
711
     * @param Node\Expr\ConstFetch $expr
712
     * @return CompiledExpression
713
     */
714 5
    protected function constFetch(Node\Expr\ConstFetch $expr)
715
    {
716 5
        if ($expr->name instanceof Node\Name) {
717 5
            if ($expr->name->parts[0] === 'true') {
718 4
                return new CompiledExpression(CompiledExpression::BOOLEAN, true);
719
            }
720
721 2
            if ($expr->name->parts[0] === 'false') {
722
                return new CompiledExpression(CompiledExpression::BOOLEAN, false);
723
            }
724 2
        }
725
726
        /**
727
         * @todo Implement check
728
         */
729 2
        return $this->compile($expr->name);
730
    }
731
}
732