Completed
Push — master ( 49e6cb...c60371 )
by Alexander
02:20
created

ReflectionMethod::isDestructor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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        = $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
     * Emulating original behaviour of reflection
65
     */
66 1
    public function ___debugInfo()
67
    {
68
        return [
69 1
            'name'  => $this->getClassMethodNode()->name,
70 1
            'class' => $this->className
71
        ];
72
    }
73
74
    /**
75
     * Returns the string representation of the Reflection method object.
76
     *
77
     * @link http://php.net/manual/en/reflectionmethod.tostring.php
78
     *
79
     * @return string
80
     */
81 64
    public function __toString()
82
    {
83
        // Internally $this->getReturnType() !== null is the same as $this->hasReturnType()
84 64
        $returnType       = $this->getReturnType();
85 64
        $hasReturnType    = $returnType !== null;
86 64
        $paramsNeeded     = $hasReturnType || $this->getNumberOfParameters() > 0;
87 64
        $paramFormat      = $paramsNeeded ? "\n\n  - Parameters [%d] {%s\n  }" : '';
88 64
        $returnFormat     = $hasReturnType ? "\n  - Return [ %s ]" : '';
89 64
        $methodParameters = $this->getParameters();
90
        try {
91 64
            $prototype = $this->getPrototype();
92 63
        } catch (\ReflectionException $e) {
93 63
            $prototype = null;
94
        }
95 64
        $prototypeClass = $prototype ? $prototype->getDeclaringClass()->name : '';
96
97 64
        $paramString = '';
98 64
        $identation  = str_repeat(' ', 4);
99 64
        foreach ($methodParameters as $methodParameter) {
100 17
            $paramString .= "\n{$identation}" . $methodParameter;
101
        }
102
103 64
        return sprintf(
104 64
            "%sMethod [ <user%s%s%s>%s%s%s %s method %s ] {\n  @@ %s %d - %d{$paramFormat}{$returnFormat}\n}\n",
105 64
            $this->getDocComment() ? $this->getDocComment() . "\n" : '',
106 64
            $prototype ? ", overwrites {$prototypeClass}, prototype {$prototypeClass}" : '',
107 64
            $this->isConstructor() ? ', ctor' : '',
108 64
            $this->isDestructor() ? ', dtor' : '',
109 64
            $this->isFinal() ? ' final' : '',
110 64
            $this->isStatic() ? ' static' : '',
111 64
            $this->isAbstract() ? ' abstract' : '',
112 64
            join(
113 64
                ' ',
114 64
                \Reflection::getModifierNames(
115 64
                    $this->getModifiers() & (self::IS_PUBLIC | self::IS_PROTECTED | self::IS_PRIVATE)
116
                )
117
            ),
118 64
            $this->getName(),
119 64
            $this->getFileName(),
120 64
            $this->getStartLine(),
121 64
            $this->getEndLine(),
122 64
            count($methodParameters),
123 64
            $paramString,
124 64
            $returnType ? ReflectionType::convertToDisplayType($returnType) : ''
125
        );
126
    }
127
128
    /**
129
     * {@inheritDoc}
130
     */
131 1
    public function getClosure($object)
132
    {
133 1
        $this->initializeInternalReflection();
134
135 1
        return parent::getClosure($object);
136
    }
137
138
    /**
139
     * {@inheritDoc}
140
     */
141 138
    public function getDeclaringClass()
142
    {
143 138
        return isset($this->declaringClass) ? $this->declaringClass : new ReflectionClass($this->className);
144
    }
145
146
    /**
147
     * {@inheritDoc}
148
     */
149 68
    public function getModifiers()
150
    {
151 68
        $modifiers = 0;
152 68
        if ($this->isPublic()) {
153 54
            $modifiers += self::IS_PUBLIC;
154
        }
155 68
        if ($this->isProtected()) {
156 9
            $modifiers += self::IS_PROTECTED;
157
        }
158 68
        if ($this->isPrivate()) {
159 9
            $modifiers += self::IS_PRIVATE;
160
        }
161 68
        if ($this->isAbstract()) {
162 9
            $modifiers += self::IS_ABSTRACT;
163
        }
164 68
        if ($this->isFinal()) {
165 6
            $modifiers += self::IS_FINAL;
166
        }
167 68
        if ($this->isStatic()) {
168 11
            $modifiers += self::IS_STATIC;
169
        }
170
171 68
        return $modifiers;
172
    }
173
174
    /**
175
     * {@inheritDoc}
176
     */
177 65
    public function getPrototype()
178
    {
179 65
        $parent = $this->getDeclaringClass()->getParentClass();
180 65
        if (!$parent) {
181 48
            throw new ReflectionException("No prototype");
182
        }
183
184 17
        $prototypeMethod = $parent->getMethod($this->getName());
185 2
        if (!$prototypeMethod) {
186
            throw new ReflectionException("No prototype");
187
        }
188
189 2
        return $prototypeMethod;
190
    }
191
192
    /**
193
     * {@inheritDoc}
194
     */
195 1
    public function invoke($object, $args = null)
196
    {
197 1
        $this->initializeInternalReflection();
198
199 1
        return call_user_func_array('parent::invoke', func_get_args());
200
    }
201
202
    /**
203
     * {@inheritDoc}
204
     */
205 3
    public function invokeArgs($object, array $args)
206
    {
207 3
        $this->initializeInternalReflection();
208
209 3
        return parent::invokeArgs($object, $args);
210
    }
211
212
    /**
213
     * {@inheritDoc}
214
     */
215 132
    public function isAbstract()
216
    {
217 132
        return $this->getDeclaringClass()->isInterface() || $this->getClassMethodNode()->isAbstract();
218
    }
219
220
    /**
221
     * {@inheritDoc}
222
     */
223 128
    public function isConstructor()
224
    {
225 128
        return $this->getClassMethodNode()->name == '__construct';
226
    }
227
228
    /**
229
     * {@inheritDoc}
230
     */
231 128
    public function isDestructor()
232
    {
233 128
        return $this->getClassMethodNode()->name == '__destruct';
234
    }
235
236
    /**
237
     * {@inheritDoc}
238
     */
239 132
    public function isFinal()
240
    {
241 132
        return $this->getClassMethodNode()->isFinal();
242
    }
243
244
    /**
245
     * {@inheritDoc}
246
     */
247 132
    public function isPrivate()
248
    {
249 132
        return $this->getClassMethodNode()->isPrivate();
250
    }
251
252
    /**
253
     * {@inheritDoc}
254
     */
255 132
    public function isProtected()
256
    {
257 132
        return $this->getClassMethodNode()->isProtected();
258
    }
259
260
    /**
261
     * {@inheritDoc}
262
     */
263 135
    public function isPublic()
264
    {
265 135
        return $this->getClassMethodNode()->isPublic();
266
    }
267
268
    /**
269
     * {@inheritDoc}
270
     */
271 132
    public function isStatic()
272
    {
273 132
        return $this->getClassMethodNode()->isStatic();
274
    }
275
276
    /**
277
     * {@inheritDoc}
278
     */
279 1
    public function setAccessible($accessible)
280
    {
281 1
        $this->initializeInternalReflection();
282
283 1
        parent::setAccessible($accessible);
284 1
    }
285
286
    /**
287
     * Parses methods from the concrete class node
288
     *
289
     * @param ClassLike $classLikeNode Class-like node
290
     * @param ReflectionClass $reflectionClass Reflection of the class
291
     *
292
     * @return array|ReflectionMethod[]
293
     */
294 60
    public static function collectFromClassNode(ClassLike $classLikeNode, ReflectionClass $reflectionClass)
295
    {
296 60
        $methods = [];
297
298 60
        foreach ($classLikeNode->stmts as $classLevelNode) {
299 57
            if ($classLevelNode instanceof ClassMethod) {
300 46
                $classLevelNode->setAttribute('fileName', $classLikeNode->getAttribute('fileName'));
301
302 46
                $methodName = $classLevelNode->name;
303 46
                $methods[$methodName] = new ReflectionMethod(
304 46
                    $reflectionClass->name,
305 46
                    $methodName,
306 46
                    $classLevelNode,
307 57
                    $reflectionClass
308
                );
309
            }
310
        }
311
312 60
        return $methods;
313
    }
314
315
    /**
316
     * Implementation of internal reflection initialization
317
     *
318
     * @return void
319
     */
320 69
    protected function __initialize()
321
    {
322 69
        parent::__construct($this->className, $this->getName());
323 69
    }
324
325
    /**
326
     * Returns ClassMethod node to prevent all possible type checks with instanceof
327
     *
328
     * @return ClassMethod
329
     */
330 581
    private function getClassMethodNode()
331
    {
332 581
        return $this->functionLikeNode;
333
    }
334
}
335