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

src/ReflectionParameter.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;
12
13
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
14
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
15
use PhpParser\Node\Name;
16
use PhpParser\Node\NullableType;
17
use PhpParser\Node\Param;
18
use ReflectionParameter as BaseReflectionParameter;
19
20
/**
21
 * AST-based reflection for method/function parameter
22
 */
23
class ReflectionParameter extends BaseReflectionParameter
24
{
25
    use InternalPropertiesEmulationTrait;
26
27
    /**
28
     * Reflection function or method
29
     *
30
     * @var \ReflectionFunctionAbstract
31
     */
32
    private $declaringFunction;
33
34
    /**
35
     * Stores the default value for node (if present)
36
     *
37
     * @var mixed
38
     */
39
    private $defaultValue = null;
40
41
    /**
42
     * Whether or not default value is constant
43
     *
44
     * @var bool
45
     */
46
    private $isDefaultValueConstant = false;
47
48
    /**
49
     * Name of the constant of default value
50
     *
51
     * @var string
52
     */
53
    private $defaultValueConstantName;
54
55
    /**
56
     * Index of parameter in the list
57
     *
58
     * @var int
59
     */
60
    private $parameterIndex = 0;
61
62
    /**
63
     * Concrete parameter node
64
     *
65
     * @var Param
66
     */
67
    private $parameterNode;
68
69
    /**
70
     * Initializes a reflection for the property
71
     *
72
     * @param string|array $unusedFunctionName Name of the function/method
73
     * @param string $parameterName Name of the parameter to reflect
74
     * @param Param $parameterNode Parameter definition node
75
     * @param int $parameterIndex Index of parameter
76
     * @param \ReflectionFunctionAbstract $declaringFunction
77
     */
78 31
    public function __construct(
79
        $unusedFunctionName,
80
        $parameterName,
81
        Param $parameterNode = null,
82
        $parameterIndex = 0,
83
        \ReflectionFunctionAbstract $declaringFunction = null
84
    ) {
85
        // Let's unset original read-only property to have a control over it via __get
86 31
        unset($this->name);
87
88 31
        $this->parameterNode     = $parameterNode;
89 31
        $this->parameterIndex    = $parameterIndex;
90 31
        $this->declaringFunction = $declaringFunction;
91
92 31
        if ($this->isDefaultValueAvailable()) {
93 19
            if ($declaringFunction instanceof \ReflectionMethod) {
94 9
                $context = $declaringFunction->getDeclaringClass();
95
            } else {
96 10
                $context = $declaringFunction;
97
            };
98
99 19
            $expressionSolver = new NodeExpressionResolver($context);
100 19
            $expressionSolver->process($this->parameterNode->default);
101 19
            $this->defaultValue             = $expressionSolver->getValue();
102 19
            $this->isDefaultValueConstant   = $expressionSolver->isConstant();
103 19
            $this->defaultValueConstantName = $expressionSolver->getConstantName();
104
        }
105 31
    }
106
107
    /**
108
     * Emulating original behaviour of reflection
109
     */
110 1
    public function ___debugInfo()
111
    {
112
        return array(
113 1
            'name' => $this->parameterNode->name,
114
        );
115
    }
116
117
    /**
118
     * Returns string representation of this parameter.
119
     *
120
     * @return string
121
     */
122 20
    public function __toString()
123
    {
124 20
        $parameterType   = $this->getType();
125 20
        $isNullableParam = !empty($parameterType) && $this->allowsNull();
0 ignored issues
show
$isNullableParam 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...
126 20
        $isOptional      = $this->isOptional();
127 20
        $hasDefaultValue = $this->isDefaultValueAvailable();
128 20
        $defaultValue    = '';
129 20
        if ($hasDefaultValue) {
130 9
            $defaultValue = $this->getDefaultValue();
131 9
            if (is_string($defaultValue) && strlen($defaultValue) > 15) {
132 4
                $defaultValue = substr($defaultValue, 0, 15) . '...';
133
            }
134
            /* @see https://3v4l.org/DJOEb for behaviour changes */
135 9
            if (is_double($defaultValue) && fmod($defaultValue, 1.0) === 0.0) {
136 3
                $defaultValue = (int) $defaultValue;
137
            }
138
139 9
            $defaultValue = str_replace('\\\\', '\\', var_export($defaultValue, true));
140
        }
141
142 20
        return sprintf(
143 20
            'Parameter #%d [ %s %s%s%s$%s%s ]',
144 20
            $this->parameterIndex,
145 20
            $isOptional ? '<optional>' : '<required>',
146 20
            $parameterType ? ReflectionType::convertToDisplayType($parameterType) . ' ' : '',
147 20
            $this->isVariadic() ? '...' : '',
148 20
            $this->isPassedByReference() ? '&' : '',
149 20
            $this->getName(),
150 20
            ($isOptional && $hasDefaultValue) ? (' = ' . $defaultValue) : ''
151
        );
152
    }
153
154
    /**
155
     * {@inheritDoc}
156
     */
157 21
    public function allowsNull()
158
    {
159
        // Enable 7.1 nullable types support
160 21
        if ($this->parameterNode->type instanceof NullableType) {
161 6
            return true;
162
        }
163
164 15
        $hasDefaultNull = $this->isDefaultValueAvailable() && $this->getDefaultValue() === null;
165 15
        if ($hasDefaultNull) {
166 3
            return true;
167
        }
168
169 14
        return !isset($this->parameterNode->type);
170
    }
171
172
    /**
173
     * {@inheritDoc}
174
     */
175 3
    public function canBePassedByValue()
176
    {
177 3
        return !$this->isPassedByReference();
178
    }
179
180
    /**
181
     * @inheritDoc
182
     */
183 2
    public function getClass()
184
    {
185 2
        $parameterType = $this->parameterNode->type;
186 2
        if ($parameterType instanceof Name) {
187 2
            if (!$parameterType instanceof Name\FullyQualified) {
188 1
                $parameterTypeName = $parameterType->toString();
189
190 1
                if ('self' === $parameterTypeName) {
191 1
                    return $this->getDeclaringClass();
192
                }
193
194 1
                if ('parent' === $parameterTypeName) {
195 1
                    return $this->getDeclaringClass()->getParentClass();
196
                }
197
198
                throw new ReflectionException("Can not resolve a class name for parameter");
199
            }
200 1
            $className   = $parameterType->toString();
201 1
            $classOrInterfaceExists = class_exists($className, false) || interface_exists($className, false);
202
203 1
            return $classOrInterfaceExists ? new \ReflectionClass($className) : new ReflectionClass($className);
204
        }
205
206 1
        return null;
207
    }
208
209
    /**
210
     * {@inheritDoc}
211
     */
212 3
    public function getDeclaringClass()
213
    {
214 3
        if ($this->declaringFunction instanceof \ReflectionMethod) {
215 2
            return $this->declaringFunction->getDeclaringClass();
216
        };
217
218 1
        return null;
219
    }
220
221
    /**
222
     * {@inheritDoc}
223
     */
224 14
    public function getDeclaringFunction()
225
    {
226 14
        return $this->declaringFunction;
227
    }
228
229
    /**
230
     * {@inheritDoc}
231
     */
232 12
    public function getDefaultValue()
233
    {
234 12
        if (!$this->isDefaultValueAvailable()) {
235 1
            throw new ReflectionException('Internal error: Failed to retrieve the default value');
236
        }
237
238 11
        return $this->defaultValue;
239
    }
240
241
    /**
242
     * {@inheritDoc}
243
     */
244 6
    public function getDefaultValueConstantName()
245
    {
246 6
        if (!$this->isDefaultValueAvailable()) {
247 1
            throw new ReflectionException('Internal error: Failed to retrieve the default value');
248
        }
249
250 5
        return $this->defaultValueConstantName;
251
    }
252
253
    /**
254
     * @inheritDoc
255
     */
256 21
    public function getName()
257
    {
258 21
        return $this->parameterNode->name;
259
    }
260
261
    /**
262
     * {@inheritDoc}
263
     */
264 3
    public function getPosition()
265
    {
266 3
        return $this->parameterIndex;
267
    }
268
269
    /**
270
     * @inheritDoc
271
     */
272 21
    public function getType()
273
    {
274 21
        $isBuiltin     = false;
275 21
        $parameterType = $this->parameterNode->type;
276 21
        if ($parameterType instanceof NullableType) {
277 6
            $parameterType = $parameterType->type;
278
        }
279
280 21
        $allowsNull = $this->allowsNull();
281 21
        if (is_object($parameterType)) {
282 3
            $parameterType = $parameterType->toString();
283 21
        } elseif (is_string($parameterType)) {
284 17
            $isBuiltin = true;
285
        } else {
286 6
            return null;
287
        }
288
289 17
        return new ReflectionType($parameterType, $allowsNull, $isBuiltin);
290
    }
291
292
    /**
293
     * @inheritDoc
294
     */
295 4
    public function hasType()
296
    {
297 4
        $hasType = isset($this->parameterNode->type);
298
299 4
        return $hasType;
300
    }
301
302
    /**
303
     * @inheritDoc
304
     */
305 3
    public function isArray()
306
    {
307 3
        return 'array' === $this->parameterNode->type;
308
    }
309
310
    /**
311
     * @inheritDoc
312
     */
313 3
    public function isCallable()
314
    {
315 3
        return 'callable' === $this->parameterNode->type;
316
    }
317
318
    /**
319
     * @inheritDoc
320
     */
321 45
    public function isDefaultValueAvailable()
322
    {
323 45
        return isset($this->parameterNode->default);
324
    }
325
326
    /**
327
     * {@inheritDoc}
328
     */
329 5
    public function isDefaultValueConstant()
330
    {
331 5
        return $this->isDefaultValueConstant;
332
    }
333
334
    /**
335
     * {@inheritDoc}
336
     */
337 36
    public function isOptional()
338
    {
339 36
        return $this->isVariadic() || ($this->isDefaultValueAvailable() && $this->haveSiblingsDefalutValues());
340
    }
341
342
    /**
343
     * @inheritDoc
344
     */
345 20
    public function isPassedByReference()
346
    {
347 20
        return (bool) $this->parameterNode->byRef;
348
    }
349
350
    /**
351
     * @inheritDoc
352
     */
353 52
    public function isVariadic()
354
    {
355 52
        return (bool) $this->parameterNode->variadic;
356
    }
357
358
    /**
359
     * Returns if all following parameters have a default value definition.
360
     *
361
     * @return bool
362
     * @throws ReflectionException If could not fetch declaring function reflection
363
     */
364 14
    protected function haveSiblingsDefalutValues()
365
    {
366 14
        $function = $this->getDeclaringFunction();
367 14
        if (null === $function) {
368
            throw new ReflectionException('Could not get the declaring function reflection.');
369
        }
370
371
        /** @var \ReflectionParameter[] $remainingParameters */
372 14
        $remainingParameters = array_slice($function->getParameters(), $this->parameterIndex + 1);
373 14
        foreach ($remainingParameters as $reflectionParameter) {
374 14
            if (!$reflectionParameter->isDefaultValueAvailable()) {
375 14
                return false;
376
            }
377
        }
378
379 13
        return true;
380
    }
381
}
382