resolveExprBinaryOpBooleanAnd()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
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
use PhpParser\Node\Stmt\Expression;
21
22
/**
23
 * Tries to resolve expression into value
24
 */
25
class NodeExpressionResolver
26
{
27
28
    /**
29
     * List of exception for constant fetch
30
     *
31
     * @var array
32
     */
33
    private static $notConstants = [
34
        'true'  => true,
35
        'false' => true,
36
        'null'  => true,
37
    ];
38
39
    /**
40
     * Name of the constant (if present)
41
     *
42
     * @var null|string
43
     */
44
    private $constantName = null;
45
46
    /**
47
     * Current reflection context for parsing
48
     *
49
     * @var mixed|\Go\ParserReflection\ReflectionClass
50
     */
51
    private $context;
52
53
    /**
54
     * Flag if expression is constant
55
     *
56
     * @var bool
57
     */
58
    private $isConstant = false;
59
60
    /**
61
     * Node resolving level, 1 = top-level
62
     *
63
     * @var int
64
     */
65
    private $nodeLevel = 0;
66
67
    /**
68
     * @var mixed Value of expression/constant
69
     */
70
    private $value;
71
72 74
    public function __construct($context)
73
    {
74 74
        $this->context = $context;
75 74
    }
76
77 21
    public function getConstantName()
78
    {
79 21
        return $this->constantName;
80
    }
81
82 49
    public function getValue()
83
    {
84 49
        return $this->value;
85
    }
86
87 21
    public function isConstant()
88
    {
89 21
        return $this->isConstant;
90
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95 51
    public function process(Node $node)
96
    {
97
        // Unwrap "expr;" statements.
98 51
        if ($node instanceof Expression) {
99 3
            $node = $node->expr;
100
        }
101
102 51
        $this->nodeLevel    = 0;
103 51
        $this->isConstant   = false;
104 51
        $this->constantName = null;
105 51
        $this->value        = $this->resolve($node);
106 49
    }
107
108
    /**
109
     * Resolves node into valid value
110
     *
111
     * @param Node $node
112
     *
113
     * @return mixed
114
     */
115 51
    protected function resolve(Node $node)
116
    {
117 51
        $value = null;
118
        try {
119 51
            ++$this->nodeLevel;
120
121 51
            $methodName = $this->getDispatchMethodFor($node);
122 51
            if (method_exists($this, $methodName)) {
123 51
                $value = $this->$methodName($node);
124
            }
125 51
        } finally {
126 51
            --$this->nodeLevel;
127
        }
128
129 51
        return $value;
130
    }
131
132 9
    protected function resolveScalarDNumber(Scalar\DNumber $node)
133
    {
134 9
        return $node->value;
135
    }
136
137 27
    protected function resolveScalarLNumber(Scalar\LNumber $node)
138
    {
139 27
        return $node->value;
140
    }
141
142 24
    protected function resolveScalarString(Scalar\String_ $node)
143
    {
144 24
        return $node->value;
145
    }
146
147
    protected function resolveScalarMagicConstMethod()
148
    {
149
        if ($this->context instanceof \ReflectionMethod) {
150
            $fullName = $this->context->getDeclaringClass()->name . '::' . $this->context->getShortName();
151
152
            return $fullName;
153
        }
154
155
        return '';
156
    }
157
158 1
    protected function resolveScalarMagicConstFunction()
159
    {
160 1
        if ($this->context instanceof \ReflectionFunctionAbstract) {
161 1
            return $this->context->getName();
162
        }
163
164
        return '';
165
    }
166
167 29
    protected function resolveScalarMagicConstNamespace()
168
    {
169 29
        if (method_exists($this->context, 'getNamespaceName')) {
170 26
            return $this->context->getNamespaceName();
171
        }
172
173 3
        if ($this->context instanceof ReflectionFileNamespace) {
174 3
            return $this->context->getName();
175
        }
176
177
        return '';
178
    }
179
180 11
    protected function resolveScalarMagicConstClass()
181
    {
182 11
        if ($this->context instanceof \ReflectionClass) {
183 5
            return $this->context->name;
184
        }
185 6
        if (method_exists($this->context, 'getDeclaringClass')) {
186
            $declaringClass = $this->context->getDeclaringClass();
187
            if ($declaringClass instanceof \ReflectionClass) {
188
                return $declaringClass->name;
189
            }
190
        }
191
192 6
        return '';
193
    }
194
195 4
    protected function resolveScalarMagicConstDir()
196
    {
197 4
        if (method_exists($this->context, 'getFileName')) {
198 4
            return dirname($this->context->getFileName());
199
        }
200
201
        return '';
202
    }
203
204 7
    protected function resolveScalarMagicConstFile()
205
    {
206 7
        if (method_exists($this->context, 'getFileName')) {
207 7
            return $this->context->getFileName();
208
        }
209
210
        return '';
211
    }
212
213 7
    protected function resolveScalarMagicConstLine(MagicConst\Line $node)
214
    {
215 7
        return $node->hasAttribute('startLine') ? $node->getAttribute('startLine') : 0;
216
    }
217
218 2
    protected function resolveScalarMagicConstTrait()
219
    {
220 2
        if ($this->context instanceof \ReflectionClass && $this->context->isTrait()) {
221 2
            return $this->context->name;
222
        }
223
224
        return '';
225
    }
226
227 27
    protected function resolveExprConstFetch(Expr\ConstFetch $node)
228
    {
229 27
        $constantValue = null;
230 27
        $isResolved    = false;
231
232 27
        $isFQNConstant = $node->name instanceof Node\Name\FullyQualified;
233 27
        $constantName  = $node->name->toString();
234
235 27
        if (!$isFQNConstant) {
236 23
            if (method_exists($this->context, 'getFileName')) {
237 23
                $fileName      = $this->context->getFileName();
238 23
                $namespaceName = $this->resolveScalarMagicConstNamespace();
239 23
                $fileNamespace = new ReflectionFileNamespace($fileName, $namespaceName);
240 23
                if ($fileNamespace->hasConstant($constantName)) {
241 13
                    $constantValue = $fileNamespace->getConstant($constantName);
242 13
                    $constantName  = $fileNamespace->getName() . '\\' . $constantName;
243 13
                    $isResolved    = true;
244
                }
245
            }
246
        }
247
248 27
        if (!$isResolved && defined($constantName)) {
249 25
            $constantValue = constant($constantName);
250
        }
251
252 27
        if ($this->nodeLevel === 1 && !isset(self::$notConstants[$constantName])) {
253 16
            $this->isConstant   = true;
254 16
            $this->constantName = $constantName;
255
        }
256
257 27
        return $constantValue;
258
    }
259
260 15
    protected function resolveExprClassConstFetch(Expr\ClassConstFetch $node)
261
    {
262 15
        $classToReflect = $node->class;
263 15
        if (!($classToReflect instanceof Node\Name)) {
264 3
            $classToReflect = $this->resolve($classToReflect) ?: $classToReflect;
265 3
            if (!is_string($classToReflect)) {
266 2
                $reason = 'Unable';
267 2
                if ($classToReflect instanceof Expr) {
268 1
                    $methodName = $this->getDispatchMethodFor($classToReflect);
269 1
                    $reason = "Method " . __CLASS__ . "::{$methodName}() not found trying";
270
                }
271 2
                throw new ReflectionException("$reason to resolve class constant.");
272
            }
273
            // Strings evaluated as class names are always treated as fully
274
            // qualified.
275 1
            $classToReflect = new Node\Name\FullyQualified(ltrim($classToReflect, '\\'));
276
        }
277 13
        $refClass = $this->fetchReflectionClass($classToReflect);
278 13
        $constantName = ($node->name instanceof Expr\Error) ? '' : $node->name->toString();
279
280
        // special handling of ::class constants
281 13
        if ('class' === $constantName) {
282 3
            return $refClass->getName();
283
        }
284
285 11
        $this->isConstant = true;
286 11
        $this->constantName = (string)$classToReflect . '::' . $constantName;
287
288 11
        return $refClass->getConstant($constantName);
289
    }
290
291 9
    protected function resolveExprArray(Expr\Array_ $node)
292
    {
293 9
        $result = [];
294 9
        foreach ($node->items as $itemIndex => $arrayItem) {
295 8
            $itemValue = $this->resolve($arrayItem->value);
296 8
            $itemKey   = isset($arrayItem->key) ? $this->resolve($arrayItem->key) : $itemIndex;
297 8
            $result[$itemKey] = $itemValue;
298
        }
299
300 9
        return $result;
301
    }
302
303 1
    protected function resolveExprBinaryOpPlus(Expr\BinaryOp\Plus $node)
304
    {
305 1
        return $this->resolve($node->left) + $this->resolve($node->right);
306
    }
307
308 1
    protected function resolveExprBinaryOpMinus(Expr\BinaryOp\Minus $node)
309
    {
310 1
        return $this->resolve($node->left) - $this->resolve($node->right);
311
    }
312
313 2
    protected function resolveExprBinaryOpMul(Expr\BinaryOp\Mul $node)
314
    {
315 2
        return $this->resolve($node->left) * $this->resolve($node->right);
316
    }
317
318 1
    protected function resolveExprBinaryOpPow(Expr\BinaryOp\Pow $node)
319
    {
320 1
        return pow($this->resolve($node->left), $this->resolve($node->right));
321
    }
322
323 1
    protected function resolveExprBinaryOpDiv(Expr\BinaryOp\Div $node)
324
    {
325 1
        return $this->resolve($node->left) / $this->resolve($node->right);
326
    }
327
328 1
    protected function resolveExprBinaryOpMod(Expr\BinaryOp\Mod $node)
329
    {
330 1
        return $this->resolve($node->left) % $this->resolve($node->right);
331
    }
332
333 1
    protected function resolveExprBooleanNot(Expr\BooleanNot $node)
334
    {
335 1
        return !$this->resolve($node->expr);
336
    }
337
338 1
    protected function resolveExprBitwiseNot(Expr\BitwiseNot $node)
339
    {
340 1
        return ~$this->resolve($node->expr);
341
    }
342
343 1
    protected function resolveExprBinaryOpBitwiseOr(Expr\BinaryOp\BitwiseOr $node)
344
    {
345 1
        return $this->resolve($node->left) | $this->resolve($node->right);
346
    }
347
348 1
    protected function resolveExprBinaryOpBitwiseAnd(Expr\BinaryOp\BitwiseAnd $node)
349
    {
350 1
        return $this->resolve($node->left) & $this->resolve($node->right);
351
    }
352
353 1
    protected function resolveExprBinaryOpBitwiseXor(Expr\BinaryOp\BitwiseXor $node)
354
    {
355 1
        return $this->resolve($node->left) ^ $this->resolve($node->right);
356
    }
357
358 1
    protected function resolveExprBinaryOpShiftLeft(Expr\BinaryOp\ShiftLeft $node)
359
    {
360 1
        return $this->resolve($node->left) << $this->resolve($node->right);
361
    }
362
363 1
    protected function resolveExprBinaryOpShiftRight(Expr\BinaryOp\ShiftRight $node)
364
    {
365 1
        return $this->resolve($node->left) >> $this->resolve($node->right);
366
    }
367
368 3
    protected function resolveExprBinaryOpConcat(Expr\BinaryOp\Concat $node)
369
    {
370 3
        return $this->resolve($node->left) . $this->resolve($node->right);
371
    }
372
373 1
    protected function resolveExprTernary(Expr\Ternary $node)
374
    {
375 1
        if (isset($node->if)) {
376
            // Full syntax $a ? $b : $c;
377
378
            return $this->resolve($node->cond) ? $this->resolve($node->if) : $this->resolve($node->else);
379
        } else {
380
            // Short syntax $a ?: $c;
381
382 1
            return $this->resolve($node->cond) ?: $this->resolve($node->else);
383
        }
384
    }
385
386 1
    protected function resolveExprBinaryOpSmallerOrEqual(Expr\BinaryOp\SmallerOrEqual $node)
387
    {
388 1
        return $this->resolve($node->left) <= $this->resolve($node->right);
389
    }
390
391 1
    protected function resolveExprBinaryOpGreaterOrEqual(Expr\BinaryOp\GreaterOrEqual $node)
392
    {
393 1
        return $this->resolve($node->left) >= $this->resolve($node->right);
394
    }
395
396 1
    protected function resolveExprBinaryOpEqual(Expr\BinaryOp\Equal $node)
397
    {
398 1
        return $this->resolve($node->left) == $this->resolve($node->right);
399
    }
400
401 1
    protected function resolveExprBinaryOpNotEqual(Expr\BinaryOp\NotEqual $node)
402
    {
403 1
        return $this->resolve($node->left) != $this->resolve($node->right);
404
    }
405
406 1
    protected function resolveExprBinaryOpSmaller(Expr\BinaryOp\Smaller $node)
407
    {
408 1
        return $this->resolve($node->left) < $this->resolve($node->right);
409
    }
410
411 2
    protected function resolveExprBinaryOpGreater(Expr\BinaryOp\Greater $node)
412
    {
413 2
        return $this->resolve($node->left) > $this->resolve($node->right);
414
    }
415
416 1
    protected function resolveExprBinaryOpIdentical(Expr\BinaryOp\Identical $node)
417
    {
418 1
        return $this->resolve($node->left) === $this->resolve($node->right);
419
    }
420
421 1
    protected function resolveExprBinaryOpNotIdentical(Expr\BinaryOp\NotIdentical $node)
422
    {
423 1
        return $this->resolve($node->left) !== $this->resolve($node->right);
424
    }
425
426 1
    protected function resolveExprBinaryOpBooleanAnd(Expr\BinaryOp\BooleanAnd $node)
427
    {
428 1
        return $this->resolve($node->left) && $this->resolve($node->right);
429
    }
430
431 1
    protected function resolveExprBinaryOpLogicalAnd(Expr\BinaryOp\LogicalAnd $node)
432
    {
433 1
        return $this->resolve($node->left) and $this->resolve($node->right);
434
    }
435
436 1
    protected function resolveExprBinaryOpBooleanOr(Expr\BinaryOp\BooleanOr $node)
437
    {
438 1
        return $this->resolve($node->left) || $this->resolve($node->right);
439
    }
440
441 1
    protected function resolveExprBinaryOpLogicalOr(Expr\BinaryOp\LogicalOr $node)
442
    {
443 1
        return $this->resolve($node->left) or $this->resolve($node->right);
444
    }
445
446 1
    protected function resolveExprBinaryOpLogicalXor(Expr\BinaryOp\LogicalXor $node)
447
    {
448 1
        return $this->resolve($node->left) xor $this->resolve($node->right);
449
    }
450
451 51
    private function getDispatchMethodFor(Node $node)
452
    {
453 51
        $nodeType = $node->getType();
454 51
        return 'resolve' . str_replace('_', '', $nodeType);
455
    }
456
457
    /**
458
     * Utility method to fetch reflection class instance by name
459
     *
460
     * Supports:
461
     *   'self' keyword
462
     *   'parent' keyword
463
     *    not-FQN class names
464
     *
465
     * @param Node\Name $node Class name node
466
     *
467
     * @return bool|\ReflectionClass
468
     *
469
     * @throws ReflectionException
470
     */
471 13
    private function fetchReflectionClass(Node\Name $node)
472
    {
473 13
        $className  = $node->toString();
474 13
        $isFQNClass = $node instanceof Node\Name\FullyQualified;
475 13
        if ($isFQNClass) {
476
            // check to see if the class is already loaded and is safe to use
477
            // PHP's ReflectionClass to determine if the class is user defined
478 6
            if (class_exists($className, false)) {
479 6
                $refClass = new \ReflectionClass($className);
480 6
                if (!$refClass->isUserDefined()) {
481 3
                    return $refClass;
482
                }
483
            }
484 4
            return new ReflectionClass($className);
485
        }
486
487 9
        if ('self' === $className) {
488 9
            if ($this->context instanceof \ReflectionClass) {
489 9
                return $this->context;
490
            } elseif (method_exists($this->context, 'getDeclaringClass')) {
491
                return $this->context->getDeclaringClass();
492
            }
493
        }
494
495 1
        if ('parent' === $className) {
496 1
            if ($this->context instanceof \ReflectionClass) {
497 1
                return $this->context->getParentClass();
498
            } elseif (method_exists($this->context, 'getDeclaringClass')) {
499
                return $this->context->getDeclaringClass()->getParentClass();
500
            }
501
        }
502
503
        if (method_exists($this->context, 'getFileName')) {
504
            /** @var ReflectionFileNamespace|null $fileNamespace */
505
            $fileName      = $this->context->getFileName();
506
            $namespaceName = $this->resolveScalarMagicConstNamespace();
507
508
            $fileNamespace = new ReflectionFileNamespace($fileName, $namespaceName);
509
            return $fileNamespace->getClass($className);
510
        }
511
512
        throw new ReflectionException("Can not resolve class $className");
513
    }
514
}
515