Completed
Push — master ( c4d2d4...eae51e )
by Alexander
9s
created

src/ValueResolver/NodeExpressionResolver.php (1 issue)

Check for unnecessary variable assignments.

Unused Code Major

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Parser Reflection API
4
 *
5
 * @copyright Copyright 2015, Lisachenko Alexander <[email protected]>
6
 *
7
 * This source file is subject to the license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace Go\ParserReflection\ValueResolver;
12
13
use Go\ParserReflection\ReflectionClass;
14
use Go\ParserReflection\ReflectionException;
15
use Go\ParserReflection\ReflectionFileNamespace;
16
use PhpParser\Node;
17
use PhpParser\Node\Expr;
18
use PhpParser\Node\Scalar;
19
use PhpParser\Node\Scalar\MagicConst;
20
21
/**
22
 * Tries to resolve expression into value
23
 */
24
class NodeExpressionResolver
25
{
26
27
    /**
28
     * List of exception for constant fetch
29
     *
30
     * @var array
31
     */
32
    private static $notConstants = [
33
        'true'  => true,
34
        'false' => true,
35
        'null'  => true,
36
    ];
37
38
    /**
39
     * Name of the constant (if present)
40
     *
41
     * @var null|string
42
     */
43
    private $constantName = null;
44
45
    /**
46
     * Current reflection context for parsing
47
     *
48
     * @var mixed|\Go\ParserReflection\ReflectionClass
49
     */
50
    private $context;
51
52
    /**
53
     * Flag if expression is constant
54
     *
55
     * @var bool
56
     */
57
    private $isConstant = false;
58
59
    /**
60
     * Node resolving level, 1 = top-level
61
     *
62
     * @var int
63
     */
64
    private $nodeLevel = 0;
65
66
    /**
67
     * @var mixed Value of expression/constant
68
     */
69
    private $value;
70
71 69
    public function __construct($context)
72
    {
73 69
        $this->context = $context;
74 69
    }
75
76 19
    public function getConstantName()
77
    {
78 19
        return $this->constantName;
79
    }
80
81 46
    public function getValue()
82
    {
83 46
        return $this->value;
84
    }
85
86 19
    public function isConstant()
87
    {
88 19
        return $this->isConstant;
89
    }
90
91
    /**
92
     * {@inheritDoc}
93
     */
94 46
    public function process(Node $node)
95
    {
96 46
        $this->nodeLevel    = 0;
97 46
        $this->isConstant   = false;
98 46
        $this->constantName = null;
99 46
        $this->value        = $this->resolve($node);
100 46
    }
101
102
    /**
103
     * Resolves node into valid value
104
     *
105
     * @param Node $node
106
     *
107
     * @return mixed
108
     */
109 46
    protected function resolve(Node $node)
110
    {
111 46
        $value = null;
112
        try {
113 46
            ++$this->nodeLevel;
114
115 46
            $nodeType   = $node->getType();
116 46
            $methodName = 'resolve' . str_replace('_', '', $nodeType);
117 46
            if (method_exists($this, $methodName)) {
118 46
                $value = $this->$methodName($node);
119
            }
120 46
        } finally {
121 46
            --$this->nodeLevel;
122
        }
123
124 46
        return $value;
125
    }
126
127 9
    protected function resolveScalarDNumber(Scalar\DNumber $node)
128
    {
129 9
        return $node->value;
130
    }
131
132 27
    protected function resolveScalarLNumber(Scalar\LNumber $node)
133
    {
134 27
        return $node->value;
135
    }
136
137 22
    protected function resolveScalarString(Scalar\String_ $node)
138
    {
139 22
        return $node->value;
140
    }
141
142
    protected function resolveScalarMagicConstMethod()
143
    {
144
        if ($this->context instanceof \ReflectionMethod) {
145
            $fullName = $this->context->getDeclaringClass()->getName() . '::' . $this->context->getShortName();
146
147
            return $fullName;
148
        }
149
150
        return '';
151
    }
152
153 1
    protected function resolveScalarMagicConstFunction()
154
    {
155 1
        if ($this->context instanceof \ReflectionFunctionAbstract) {
156 1
            return $this->context->getName();
157
        }
158
159
        return '';
160
    }
161
162 28
    protected function resolveScalarMagicConstNamespace()
163
    {
164 28
        if (method_exists($this->context, 'getNamespaceName')) {
165 24
            return $this->context->getNamespaceName();
166
        }
167
168 4
        if ($this->context instanceof ReflectionFileNamespace) {
169 4
            return $this->context->getName();
170
        }
171
172
        return '';
173
    }
174
175 10
    protected function resolveScalarMagicConstClass()
176
    {
177 10
        if ($this->context instanceof \ReflectionClass) {
178 4
            return $this->context->getName();
179
        }
180 6
        if (method_exists($this->context, 'getDeclaringClass')) {
181
            $declaringClass = $this->context->getDeclaringClass();
182
            if ($declaringClass instanceof \ReflectionClass) {
183
                return $declaringClass->getName();
184
            }
185
        }
186
187 6
        return '';
188
    }
189
190 3
    protected function resolveScalarMagicConstDir()
191
    {
192 3
        if (method_exists($this->context, 'getFileName')) {
193 3
            return dirname($this->context->getFileName());
194
        }
195
196
        return '';
197
    }
198
199 6
    protected function resolveScalarMagicConstFile()
200
    {
201 6
        if (method_exists($this->context, 'getFileName')) {
202 6
            return $this->context->getFileName();
203
        }
204
205
        return '';
206
    }
207
208 6
    protected function resolveScalarMagicConstLine(MagicConst\Line $node)
209
    {
210 6
        return $node->hasAttribute('startLine') ? $node->getAttribute('startLine') : 0;
211
    }
212
213 2
    protected function resolveScalarMagicConstTrait()
214
    {
215 2
        if ($this->context instanceof \ReflectionClass && $this->context->isTrait()) {
216 2
            return $this->context->getName();
217
        }
218
219
        return '';
220
    }
221
222 25
    protected function resolveExprConstFetch(Expr\ConstFetch $node)
223
    {
224 25
        $constantValue = null;
225 25
        $isResolved    = false;
226
227
        /** @var ReflectionFileNamespace|null $fileNamespace */
228 25
        $fileNamespace = null;
0 ignored issues
show
$fileNamespace is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
229 25
        $isFQNConstant = $node->name instanceof Node\Name\FullyQualified;
230 25
        $constantName  = $node->name->toString();
231
232 25
        if (!$isFQNConstant) {
233 22
            if (method_exists($this->context, 'getFileName')) {
234 22
                $fileName      = $this->context->getFileName();
235 22
                $namespaceName = $this->resolveScalarMagicConstNamespace();
236 22
                $fileNamespace = new ReflectionFileNamespace($fileName, $namespaceName);
237 22
                if ($fileNamespace->hasConstant($constantName)) {
238 12
                    $constantValue = $fileNamespace->getConstant($constantName);
239 12
                    $constantName  = $fileNamespace->getName() . '\\' . $constantName;
240 12
                    $isResolved    = true;
241
                }
242
            }
243
        }
244
245 25
        if (!$isResolved && defined($constantName)) {
246 23
            $constantValue = constant($constantName);
247
        }
248
249 25
        if ($this->nodeLevel === 1 && !isset(self::$notConstants[$constantName])) {
250 15
            $this->isConstant   = true;
251 15
            $this->constantName = $constantName;
252
        }
253
254 25
        return $constantValue;
255
    }
256
257 11
    protected function resolveExprClassConstFetch(Expr\ClassConstFetch $node)
258
    {
259 11
        $refClass     = $this->fetchReflectionClass($node->class);
260 11
        $constantName = $node->name;
261
262
        // special handling of ::class constants
263 11
        if ('class' === $constantName) {
264 3
            return $refClass->getName();
265
        }
266
267 9
        $this->isConstant = true;
268 9
        $this->constantName = (string)$node->class . '::' . $constantName;
269
270 9
        return $refClass->getConstant($constantName);
271
    }
272
273 9
    protected function resolveExprArray(Expr\Array_ $node)
274
    {
275 9
        $result = [];
276 9
        foreach ($node->items as $itemIndex => $arrayItem) {
277 8
            $itemValue = $this->resolve($arrayItem->value);
278 8
            $itemKey   = isset($arrayItem->key) ? $this->resolve($arrayItem->key) : $itemIndex;
279 8
            $result[$itemKey] = $itemValue;
280
        }
281
282 9
        return $result;
283
    }
284
285 1
    protected function resolveExprBinaryOpPlus(Expr\BinaryOp\Plus $node)
286
    {
287 1
        return $this->resolve($node->left) + $this->resolve($node->right);
288
    }
289
290 1
    protected function resolveExprBinaryOpMinus(Expr\BinaryOp\Minus $node)
291
    {
292 1
        return $this->resolve($node->left) - $this->resolve($node->right);
293
    }
294
295 2
    protected function resolveExprBinaryOpMul(Expr\BinaryOp\Mul $node)
296
    {
297 2
        return $this->resolve($node->left) * $this->resolve($node->right);
298
    }
299
300 1
    protected function resolveExprBinaryOpPow(Expr\BinaryOp\Pow $node)
301
    {
302 1
        return pow($this->resolve($node->left), $this->resolve($node->right));
303
    }
304
305 1
    protected function resolveExprBinaryOpDiv(Expr\BinaryOp\Div $node)
306
    {
307 1
        return $this->resolve($node->left) / $this->resolve($node->right);
308
    }
309
310 1
    protected function resolveExprBinaryOpMod(Expr\BinaryOp\Mod $node)
311
    {
312 1
        return $this->resolve($node->left) % $this->resolve($node->right);
313
    }
314
315 1
    protected function resolveExprBooleanNot(Expr\BooleanNot $node)
316
    {
317 1
        return !$this->resolve($node->expr);
318
    }
319
320 1
    protected function resolveExprBitwiseNot(Expr\BitwiseNot $node)
321
    {
322 1
        return ~$this->resolve($node->expr);
323
    }
324
325 1
    protected function resolveExprBinaryOpBitwiseOr(Expr\BinaryOp\BitwiseOr $node)
326
    {
327 1
        return $this->resolve($node->left) | $this->resolve($node->right);
328
    }
329
330 1
    protected function resolveExprBinaryOpBitwiseAnd(Expr\BinaryOp\BitwiseAnd $node)
331
    {
332 1
        return $this->resolve($node->left) & $this->resolve($node->right);
333
    }
334
335 1
    protected function resolveExprBinaryOpBitwiseXor(Expr\BinaryOp\BitwiseXor $node)
336
    {
337 1
        return $this->resolve($node->left) ^ $this->resolve($node->right);
338
    }
339
340 1
    protected function resolveExprBinaryOpShiftLeft(Expr\BinaryOp\ShiftLeft $node)
341
    {
342 1
        return $this->resolve($node->left) << $this->resolve($node->right);
343
    }
344
345 1
    protected function resolveExprBinaryOpShiftRight(Expr\BinaryOp\ShiftRight $node)
346
    {
347 1
        return $this->resolve($node->left) >> $this->resolve($node->right);
348
    }
349
350 2
    protected function resolveExprBinaryOpConcat(Expr\BinaryOp\Concat $node)
351
    {
352 2
        return $this->resolve($node->left) . $this->resolve($node->right);
353
    }
354
355 1
    protected function resolveExprTernary(Expr\Ternary $node)
356
    {
357 1
        if (isset($node->if)) {
358
            // Full syntax $a ? $b : $c;
359
360
            return $this->resolve($node->cond) ? $this->resolve($node->if) : $this->resolve($node->else);
361
        } else {
362
            // Short syntax $a ?: $c;
363
364 1
            return $this->resolve($node->cond) ?: $this->resolve($node->else);
365
        }
366
    }
367
368 1
    protected function resolveExprBinaryOpSmallerOrEqual(Expr\BinaryOp\SmallerOrEqual $node)
369
    {
370 1
        return $this->resolve($node->left) <= $this->resolve($node->right);
371
    }
372
373 1
    protected function resolveExprBinaryOpGreaterOrEqual(Expr\BinaryOp\GreaterOrEqual $node)
374
    {
375 1
        return $this->resolve($node->left) >= $this->resolve($node->right);
376
    }
377
378 1
    protected function resolveExprBinaryOpEqual(Expr\BinaryOp\Equal $node)
379
    {
380 1
        return $this->resolve($node->left) == $this->resolve($node->right);
381
    }
382
383 1
    protected function resolveExprBinaryOpNotEqual(Expr\BinaryOp\NotEqual $node)
384
    {
385 1
        return $this->resolve($node->left) != $this->resolve($node->right);
386
    }
387
388 1
    protected function resolveExprBinaryOpSmaller(Expr\BinaryOp\Smaller $node)
389
    {
390 1
        return $this->resolve($node->left) < $this->resolve($node->right);
391
    }
392
393 2
    protected function resolveExprBinaryOpGreater(Expr\BinaryOp\Greater $node)
394
    {
395 2
        return $this->resolve($node->left) > $this->resolve($node->right);
396
    }
397
398 1
    protected function resolveExprBinaryOpIdentical(Expr\BinaryOp\Identical $node)
399
    {
400 1
        return $this->resolve($node->left) === $this->resolve($node->right);
401
    }
402
403 1
    protected function resolveExprBinaryOpNotIdentical(Expr\BinaryOp\NotIdentical $node)
404
    {
405 1
        return $this->resolve($node->left) !== $this->resolve($node->right);
406
    }
407
408 1
    protected function resolveExprBinaryOpBooleanAnd(Expr\BinaryOp\BooleanAnd $node)
409
    {
410 1
        return $this->resolve($node->left) && $this->resolve($node->right);
411
    }
412
413 1
    protected function resolveExprBinaryOpLogicalAnd(Expr\BinaryOp\LogicalAnd $node)
414
    {
415 1
        return $this->resolve($node->left) and $this->resolve($node->right);
416
    }
417
418 1
    protected function resolveExprBinaryOpBooleanOr(Expr\BinaryOp\BooleanOr $node)
419
    {
420 1
        return $this->resolve($node->left) || $this->resolve($node->right);
421
    }
422
423 1
    protected function resolveExprBinaryOpLogicalOr(Expr\BinaryOp\LogicalOr $node)
424
    {
425 1
        return $this->resolve($node->left) or $this->resolve($node->right);
426
    }
427
428 1
    protected function resolveExprBinaryOpLogicalXor(Expr\BinaryOp\LogicalXor $node)
429
    {
430 1
        return $this->resolve($node->left) xor $this->resolve($node->right);
431
    }
432
433
    /**
434
     * Utility method to fetch reflection class instance by name
435
     *
436
     * Supports:
437
     *   'self' keyword
438
     *   'parent' keyword
439
     *    not-FQN class names
440
     *
441
     * @param Node\Name $node Class name node
442
     *
443
     * @return bool|\ReflectionClass
444
     *
445
     * @throws ReflectionException
446
     */
447 11
    private function fetchReflectionClass(Node\Name $node)
448
    {
449 11
        $className  = $node->toString();
450 11
        $isFQNClass = $node instanceof Node\Name\FullyQualified;
451 11
        if ($isFQNClass) {
452
            // check to see if the class is already loaded and is safe to use
453
            // PHP's ReflectionClass to determine if the class is user defined
454 5
            if (class_exists($className, false)) {
455 5
                $refClass = new \ReflectionClass($className);
456 5
                if (!$refClass->isUserDefined()) {
457 2
                    return $refClass;
458
                }
459
            }
460 4
            return new ReflectionClass($className);
461
        }
462
463 8
        if ('self' === $className) {
464 8
            if ($this->context instanceof \ReflectionClass) {
465 8
                return $this->context;
466
            } elseif (method_exists($this->context, 'getDeclaringClass')) {
467
                return $this->context->getDeclaringClass();
468
            }
469
        }
470
471 1
        if ('parent' === $className) {
472 1
            if ($this->context instanceof \ReflectionClass) {
473 1
                return $this->context->getParentClass();
474
            } elseif (method_exists($this->context, 'getDeclaringClass')) {
475
                return $this->context->getDeclaringClass()->getParentClass();
476
            }
477
        }
478
479
        if (method_exists($this->context, 'getFileName')) {
480
            /** @var ReflectionFileNamespace|null $fileNamespace */
481
            $fileName      = $this->context->getFileName();
482
            $namespaceName = $this->resolveScalarMagicConstNamespace();
483
484
            $fileNamespace = new ReflectionFileNamespace($fileName, $namespaceName);
485
            return $fileNamespace->getClass($className);
486
        }
487
488
        throw new ReflectionException("Can not resolve class $className");
489
    }
490
}
491