ReflectionMethod   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 323
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 10

Test Coverage

Coverage 97.44%

Importance

Changes 0
Metric Value
wmc 49
lcom 3
cbo 10
dl 0
loc 323
ccs 114
cts 117
cp 0.9744
rs 8.48
c 0
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 2
A getNode() 0 4 1
A ___debugInfo() 0 7 1
C __toString() 0 46 15
A getClosure() 0 6 1
A getDeclaringClass() 0 4 2
B getModifiers() 0 24 7
A getPrototype() 0 14 3
A invoke() 0 6 1
A invokeArgs() 0 6 1
A isAbstract() 0 4 2
A isConstructor() 0 4 1
A isDestructor() 0 4 1
A isFinal() 0 4 1
A isPrivate() 0 4 1
A isProtected() 0 4 1
A isPublic() 0 4 1
A isStatic() 0 4 1
A setAccessible() 0 6 1
A collectFromClassNode() 0 20 3
A __initialize() 0 4 1
A getClassMethodNode() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like ReflectionMethod 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 ReflectionMethod, 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;
12
13
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
14
use Go\ParserReflection\Traits\ReflectionFunctionLikeTrait;
15
use PhpParser\Node\Stmt\ClassLike;
16
use PhpParser\Node\Stmt\ClassMethod;
17
use ReflectionMethod as BaseReflectionMethod;
18
19
/**
20
 * AST-based reflection for the method in a class
21
 */
22
class ReflectionMethod extends BaseReflectionMethod
23
{
24
    use ReflectionFunctionLikeTrait, InternalPropertiesEmulationTrait;
25
26
    /**
27
     * Name of the class
28
     *
29
     * @var string
30
     */
31
    private $className;
32
33
    /**
34
     * Optional declaring class reference
35
     *
36
     * @var ReflectionClass
37
     */
38
    private $declaringClass;
39
40
    /**
41
     * Initializes reflection instance for the method node
42
     *
43
     * @param string $className Name of the class
44
     * @param string $methodName Name of the method
45
     * @param ClassMethod $classMethodNode AST-node for method
46
     * @param ReflectionClass $declaringClass Optional declaring class
47
     */
48 46
    public function __construct(
49
        $className,
50
        $methodName,
51
        ClassMethod $classMethodNode = null,
52
        ReflectionClass $declaringClass = null
53
    ) {
54
        //for some reason, ReflectionMethod->getNamespaceName in php always returns '', so we shouldn't use it too
55 46
        $this->className        = ltrim($className, '\\');
56 46
        $this->declaringClass   = $declaringClass;
57 46
        $this->functionLikeNode = $classMethodNode ?: ReflectionEngine::parseClassMethod($className, $methodName);
58
59
        // Let's unset original read-only properties to have a control over them via __get
60 46
        unset($this->name, $this->class);
61 46
    }
62
63
    /**
64
     * Returns an AST-node for method
65
     *
66
     * @return ClassMethod
67
     */
68
    public function getNode()
69
    {
70
        return $this->functionLikeNode;
71
    }
72
73
    /**
74
     * Emulating original behaviour of reflection
75
     */
76 1
    public function ___debugInfo()
77
    {
78
        return [
79 1
            'name'  => $this->getClassMethodNode()->name->toString(),
80 1
            'class' => $this->className
81
        ];
82
    }
83
84
    /**
85
     * Returns the string representation of the Reflection method object.
86
     *
87
     * @link http://php.net/manual/en/reflectionmethod.tostring.php
88
     *
89
     * @return string
90
     */
91 64
    public function __toString()
92
    {
93
        // Internally $this->getReturnType() !== null is the same as $this->hasReturnType()
94 64
        $returnType       = $this->getReturnType();
95 64
        $hasReturnType    = $returnType !== null;
96 64
        $paramsNeeded     = $hasReturnType || $this->getNumberOfParameters() > 0;
97 64
        $paramFormat      = $paramsNeeded ? "\n\n  - Parameters [%d] {%s\n  }" : '';
98 64
        $returnFormat     = $hasReturnType ? "\n  - Return [ %s ]" : '';
99 64
        $methodParameters = $this->getParameters();
100
        try {
101 64
            $prototype = $this->getPrototype();
102 63
        } catch (\ReflectionException $e) {
103 63
            $prototype = null;
104
        }
105 64
        $prototypeClass = $prototype ? $prototype->getDeclaringClass()->name : '';
106
107 64
        $paramString = '';
108 64
        $identation  = str_repeat(' ', 4);
109 64
        foreach ($methodParameters as $methodParameter) {
110 17
            $paramString .= "\n{$identation}" . $methodParameter;
111
        }
112
113 64
        return sprintf(
114 64
            "%sMethod [ <user%s%s%s>%s%s%s %s method %s ] {\n  @@ %s %d - %d{$paramFormat}{$returnFormat}\n}\n",
115 64
            $this->getDocComment() ? $this->getDocComment() . "\n" : '',
116 64
            $prototype ? ", overwrites {$prototypeClass}, prototype {$prototypeClass}" : '',
117 64
            $this->isConstructor() ? ', ctor' : '',
118 64
            $this->isDestructor() ? ', dtor' : '',
119 64
            $this->isFinal() ? ' final' : '',
120 64
            $this->isStatic() ? ' static' : '',
121 64
            $this->isAbstract() ? ' abstract' : '',
122 64
            join(
123 64
                ' ',
124 64
                \Reflection::getModifierNames(
125 64
                    $this->getModifiers() & (self::IS_PUBLIC | self::IS_PROTECTED | self::IS_PRIVATE)
126
                )
127
            ),
128 64
            $this->getName(),
129 64
            $this->getFileName(),
130 64
            $this->getStartLine(),
131 64
            $this->getEndLine(),
132 64
            count($methodParameters),
133 64
            $paramString,
134 64
            $returnType ? ReflectionType::convertToDisplayType($returnType) : ''
135
        );
136
    }
137
138
    /**
139
     * {@inheritDoc}
140
     */
141 1
    public function getClosure($object)
142
    {
143 1
        $this->initializeInternalReflection();
144
145 1
        return parent::getClosure($object);
146
    }
147
148
    /**
149
     * {@inheritDoc}
150
     */
151 138
    public function getDeclaringClass()
152
    {
153 138
        return isset($this->declaringClass) ? $this->declaringClass : new ReflectionClass($this->className);
154
    }
155
156
    /**
157
     * {@inheritDoc}
158
     */
159 68
    public function getModifiers()
160
    {
161 68
        $modifiers = 0;
162 68
        if ($this->isPublic()) {
163 54
            $modifiers += self::IS_PUBLIC;
164
        }
165 68
        if ($this->isProtected()) {
166 9
            $modifiers += self::IS_PROTECTED;
167
        }
168 68
        if ($this->isPrivate()) {
169 9
            $modifiers += self::IS_PRIVATE;
170
        }
171 68
        if ($this->isAbstract()) {
172 9
            $modifiers += self::IS_ABSTRACT;
173
        }
174 68
        if ($this->isFinal()) {
175 6
            $modifiers += self::IS_FINAL;
176
        }
177 68
        if ($this->isStatic()) {
178 11
            $modifiers += self::IS_STATIC;
179
        }
180
181 68
        return $modifiers;
182
    }
183
184
    /**
185
     * {@inheritDoc}
186
     */
187 65
    public function getPrototype()
188
    {
189 65
        $parent = $this->getDeclaringClass()->getParentClass();
190 65
        if (!$parent) {
191 48
            throw new ReflectionException("No prototype");
192
        }
193
194 17
        $prototypeMethod = $parent->getMethod($this->getName());
195 2
        if (!$prototypeMethod) {
196
            throw new ReflectionException("No prototype");
197
        }
198
199 2
        return $prototypeMethod;
200
    }
201
202
    /**
203
     * {@inheritDoc}
204
     */
205 1
    public function invoke($object, $args = null)
206
    {
207 1
        $this->initializeInternalReflection();
208
209 1
        return call_user_func_array('parent::invoke', func_get_args());
210
    }
211
212
    /**
213
     * {@inheritDoc}
214
     */
215 3
    public function invokeArgs($object, array $args)
216
    {
217 3
        $this->initializeInternalReflection();
218
219 3
        return parent::invokeArgs($object, $args);
220
    }
221
222
    /**
223
     * {@inheritDoc}
224
     */
225 132
    public function isAbstract()
226
    {
227 132
        return $this->getDeclaringClass()->isInterface() || $this->getClassMethodNode()->isAbstract();
228
    }
229
230
    /**
231
     * {@inheritDoc}
232
     */
233 128
    public function isConstructor()
234
    {
235 128
        return $this->getClassMethodNode()->name == '__construct';
236
    }
237
238
    /**
239
     * {@inheritDoc}
240
     */
241 128
    public function isDestructor()
242
    {
243 128
        return $this->getClassMethodNode()->name == '__destruct';
244
    }
245
246
    /**
247
     * {@inheritDoc}
248
     */
249 132
    public function isFinal()
250
    {
251 132
        return $this->getClassMethodNode()->isFinal();
252
    }
253
254
    /**
255
     * {@inheritDoc}
256
     */
257 132
    public function isPrivate()
258
    {
259 132
        return $this->getClassMethodNode()->isPrivate();
260
    }
261
262
    /**
263
     * {@inheritDoc}
264
     */
265 132
    public function isProtected()
266
    {
267 132
        return $this->getClassMethodNode()->isProtected();
268
    }
269
270
    /**
271
     * {@inheritDoc}
272
     */
273 135
    public function isPublic()
274
    {
275 135
        return $this->getClassMethodNode()->isPublic();
276
    }
277
278
    /**
279
     * {@inheritDoc}
280
     */
281 132
    public function isStatic()
282
    {
283 132
        return $this->getClassMethodNode()->isStatic();
284
    }
285
286
    /**
287
     * {@inheritDoc}
288
     */
289 1
    public function setAccessible($accessible)
290
    {
291 1
        $this->initializeInternalReflection();
292
293 1
        parent::setAccessible($accessible);
294 1
    }
295
296
    /**
297
     * Parses methods from the concrete class node
298
     *
299
     * @param ClassLike $classLikeNode Class-like node
300
     * @param ReflectionClass $reflectionClass Reflection of the class
301
     *
302
     * @return array|ReflectionMethod[]
303
     */
304 60
    public static function collectFromClassNode(ClassLike $classLikeNode, ReflectionClass $reflectionClass)
305
    {
306 60
        $methods = [];
307
308 60
        foreach ($classLikeNode->stmts as $classLevelNode) {
309 57
            if ($classLevelNode instanceof ClassMethod) {
310 46
                $classLevelNode->setAttribute('fileName', $classLikeNode->getAttribute('fileName'));
311
312 46
                $methodName = $classLevelNode->name->toString();
313 46
                $methods[$methodName] = new ReflectionMethod(
314 46
                    $reflectionClass->name,
315 46
                    $methodName,
316 46
                    $classLevelNode,
317 57
                    $reflectionClass
318
                );
319
            }
320
        }
321
322 60
        return $methods;
323
    }
324
325
    /**
326
     * Implementation of internal reflection initialization
327
     *
328
     * @return void
329
     */
330 69
    protected function __initialize()
331
    {
332 69
        parent::__construct($this->className, $this->getName());
333 69
    }
334
335
    /**
336
     * Returns ClassMethod node to prevent all possible type checks with instanceof
337
     *
338
     * @return ClassMethod
339
     */
340 581
    private function getClassMethodNode()
341
    {
342 581
        return $this->functionLikeNode;
343
    }
344
}
345