Completed
Push — master ( d36faf...a71d04 )
by Alexander
22s
created

ReflectionFunctionLikeTrait   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 320
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 96.94%

Importance

Changes 0
Metric Value
wmc 43
lcom 1
cbo 8
dl 0
loc 320
ccs 95
cts 98
cp 0.9694
rs 8.96
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A getClosureScopeClass() 0 6 1
A getClosureThis() 0 6 1
A getDocComment() 0 6 2
A getEndLine() 0 4 1
A getExtension() 0 4 1
A getExtensionName() 0 4 1
A getFileName() 0 4 1
A getName() 0 10 4
A getNamespaceName() 0 4 1
A getNumberOfParameters() 0 4 1
A getNumberOfRequiredParameters() 0 11 3
A getParameters() 0 21 3
A getReturnType() 0 22 5
A getShortName() 0 8 3
A getStartLine() 0 4 1
A getStaticVariables() 0 20 2
A hasReturnType() 0 6 1
A inNamespace() 0 4 1
A isClosure() 0 4 1
A isDeprecated() 0 5 1
A isGenerator() 0 20 2
A isInternal() 0 5 1
A isUserDefined() 0 5 1
A isVariadic() 0 10 3
A returnsReference() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like ReflectionFunctionLikeTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ReflectionFunctionLikeTrait, and based on these observations, apply Extract Interface, too.

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\Identifier;
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 65
    public function getClosureScopeClass()
54
    {
55 65
        $this->initializeInternalReflection();
56
57 65
        return parent::getClosureScopeClass();
58
    }
59
60
    /**
61
     * {@inheritDoc}
62
     */
63 65
    public function getClosureThis()
64
    {
65 65
        $this->initializeInternalReflection();
66
67 65
        return parent::getClosureThis();
68
    }
69
70 129
    public function getDocComment()
71
    {
72 129
        $docComment = $this->functionLikeNode->getDocComment();
73
74 129
        return $docComment ? $docComment->getText() : false;
75
    }
76
77 129
    public function getEndLine()
78
    {
79 129
        return $this->functionLikeNode->getAttribute('endLine');
80
    }
81
82 65
    public function getExtension()
83
    {
84 65
        return null;
85
    }
86
87 65
    public function getExtensionName()
88
    {
89 65
        return false;
90
    }
91
92 71
    public function getFileName()
93
    {
94 71
        return $this->functionLikeNode->getAttribute('fileName');
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     */
100 2028
    public function getName()
101
    {
102 2028
        if ($this->functionLikeNode instanceof Function_ || $this->functionLikeNode instanceof ClassMethod) {
103 2028
            $functionName = $this->functionLikeNode->name->toString();
104
105 2028
            return $this->namespaceName ? $this->namespaceName . '\\' . $functionName : $functionName;
106
        }
107
108
        return false;
109
    }
110
111
    /**
112
     * {@inheritDoc}
113
     */
114 71
    public function getNamespaceName()
115
    {
116 71
        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 115
    public function getNumberOfParameters()
127
    {
128 115
        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 65
    public function getNumberOfRequiredParameters()
139
    {
140 65
        $requiredParameters = 0;
141 65
        foreach ($this->getParameters() as $parameter) {
142 18
            if (!$parameter->isOptional()) {
143 18
                $requiredParameters++;
144
            }
145
        }
146
147 65
        return $requiredParameters;
148
    }
149
150
    /**
151
     * {@inheritDoc}
152
     */
153 207
    public function getParameters()
154
    {
155 207
        if (!isset($this->parameters)) {
156 79
            $parameters = [];
157
158 79
            foreach ($this->functionLikeNode->getParams() as $parameterIndex => $parameterNode) {
159 32
                $reflectionParameter = new ReflectionParameter(
160 32
                    $this->getName(),
161 32
                    (string)$parameterNode->var->name,
162 32
                    $parameterNode,
163 32
                    $parameterIndex,
164 32
                    $this
165
                );
166 32
                $parameters[] = $reflectionParameter;
167
            }
168
169 79
            $this->parameters = $parameters;
170
        }
171
172 207
        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 65
    public function getReturnType()
183
    {
184 65
        $isBuiltin  = false;
185 65
        $returnType = $this->functionLikeNode->getReturnType();
186 65
        $isNullable = $returnType instanceof NullableType;
187
188 65
        if ($isNullable) {
189 6
            $returnType = $returnType->type;
190
        }
191 65
        if ($returnType instanceof Identifier) {
192 11
            $isBuiltin = true;
193 11
            $returnType = $returnType->toString();
194 55
        } elseif (is_object($returnType)) {
195 5
            $returnType = $returnType->toString();
196 51
        } elseif (is_string($returnType)) {
197
            $isBuiltin = true;
198
        } else {
199 51
            return null;
200
        }
201
202 15
        return new ReflectionType($returnType, $isNullable, $isBuiltin);
203
    }
204
205
    /**
206
     * {@inheritDoc}
207
     */
208 65
    public function getShortName()
209
    {
210 65
        if ($this->functionLikeNode instanceof Function_ || $this->functionLikeNode instanceof ClassMethod) {
211 65
            return $this->functionLikeNode->name->toString();
212
        }
213
214
        return false;
215
    }
216
217 129
    public function getStartLine()
218
    {
219 129
        return $this->functionLikeNode->getAttribute('startLine');
220
    }
221
222
    /**
223
     * {@inheritDoc}
224
     */
225 65
    public function getStaticVariables()
226
    {
227
        // In nikic/PHP-Parser < 2.0.0 the default behavior is cloning
228
        //     nodes when traversing them. Passing FALSE to the constructor
229
        //     prevents this.
230
        // In nikic/PHP-Parser >= 2.0.0 and < 3.0.0 the default behavior was
231
        //     changed to not clone nodes, but the parameter was retained as
232
        //     an option.
233
        // In nikic/PHP-Parser >= 3.0.0 the option to clone nodes was removed
234
        //     as a constructor parameter, so Scrutinizer will pick this up as
235
        //     an issue. It is retained for legacy compatibility.
236 65
        $nodeTraverser      = new NodeTraverser(false);
0 ignored issues
show
Unused Code introduced by Lisachenko Alexander
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...
237 65
        $variablesCollector = new StaticVariablesCollector($this);
238 65
        $nodeTraverser->addVisitor($variablesCollector);
239
240
        /* @see https://github.com/nikic/PHP-Parser/issues/235 */
241 65
        $nodeTraverser->traverse($this->functionLikeNode->getStmts() ?: array());
242
243 65
        return $variablesCollector->getStaticVariables();
244
    }
245
246
    /**
247
     * Checks if the function has a specified return type
248
     *
249
     * @return bool
250
     *
251
     * @link http://php.net/manual/en/reflectionfunctionabstract.hasreturntype.php
252
     */
253 66
    public function hasReturnType()
254
    {
255 66
        $returnType = $this->functionLikeNode->getReturnType();
256
257 66
        return isset($returnType);
258
    }
259
260
    /**
261
     * {@inheritDoc}
262
     */
263 65
    public function inNamespace()
264
    {
265 65
        return !empty($this->namespaceName);
266
    }
267
268
    /**
269
     * {@inheritDoc}
270
     */
271 64
    public function isClosure()
272
    {
273 64
        return $this->functionLikeNode instanceof Closure;
274
    }
275
276
    /**
277
     * {@inheritDoc}
278
     */
279 64
    public function isDeprecated()
280
    {
281
        // userland method/function/closure can not be deprecated
282 64
        return false;
283
    }
284
285
    /**
286
     * {@inheritDoc}
287
     */
288 64
    public function isGenerator()
289
    {
290
        // In nikic/PHP-Parser < 2.0.0 the default behavior is cloning
291
        //     nodes when traversing them. Passing FALSE to the constructor
292
        //     prevents this.
293
        // In nikic/PHP-Parser >= 2.0.0 and < 3.0.0 the default behavior was
294
        //     changed to not clone nodes, but the parameter was retained as
295
        //     an option.
296
        // In nikic/PHP-Parser >= 3.0.0 the option to clone nodes was removed
297
        //     as a constructor parameter, so Scrutinizer will pick this up as
298
        //     an issue. It is retained for legacy compatibility.
299 64
        $nodeTraverser = new NodeTraverser(false);
0 ignored issues
show
Unused Code introduced by Lisachenko Alexander
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...
300 64
        $nodeDetector  = new GeneratorDetector();
301 64
        $nodeTraverser->addVisitor($nodeDetector);
302
303
        /* @see https://github.com/nikic/PHP-Parser/issues/235 */
304 64
        $nodeTraverser->traverse($this->functionLikeNode->getStmts() ?: array());
305
306 64
        return $nodeDetector->isGenerator();
307
    }
308
309
    /**
310
     * {@inheritDoc}
311
     */
312 64
    public function isInternal()
313
    {
314
        // never can be an internal method
315 64
        return false;
316
    }
317
318
    /**
319
     * {@inheritDoc}
320
     */
321 64
    public function isUserDefined()
322
    {
323
        // always defined by user, because we parse the source code
324 64
        return true;
325
    }
326
327
    /**
328
     * {@inheritDoc}
329
     */
330 64
    public function isVariadic()
331
    {
332 64
        foreach ($this->getParameters() as $parameter) {
333 17
            if ($parameter->isVariadic()) {
334 17
                return true;
335
            }
336
        }
337
338 62
        return false;
339
    }
340
341
    /**
342
     * {@inheritDoc}
343
     */
344 65
    public function returnsReference()
345
    {
346 65
        return $this->functionLikeNode->returnsByRef();
347
    }
348
}
349