Completed
Pull Request — master (#15)
by Alexander
03:39
created

NodeExpressionResolver::resolveExprConstFetch()   C

Complexity

Conditions 8
Paths 16

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 34
c 0
b 0
f 0
ccs 21
cts 21
cp 1
rs 5.3846
cc 8
eloc 21
nc 16
nop 1
crap 8
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
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 36
    public function __construct($context)
72
    {
73 36
        $this->context = $context;
74 36
    }
75
76 10
    public function getConstantName()
77
    {
78 10
        return $this->constantName;
79
    }
80
81 22
    public function getValue()
82
    {
83 22
        return $this->value;
84
    }
85
86 10
    public function isConstant()
87
    {
88 10
        return $this->isConstant;
89
    }
90
91
    /**
92
     * {@inheritDoc}
93
     */
94 22
    public function process(Node $node)
95
    {
96 22
        $this->nodeLevel    = 0;
97 22
        $this->isConstant   = false;
98 22
        $this->constantName = null;
99 22
        $this->value        = $this->resolve($node);
100 22
    }
101
102
    /**
103
     * Resolves node into valid value
104
     *
105
     * @param Node $node
106
     *
107
     * @return mixed
108
     */
109 22
    protected function resolve(Node $node)
110
    {
111 22
        $value = null;
112
        try {
113 22
            ++$this->nodeLevel;
114
115 22
            $nodeType   = $node->getType();
116 22
            $methodName = 'resolve' . str_replace('_', '', $nodeType);
117 22
            if (method_exists($this, $methodName)) {
118 22
                $value = $this->$methodName($node);
119
            }
120
121 22
            --$this->nodeLevel;
122
        } catch (\Exception $e) {
123
            --$this->nodeLevel;
124
        }
125
126 22
        return $value;
127
    }
128
129 4
    protected function resolveScalarDNumber(Scalar\DNumber $node)
130
    {
131 4
        return $node->value;
132
    }
133
134 13
    protected function resolveScalarLNumber(Scalar\LNumber $node)
135
    {
136 13
        return $node->value;
137
    }
138
139 9
    protected function resolveScalarString(Scalar\String_ $node)
140
    {
141 9
        return $node->value;
142
    }
143
144
    protected function resolveScalarMagicConstMethod()
145
    {
146
        if ($this->context instanceof \ReflectionMethod) {
147
            $fullName = $this->context->getDeclaringClass()->getName() . '::' . $this->context->getShortName();
148
149
            return $fullName;
150
        }
151
152
        return '';
153
    }
154
155
    protected function resolveScalarMagicConstFunction()
156
    {
157
        if ($this->context instanceof \ReflectionFunctionAbstract) {
158
            return $this->context->getName();
159
        }
160
161
        return '';
162
    }
163
164 10
    protected function resolveScalarMagicConstNamespace()
165
    {
166 10
        if (method_exists($this->context, 'getNamespaceName')) {
167 8
            return $this->context->getNamespaceName();
168
        }
169
170 2
        if ($this->context instanceof ReflectionFileNamespace) {
171 2
            return $this->context->getName();
172
        }
173
174
        return '';
175
    }
176
177 8
    protected function resolveScalarMagicConstClass()
178
    {
179 8
        if ($this->context instanceof \ReflectionClass) {
180 1
            return $this->context->getName();
181
        }
182 7
        if (method_exists($this->context, 'getDeclaringClass')) {
183
            $declaringClass = $this->context->getDeclaringClass();
184
            if ($declaringClass instanceof \ReflectionClass) {
185
                return $declaringClass->getName();
186
            }
187
        }
188
189 7
        return '';
190
    }
191
192 1
    protected function resolveScalarMagicConstDir()
193
    {
194 1
        if (method_exists($this->context, 'getFileName')) {
195 1
            return dirname($this->context->getFileName());
196
        }
197
198
        return '';
199
    }
200
201 3
    protected function resolveScalarMagicConstFile()
202
    {
203 3
        if (method_exists($this->context, 'getFileName')) {
204 3
            return $this->context->getFileName();
205
        }
206
207
        return '';
208
    }
209
210 3
    protected function resolveScalarMagicConstLine(MagicConst\Line $node)
211
    {
212 3
        return $node->hasAttribute('startLine') ? $node->getAttribute('startLine') : 0;
213
    }
214
215 1
    protected function resolveScalarMagicConstTrait()
216
    {
217 1
        if ($this->context instanceof \ReflectionClass && $this->context->isTrait()) {
218 1
            return $this->context->getName();
219
        }
220
221
        return '';
222
    }
223
224 13
    protected function resolveExprConstFetch(Expr\ConstFetch $node)
225
    {
226 13
        $constantValue = null;
227 13
        $isResolved    = false;
228
229
        /** @var ReflectionFileNamespace|null $fileNamespace */
230 13
        $fileNamespace = null;
0 ignored issues
show
Unused Code introduced by
$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...
231 13
        $isFQNConstant = $node->name instanceof Node\Name\FullyQualified;
232 13
        $constantName  = $node->name->toString();
233
234 13
        if (!$isFQNConstant) {
235 13
            if (method_exists($this->context, 'getFileName')) {
236 13
                $fileName      = $this->context->getFileName();
237 13
                $namespaceName = $this->context->getNamespaceName();
238 13
                $fileNamespace = new ReflectionFileNamespace($fileName, $namespaceName);
239 13
                if ($fileNamespace->hasConstant($constantName)) {
240 9
                    $constantValue = $fileNamespace->getConstant($constantName);
241 9
                    $constantName  = $fileNamespace->getName() . '\\' . $constantName;
242 9
                    $isResolved    = true;
243
                }
244
            }
245
        }
246
247 13
        if (!$isResolved && defined($constantName)) {
248 12
            $constantValue = constant($constantName);
249
        }
250
251 13
        if ($this->nodeLevel === 1 && !isset(self::$notConstants[$constantName])) {
252 9
            $this->isConstant   = true;
253 9
            $this->constantName = $constantName;
254
        }
255
256 13
        return $constantValue;
257
    }
258
259 4
    protected function resolveExprClassConstFetch(Expr\ClassConstFetch $node)
260
    {
261 4
        $refClass     = $this->fetchReflectionClass($node->class);
262 4
        $constantName = $node->name;
263
264
        // special handling of ::class constants
265 4
        if ('class' === $constantName) {
266 1
            return $refClass->getName();
267
        }
268
269 4
        return $refClass->getConstant($constantName);
270
    }
271
272 9
    protected function resolveExprArray(Expr\Array_ $node)
273
    {
274 9
        $result = [];
275 9
        foreach ($node->items as $itemIndex => $arrayItem) {
276 8
            $itemValue = $this->resolve($arrayItem->value);
277 8
            $itemKey   = isset($arrayItem->key) ? $this->resolve($arrayItem->key) : $itemIndex;
278 8
            $result[$itemKey] = $itemValue;
279
        }
280
281 9
        return $result;
282
    }
283
284
    /**
285
     * Utility method to fetch reflection class instance by name
286
     *
287
     * Supports:
288
     *   'self' keyword
289
     *   'parent' keyword
290
     *    not-FQN class names
291
     *
292
     * @param Node\Name $node Class name node
293
     *
294
     * @return bool|\ReflectionClass
295
     *
296
     * @throws ReflectionException
297
     */
298 4
    private function fetchReflectionClass(Node\Name $node)
299
    {
300 4
        $className  = $node->toString();
301 4
        $isFQNClass = $node instanceof Node\Name\FullyQualified;
302 4
        if ($isFQNClass) {
303 1
            return new ReflectionClass($className);
304
        }
305
306 4
        if ('self' === $className) {
307 4
            if ($this->context instanceof \ReflectionClass) {
308 4
                return $this->context;
309
            } elseif (method_exists($this->context, 'getDeclaringClass')) {
310
                return $this->context->getDeclaringClass();
311
            }
312
        }
313
314 1
        if ('parent' === $className) {
315 1
            if ($this->context instanceof \ReflectionClass) {
316 1
                return $this->context->getParentClass();
317
            } elseif (method_exists($this->context, 'getDeclaringClass')) {
318
                return $this->context->getDeclaringClass()->getParentClass();
319
            }
320
        }
321
322
        if (method_exists($this->context, 'getFileName')) {
323
            /** @var ReflectionFileNamespace|null $fileNamespace */
324
            $fileName      = $this->context->getFileName();
325
            $namespaceName = $this->context->getNamespaceName();
326
327
            $fileNamespace = new ReflectionFileNamespace($fileName, $namespaceName);
328
            return $fileNamespace->getClass($className);
329
        }
330
331
        throw new ReflectionException("Can not resolve class $className");
332
    }
333
}
334