Completed
Push — master ( 34991a...adfc38 )
by Alexander
13s
created

src/Traits/ReflectionFunctionLikeTrait.php (2 issues)

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\Traits;
12
13
14
use Go\ParserReflection\NodeVisitor\GeneratorDetector;
15
use Go\ParserReflection\NodeVisitor\StaticVariablesCollector;
16
use Go\ParserReflection\ReflectionParameter;
17
use Go\ParserReflection\ReflectionType;
18
use PhpParser\Node\Expr\Closure;
19
use PhpParser\Node\FunctionLike;
20
use PhpParser\Node\NullableType;
21
use PhpParser\Node\Stmt\ClassMethod;
22
use PhpParser\Node\Stmt\Function_;
23
use PhpParser\NodeTraverser;
24
25
/**
26
 * General trait for all function-like reflections
27
 */
28
trait ReflectionFunctionLikeTrait
29
{
30
    use InitializationTrait;
31
32
    /**
33
     * @var FunctionLike
34
     */
35
    protected $functionLikeNode;
36
37
    /**
38
     * Namespace name
39
     *
40
     * @var string
41
     */
42
    protected $namespaceName = '';
43
44
    /**
45
     * @var array|ReflectionParameter[]
46
     */
47
    protected $parameters;
48
49
    /**
50
     * {@inheritDoc}
51
     */
52 65
    public function getClosureScopeClass()
53
    {
54 65
        $this->initializeInternalReflection();
55
56 65
        return forward_static_call('parent::getClosureScopeClass');
57
    }
58
59
    /**
60
     * {@inheritDoc}
61
     */
62 65
    public function getClosureThis()
63
    {
64 65
        $this->initializeInternalReflection();
65
66 65
        return forward_static_call('parent::getClosureThis');
67
    }
68
69 129
    public function getDocComment()
70
    {
71 129
        $docComment = $this->functionLikeNode->getDocComment();
72
73 129
        return $docComment ? $docComment->getText() : false;
74
    }
75
76 129
    public function getEndLine()
77
    {
78 129
        return $this->functionLikeNode->getAttribute('endLine');
79
    }
80
81 65
    public function getExtension()
82
    {
83 65
        return null;
84
    }
85
86 65
    public function getExtensionName()
87
    {
88 65
        return false;
89
    }
90
91 71
    public function getFileName()
92
    {
93 71
        return $this->functionLikeNode->getAttribute('fileName');
94
    }
95
96
    /**
97
     * {@inheritDoc}
98
     */
99 2028
    public function getName()
100
    {
101 2028
        if ($this->functionLikeNode instanceof Function_ || $this->functionLikeNode instanceof ClassMethod) {
102 2028
            $functionName = $this->functionLikeNode->name;
103
104 2028
            return $this->namespaceName ? $this->namespaceName . '\\' . $functionName : $functionName;
105
        }
106
107
        return false;
108
    }
109
110
    /**
111
     * {@inheritDoc}
112
     */
113 71
    public function getNamespaceName()
114
    {
115 71
        return $this->namespaceName;
116
    }
117
118
    /**
119
     * Get the number of parameters that a function defines, both optional and required.
120
     *
121
     * @link http://php.net/manual/en/reflectionfunctionabstract.getnumberofparameters.php
122
     *
123
     * @return int
124
     */
125 115
    public function getNumberOfParameters()
126
    {
127 115
        return count($this->functionLikeNode->getParams());
128
    }
129
130
    /**
131
     * Get the number of required parameters that a function defines.
132
     *
133
     * @link http://php.net/manual/en/reflectionfunctionabstract.getnumberofrequiredparameters.php
134
     *
135
     * @return int
136
     */
137 65
    public function getNumberOfRequiredParameters()
138
    {
139 65
        $requiredParameters = 0;
140 65
        foreach ($this->getParameters() as $parameter) {
141 18
            if (!$parameter->isOptional()) {
142 18
                $requiredParameters++;
143
            }
144
        }
145
146 65
        return $requiredParameters;
147
    }
148
149
    /**
150
     * {@inheritDoc}
151
     */
152 207
    public function getParameters()
153
    {
154 207
        if (!isset($this->parameters)) {
155 79
            $parameters = [];
156
157 79
            foreach ($this->functionLikeNode->getParams() as $parameterIndex => $parameterNode) {
158 32
                $reflectionParameter = new ReflectionParameter(
159 32
                    $this->getName(),
160 32
                    $parameterNode->name,
161 32
                    $parameterNode,
162 32
                    $parameterIndex,
163 32
                    $this
164
                );
165 32
                $parameters[] = $reflectionParameter;
166
            }
167
168 79
            $this->parameters = $parameters;
169
        }
170
171 207
        return $this->parameters;
172
    }
173
174
    /**
175
     * Gets the specified return type of a function
176
     *
177
     * @return \ReflectionType
178
     *
179
     * @link http://php.net/manual/en/reflectionfunctionabstract.getreturntype.php
180
     */
181 65
    public function getReturnType()
182
    {
183 65
        $isBuiltin  = false;
184 65
        $returnType = $this->functionLikeNode->getReturnType();
185 65
        $isNullable = $returnType instanceof NullableType;
186
187 65
        if ($isNullable) {
188 6
            $returnType = $returnType->type;
189
        }
190 65
        if (is_object($returnType)) {
191 5
            $returnType = $returnType->toString();
192 61
        } elseif (is_string($returnType)) {
193 11
            $isBuiltin = true;
194
        } else {
195 51
            return null;
196
        }
197
198 15
        return new ReflectionType($returnType, $isNullable, $isBuiltin);
199
    }
200
201
    /**
202
     * {@inheritDoc}
203
     */
204 65
    public function getShortName()
205
    {
206 65
        if ($this->functionLikeNode instanceof Function_ || $this->functionLikeNode instanceof ClassMethod) {
207 65
            return $this->functionLikeNode->name;
208
        }
209
210
        return false;
211
    }
212
213 129
    public function getStartLine()
214
    {
215 129
        return $this->functionLikeNode->getAttribute('startLine');
216
    }
217
218
    /**
219
     * {@inheritDoc}
220
     */
221 65
    public function getStaticVariables()
222
    {
223
        // In nikic/PHP-Parser < 2.0.0 the default behavior is cloning
224
        //     nodes when traversing them. Passing FALSE to the constructor
225
        //     prevents this.
226
        // In nikic/PHP-Parser >= 2.0.0 and < 3.0.0 the default behavior was
227
        //     changed to not clone nodes, but the parameter was retained as
228
        //     an option.
229
        // In nikic/PHP-Parser >= 3.0.0 the option to clone nodes was removed
230
        //     as a constructor parameter, so Scrutinizer will pick this up as
231
        //     an issue. It is retained for legacy compatibility.
232 65
        $nodeTraverser      = new NodeTraverser(false);
0 ignored issues
show
The call to NodeTraverser::__construct() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
233 65
        $variablesCollector = new StaticVariablesCollector($this);
234 65
        $nodeTraverser->addVisitor($variablesCollector);
235
236
        /* @see https://github.com/nikic/PHP-Parser/issues/235 */
237 65
        $nodeTraverser->traverse($this->functionLikeNode->getStmts() ?: array());
238
239 65
        return $variablesCollector->getStaticVariables();
240
    }
241
242
    /**
243
     * Checks if the function has a specified return type
244
     *
245
     * @return bool
246
     *
247
     * @link http://php.net/manual/en/reflectionfunctionabstract.hasreturntype.php
248
     */
249 66
    public function hasReturnType()
250
    {
251 66
        $returnType = $this->functionLikeNode->getReturnType();
252
253 66
        return isset($returnType);
254
    }
255
256
    /**
257
     * {@inheritDoc}
258
     */
259 65
    public function inNamespace()
260
    {
261 65
        return !empty($this->namespaceName);
262
    }
263
264
    /**
265
     * {@inheritDoc}
266
     */
267 64
    public function isClosure()
268
    {
269 64
        return $this->functionLikeNode instanceof Closure;
270
    }
271
272
    /**
273
     * {@inheritDoc}
274
     */
275 64
    public function isDeprecated()
276
    {
277
        // userland method/function/closure can not be deprecated
278 64
        return false;
279
    }
280
281
    /**
282
     * {@inheritDoc}
283
     */
284 64
    public function isGenerator()
285
    {
286
        // In nikic/PHP-Parser < 2.0.0 the default behavior is cloning
287
        //     nodes when traversing them. Passing FALSE to the constructor
288
        //     prevents this.
289
        // In nikic/PHP-Parser >= 2.0.0 and < 3.0.0 the default behavior was
290
        //     changed to not clone nodes, but the parameter was retained as
291
        //     an option.
292
        // In nikic/PHP-Parser >= 3.0.0 the option to clone nodes was removed
293
        //     as a constructor parameter, so Scrutinizer will pick this up as
294
        //     an issue. It is retained for legacy compatibility.
295 64
        $nodeTraverser = new NodeTraverser(false);
0 ignored issues
show
The call to NodeTraverser::__construct() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
296 64
        $nodeDetector  = new GeneratorDetector();
297 64
        $nodeTraverser->addVisitor($nodeDetector);
298
299
        /* @see https://github.com/nikic/PHP-Parser/issues/235 */
300 64
        $nodeTraverser->traverse($this->functionLikeNode->getStmts() ?: array());
301
302 64
        return $nodeDetector->isGenerator();
303
    }
304
305
    /**
306
     * {@inheritDoc}
307
     */
308 64
    public function isInternal()
309
    {
310
        // never can be an internal method
311 64
        return false;
312
    }
313
314
    /**
315
     * {@inheritDoc}
316
     */
317 64
    public function isUserDefined()
318
    {
319
        // always defined by user, because we parse the source code
320 64
        return true;
321
    }
322
323
    /**
324
     * {@inheritDoc}
325
     */
326 64
    public function isVariadic()
327
    {
328 64
        foreach ($this->getParameters() as $parameter) {
329 17
            if ($parameter->isVariadic()) {
330 17
                return true;
331
            }
332
        }
333
334 62
        return false;
335
    }
336
337
    /**
338
     * {@inheritDoc}
339
     */
340 65
    public function returnsReference()
341
    {
342 65
        return $this->functionLikeNode->returnsByRef();
343
    }
344
}
345