Completed
Push — master ( c4d2d4...eae51e )
by Alexander
9s
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\ReflectionFileNamespace;
17
use Go\ParserReflection\ReflectionParameter;
18
use Go\ParserReflection\ReflectionType;
19
use PhpParser\Node\Expr\Closure;
20
use PhpParser\Node\FunctionLike;
21
use PhpParser\Node\NullableType;
22
use PhpParser\Node\Stmt\ClassMethod;
23
use PhpParser\Node\Stmt\Function_;
24
use PhpParser\NodeTraverser;
25
26
/**
27
 * General trait for all function-like reflections
28
 */
29
trait ReflectionFunctionLikeTrait
30
{
31
    use InitializationTrait;
32
33
    /**
34
     * @var FunctionLike
35
     */
36
    protected $functionLikeNode;
37
38
    /**
39
     * Namespace name
40
     *
41
     * @var string
42
     */
43
    protected $namespaceName = '';
44
45
    /**
46
     * @var array|ReflectionParameter[]
47
     */
48
    protected $parameters;
49
50
    /**
51
     * {@inheritDoc}
52
     */
53 64
    public function getClosureScopeClass()
54
    {
55 64
        $this->initializeInternalReflection();
56
57 64
        return forward_static_call('parent::getClosureScopeClass');
58
    }
59
60
    /**
61
     * {@inheritDoc}
62
     */
63 64
    public function getClosureThis()
64
    {
65 64
        $this->initializeInternalReflection();
66
67 64
        return forward_static_call('parent::getClosureThis');
68
    }
69
70 127
    public function getDocComment()
71
    {
72 127
        $docComment = $this->functionLikeNode->getDocComment();
73
74 127
        return $docComment ? $docComment->getText() : false;
75
    }
76
77 127
    public function getEndLine()
78
    {
79 127
        return $this->functionLikeNode->getAttribute('endLine');
80
    }
81
82 64
    public function getExtension()
83
    {
84 64
        return null;
85
    }
86
87 64
    public function getExtensionName()
88
    {
89 64
        return false;
90
    }
91
92 70
    public function getFileName()
93
    {
94 70
        return $this->functionLikeNode->getAttribute('fileName');
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     */
100 1997
    public function getName()
101
    {
102 1997
        if ($this->functionLikeNode instanceof Function_ || $this->functionLikeNode instanceof ClassMethod) {
103 1997
            $functionName = $this->functionLikeNode->name;
104
105 1997
            return $this->namespaceName ? $this->namespaceName . '\\' . $functionName : $functionName;
106
        }
107
108
        return false;
109
    }
110
111
    /**
112
     * {@inheritDoc}
113
     */
114 70
    public function getNamespaceName()
115
    {
116 70
        return $this->namespaceName;
117
    }
118
119
    /**
120
     * Get the number of parameters that a function defines, both optional and required.
121
     *
122
     * @link http://php.net/manual/en/reflectionfunctionabstract.getnumberofparameters.php
123
     *
124
     * @return int
125
     */
126 113
    public function getNumberOfParameters()
127
    {
128 113
        return count($this->functionLikeNode->getParams());
129
    }
130
131
    /**
132
     * Get the number of required parameters that a function defines.
133
     *
134
     * @link http://php.net/manual/en/reflectionfunctionabstract.getnumberofrequiredparameters.php
135
     *
136
     * @return int
137
     */
138 64
    public function getNumberOfRequiredParameters()
139
    {
140 64
        $requiredParameters = 0;
141 64
        foreach ($this->getParameters() as $parameter) {
142 17
            if (!$parameter->isOptional()) {
143 17
                $requiredParameters++;
144
            }
145
        }
146
147 64
        return $requiredParameters;
148
    }
149
150
    /**
151
     * {@inheritDoc}
152
     */
153 204
    public function getParameters()
154
    {
155 204
        if (!isset($this->parameters)) {
156 78
            $parameters = [];
157
158 78
            foreach ($this->functionLikeNode->getParams() as $parameterIndex => $parameterNode) {
159 31
                $reflectionParameter = new ReflectionParameter(
160 31
                    $this->getName(),
161 31
                    $parameterNode->name,
162
                    $parameterNode,
163
                    $parameterIndex,
164
                    $this
165
                );
166 31
                $parameters[] = $reflectionParameter;
167
            }
168
169 78
            $this->parameters = $parameters;
170
        }
171
172 204
        return $this->parameters;
173
    }
174
175
    /**
176
     * Gets the specified return type of a function
177
     *
178
     * @return \ReflectionType
179
     *
180
     * @link http://php.net/manual/en/reflectionfunctionabstract.getreturntype.php
181
     */
182 15
    public function getReturnType()
183
    {
184 15
        $isBuiltin  = false;
185 15
        $returnType = $this->functionLikeNode->getReturnType();
186 15
        $isNullable = $returnType instanceof NullableType;
187
188 15
        if ($isNullable) {
189 6
            $returnType = $returnType->type;
190
        }
191 15
        if (is_object($returnType)) {
192 5
            $returnType = $returnType->toString();
193 11
        } elseif (is_string($returnType)) {
194 11
            $isBuiltin = true;
195
        } else {
196 1
            return null;
197
        }
198
199 15
        return new ReflectionType($returnType, $isNullable, $isBuiltin);
200
    }
201
202
    /**
203
     * {@inheritDoc}
204
     */
205 64
    public function getShortName()
206
    {
207 64
        if ($this->functionLikeNode instanceof Function_ || $this->functionLikeNode instanceof ClassMethod) {
208 64
            return $this->functionLikeNode->name;
209
        }
210
211
        return false;
212
    }
213
214 127
    public function getStartLine()
215
    {
216 127
        return $this->functionLikeNode->getAttribute('startLine');
217
    }
218
219
    /**
220
     * {@inheritDoc}
221
     */
222 64
    public function getStaticVariables()
223
    {
224 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...
225 64
        $variablesCollector = new StaticVariablesCollector($this);
226 64
        $nodeTraverser->addVisitor($variablesCollector);
227
228
        /* @see https://github.com/nikic/PHP-Parser/issues/235 */
229 64
        $nodeTraverser->traverse($this->functionLikeNode->getStmts() ?: array());
230
231 64
        return $variablesCollector->getStaticVariables();
232
    }
233
234
    /**
235
     * Checks if the function has a specified return type
236
     *
237
     * @return bool
238
     *
239
     * @link http://php.net/manual/en/reflectionfunctionabstract.hasreturntype.php
240
     */
241 128
    public function hasReturnType()
242
    {
243 128
        $returnType = $this->functionLikeNode->getReturnType();
244
245 128
        return isset($returnType);
246
    }
247
248
    /**
249
     * {@inheritDoc}
250
     */
251 64
    public function inNamespace()
252
    {
253 64
        return !empty($this->namespaceName);
254
    }
255
256
    /**
257
     * {@inheritDoc}
258
     */
259 63
    public function isClosure()
260
    {
261 63
        return $this->functionLikeNode instanceof Closure;
262
    }
263
264
    /**
265
     * {@inheritDoc}
266
     */
267 63
    public function isDeprecated()
268
    {
269
        // userland method/function/closure can not be deprecated
270 63
        return false;
271
    }
272
273
    /**
274
     * {@inheritDoc}
275
     */
276 63
    public function isGenerator()
277
    {
278 63
        $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...
279 63
        $nodeDetector  = new GeneratorDetector();
280 63
        $nodeTraverser->addVisitor($nodeDetector);
281
282
        /* @see https://github.com/nikic/PHP-Parser/issues/235 */
283 63
        $nodeTraverser->traverse($this->functionLikeNode->getStmts() ?: array());
284
285 63
        return $nodeDetector->isGenerator();
286
    }
287
288
    /**
289
     * {@inheritDoc}
290
     */
291 63
    public function isInternal()
292
    {
293
        // never can be an internal method
294 63
        return false;
295
    }
296
297
    /**
298
     * {@inheritDoc}
299
     */
300 63
    public function isUserDefined()
301
    {
302
        // always defined by user, because we parse the source code
303 63
        return true;
304
    }
305
306
    /**
307
     * {@inheritDoc}
308
     */
309 63
    public function isVariadic()
310
    {
311 63
        foreach ($this->getParameters() as $parameter) {
312 16
            if ($parameter->isVariadic()) {
313 16
                return true;
314
            }
315
        }
316
317 61
        return false;
318
    }
319
320
    /**
321
     * {@inheritDoc}
322
     */
323 64
    public function returnsReference()
324
    {
325 64
        return $this->functionLikeNode->returnsByRef();
326
    }
327
}
328