Completed
Push — master ( b67b3c...3cb246 )
by Alexander
02:16
created

ReflectionMethod   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 281
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 7

Test Coverage

Coverage 74.55%

Importance

Changes 4
Bugs 1 Features 2
Metric Value
wmc 40
c 4
b 1
f 2
lcom 3
cbo 7
dl 0
loc 281
ccs 82
cts 110
cp 0.7455
rs 8.2609

21 Methods

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