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

ReflectionClassLikeTrait   F

Complexity

Total Complexity 153

Size/Duplication

Total Lines 926
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 12

Test Coverage

Coverage 73.95%

Importance

Changes 13
Bugs 3 Features 4
Metric Value
wmc 153
c 13
b 3
f 4
lcom 2
cbo 12
dl 0
loc 926
ccs 298
cts 403
cp 0.7395
rs 3.4286

50 Methods

Rating   Name   Duplication   Size   Complexity  
D __toString() 0 89 16
A getConstant() 0 8 2
A getConstants() 0 14 2
A getConstructor() 0 9 2
C getDefaultProperties() 0 26 7
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 getInterfaceNames() 0 4 1
A getInterfaces() 0 13 3
A getMethod() 0 11 3
C getMethods() 0 31 8
C getModifiers() 0 27 7
A getName() 0 6 2
A getNamespaceName() 0 4 1
B getParentClass() 0 17 5
C getProperties() 0 33 8
A getProperty() 0 11 3
A getShortName() 0 4 1
A getStartLine() 0 4 1
B getTraitAliases() 0 20 5
A getTraitNames() 0 4 1
A getTraits() 0 10 2
A hasConstant() 0 7 2
A hasMethod() 0 11 3
A hasProperty() 0 11 3
A implementsInterface() 0 6 1
A inNamespace() 0 4 1
B isAbstract() 0 12 7
A isAnonymous() 0 4 1
B isCloneable() 0 12 5
A isFinal() 0 6 2
A isInstance() 0 10 3
B isInstantiable() 0 12 5
A isInterface() 0 4 1
A isInternal() 0 5 1
A isIterateable() 0 4 1
C isSubclassOf() 0 23 7
A isTrait() 0 4 1
A isUserDefined() 0 5 1
B getStaticProperties() 0 21 5
A getStaticPropertyValue() 0 11 4
A newInstance() 0 6 1
A newInstanceArgs() 0 7 1
A newInstanceWithoutConstructor() 0 7 1
A setStaticPropertyValue() 0 6 1
A recursiveCollect() 0 21 4
B findConstants() 0 20 5

How to fix   Complexity   

Complex Class

Complex classes like ReflectionClassLikeTrait 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 ReflectionClassLikeTrait, 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
use Go\ParserReflection\ReflectionClass;
14
use Go\ParserReflection\ReflectionException;
15
use Go\ParserReflection\ReflectionMethod;
16
use Go\ParserReflection\ReflectionProperty;
17
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
18
use PhpParser\Node\Name\FullyQualified;
19
use PhpParser\Node\Stmt\Class_;
20
use PhpParser\Node\Stmt\ClassConst;
21
use PhpParser\Node\Stmt\ClassLike;
22
use PhpParser\Node\Stmt\Interface_;
23
use PhpParser\Node\Stmt\Trait_;
24
use PhpParser\Node\Stmt\TraitUseAdaptation;
25
26
/**
27
 * General class-like reflection
28
 */
29
trait ReflectionClassLikeTrait
30
{
31
    use InitializationTrait;
32
33
    /**
34
     * @var ClassLike
35
     */
36
    protected $classLikeNode;
37
38
    /**
39
     * Short name of the class, without namespace
40
     *
41
     * @var string
42
     */
43
    protected $className;
44
45
    /**
46
     * List of all constants from the class
47
     *
48
     * @var array
49
     */
50
    protected $constants;
51
52
    /**
53
     * Interfaces, empty array or null if not initialized yet
54
     *
55
     * @var \ReflectionClass[]|array|null
56
     */
57
    protected $interfaceClasses;
58
59
    /**
60
     * List of traits, empty array or null if not initialized yet
61
     *
62
     * @var  \ReflectionClass[]|array|null
63
     */
64
    protected $traits;
65
66
    /**
67
     * Additional list of trait adaptations
68
     *
69
     * @var TraitUseAdaptation[]|array
70
     */
71
    protected $traitAdaptations;
72
73
    /**
74
     * @var array|ReflectionMethod[]
75
     */
76
    protected $methods;
77
78
    /**
79
     * Namespace name
80
     *
81
     * @var string
82
     */
83
    protected $namespaceName = '';
84
85
    /**
86
     * Parent class, or false if not present, null if uninitialized yet
87
     *
88
     * @var \ReflectionClass|false|null
89
     */
90
    protected $parentClass;
91
92
    /**
93
     * @var array|ReflectionProperty[]
94
     */
95
    protected $properties;
96
97
    /**
98
     * Returns the string representation of the ReflectionClass object.
99
     *
100
     * @return string
101
     */
102
    public function __toString()
103
    {
104
        $isObject = $this instanceof \ReflectionObject;
105
106
        $staticProperties = $staticMethods = $defaultProperties = $dynamicProperties = $methods = [];
107
108
        $format  = "%s [ <user> %sclass %s%s%s ] {\n";
109
        $format .= "  @@ %s %d-%d\n\n";
110
        $format .= "  - Constants [%d] {%s\n  }\n\n";
111
        $format .= "  - Static properties [%d] {%s\n  }\n\n";
112
        $format .= "  - Static methods [%d] {%s\n  }\n\n";
113
        $format .= "  - Properties [%d] {%s\n  }\n\n";
114
        $format .= ($isObject ? "  - Dynamic properties [%d] {%s\n  }\n\n" : '%s%s');
115
        $format .= "  - Methods [%d] {%s\n  }\n";
116
        $format .= "}\n";
117
118
        foreach ($this->getProperties() as $property) {
119
            if ($property->isStatic()) {
120
                $staticProperties[] = $property;
121
            } elseif ($property->isDefault()) {
122
                $defaultProperties[] = $property;
123
            } else {
124
                $dynamicProperties[] = $property;
125
            }
126
        }
127
128
        foreach ($this->getMethods() as $method) {
129
            if ($method->isStatic()) {
130
                $staticMethods[] = $method;
131
            } else {
132
                $methods[] = $method;
133
            }
134
        }
135
136
        $buildString = function (array $items, $indentLevel = 4) {
137
            if (!count($items)) {
138
                return '';
139
            }
140
            $indent = "\n" . str_repeat(' ', $indentLevel);
141
            return $indent . implode($indent, explode("\n", implode("\n", $items)));
142
        };
143
        $buildConstants = function (array $items, $indentLevel = 4) {
144
            $str = '';
145
            foreach ($items as $name => $value) {
146
                $str .= "\n" . str_repeat(' ', $indentLevel);
147
                $str .= sprintf(
148
                    'Constant [ %s %s ] { %s }',
149
                    gettype($value),
150
                    $name,
151
                    $value
152
                );
153
            }
154
            return $str;
155
        };
156
        $interfaceNames = $this->getInterfaceNames();
157
        $parentClass    = $this->getParentClass();
158
        $modifiers      = '';
159
        if ($this->isAbstract()) {
160
            $modifiers = 'abstract ';
161
        } elseif ($this->isFinal()) {
162
            $modifiers = 'final ';
163
        };
164
165
        $string = sprintf(
166
            $format,
167
            ($isObject ? 'Object of class' : 'Class'),
168
            $modifiers,
169
            $this->getName(),
170
            false !== $parentClass ? (' extends ' . $parentClass->getName()) : '',
171
            $interfaceNames ? (' implements ' . implode(', ', $interfaceNames)) : '',
172
            $this->getFileName(),
173
            $this->getStartLine(),
174
            $this->getEndLine(),
175
            count($this->getConstants()),
176
            $buildConstants($this->getConstants()),
177
            count($staticProperties),
178
            $buildString($staticProperties),
179
            count($staticMethods),
180
            $buildString($staticMethods),
181
            count($defaultProperties),
182
            $buildString($defaultProperties),
183
            $isObject ? count($dynamicProperties) : '',
184
            $isObject ? $buildString($dynamicProperties) : '',
185
            count($methods),
186
            $buildString($methods)
187
        );
188
189
        return $string;
190
    }
191
192
193
    /**
194
     * {@inheritDoc}
195
     */
196 4
    public function getConstant($name)
197
    {
198 4
        if ($this->hasConstant($name)) {
199 4
            return $this->constants[$name];
200
        }
201
202
        return false;
203
    }
204
205
    /**
206
     * {@inheritDoc}
207
     */
208 23
    public function getConstants()
209
    {
210 23
        if (!isset($this->constants)) {
211 23
            $directConstants = $this->findConstants();
212
            $parentConstants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
213 9
                $result += $instance->getConstants();
214 23
            });
215 23
            $constants = $directConstants + $parentConstants;
216
217 23
            $this->constants = $constants;
218 23
        }
219
220 23
        return $this->constants;
221
    }
222
223
    /**
224
     * {@inheritDoc}
225
     */
226 13
    public function getConstructor()
227
    {
228 13
        $constructor = $this->getMethod('__construct');
229 13
        if (!$constructor) {
230 11
            return null;
231
        }
232
233 2
        return $constructor;
234
    }
235
236
    /**
237
     * Gets default properties from a class (including inherited properties).
238
     *
239
     * @link http://php.net/manual/en/reflectionclass.getdefaultproperties.php
240
     *
241
     * @return array An array of default properties, with the key being the name of the property and the value being
242
     * the default value of the property or NULL if the property doesn't have a default value
243
     */
244 22
    public function getDefaultProperties()
245
    {
246 22
        $defaultValues = [];
247 22
        $properties    = $this->getProperties();
248 22
        $staticOrder   = [true, false];
249 22
        foreach ($staticOrder as $shouldBeStatic) {
250 22
            foreach ($properties as $property) {
251 7
                $isStaticProperty     = $property->isStatic();
252 7
                if ($shouldBeStatic !== $isStaticProperty) {
253 7
                    continue;
254
                }
255 7
                $propertyName         = $property->getName();
256 7
                $isInternalReflection = get_class($property) == \ReflectionProperty::class;
257
258 7
                if (!$isInternalReflection || $isStaticProperty) {
259 7
                    $defaultValues[$propertyName] = $property->getValue();
260 7
                } elseif (!$isStaticProperty) {
261
                    // Internal reflection and dynamic property
262 1
                    $classProperties = $property->getDeclaringClass()->getDefaultProperties();
263 1
                    $defaultValues[$propertyName] = $classProperties[$propertyName];
264 1
                }
265 22
            }
266 22
        }
267
268 22
        return $defaultValues;
269
    }
270
271
    /**
272
     * {@inheritDoc}
273
     */
274 22
    public function getDocComment()
275
    {
276 22
        $docComment = $this->classLikeNode->getDocComment();
277
278 22
        return $docComment ? $docComment->getText() : false;
279
    }
280
281 22
    public function getEndLine()
282
    {
283 22
        return $this->classLikeNode->getAttribute('endLine');
284
    }
285
286 22
    public function getExtension()
287
    {
288 22
        return null;
289
    }
290
291 22
    public function getExtensionName()
292
    {
293 22
        return false;
294
    }
295
296 7
    public function getFileName()
297
    {
298 7
        return $this->classLikeNode->getAttribute('fileName');
299
    }
300
301
    /**
302
     * {@inheritDoc}
303
     */
304 22
    public function getInterfaceNames()
305
    {
306 22
        return array_keys($this->getInterfaces());
307
    }
308
309
    /**
310
     * {@inheritDoc}
311
     */
312 22
    public function getInterfaces()
313
    {
314 22
        if (!isset($this->interfaceClasses)) {
315
            $this->interfaceClasses = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
316 9
                if ($instance->isInterface()) {
317 2
                    $result[$instance->name] = $instance;
318 2
                }
319 9
                $result += $instance->getInterfaces();
320 22
            });
321 22
        }
322
323 22
        return $this->interfaceClasses;
324
    }
325
326
    /**
327
     * {@inheritdoc}
328
     */
329 16
    public function getMethod($name)
330
    {
331 16
        $methods = $this->getMethods();
332 16
        foreach ($methods as $method) {
333 10
            if ($method->getName() == $name) {
334 6
                return $method;
335
            }
336 12
        }
337
338 11
        return false;
339
    }
340
341
    /**
342
     * Returns list of reflection methods
343
     *
344
     * @param null|integer $filter Optional filter
345
     *
346
     * @return array|\ReflectionMethod[]
347
     */
348 61
    public function getMethods($filter = null)
349
    {
350 61
        if (!isset($this->methods)) {
351 61
            $directMethods = ReflectionMethod::collectFromClassNode($this->classLikeNode, $this->getName());
352
            $parentMethods = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
353 23
                $reflectionMethods = [];
354 23
                foreach ($instance->getMethods() as $reflectionMethod) {
355 13
                    if (!$isParent || !$reflectionMethod->isPrivate()) {
356 13
                        $reflectionMethods[$reflectionMethod->name] = $reflectionMethod;
357 13
                    }
358 23
                }
359 23
                $result += $reflectionMethods;
360 61
            });
361 61
            $methods = $directMethods + $parentMethods;
362
363 61
            $this->methods = $methods;
364 61
        }
365 61
        if (!isset($filter)) {
366 42
            return array_values($this->methods);
367
        }
368
369 19
        $methods = [];
370 19
        foreach ($this->methods as $method) {
371 12
            if (!($filter & $method->getModifiers())) {
372 10
                continue;
373
            }
374 4
            $methods[] = $method;
375 19
        }
376
377 19
        return $methods;
378
    }
379
380
    /**
381
     * Returns a bitfield of the access modifiers for this class.
382
     *
383
     * @link http://php.net/manual/en/reflectionclass.getmodifiers.php
384
     *
385
     * NB: this method is not fully compatible with original value because of hidden internal constants
386
     *
387
     * @return int
388
     */
389 21
    public function getModifiers()
390
    {
391 21
        $modifiers = 0;
392
393 21
        if ($this->isFinal()) {
394 1
            $modifiers += \ReflectionClass::IS_FINAL;
395 1
        }
396
397 21
        if ($this->isTrait()) {
398 2
            $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
399 2
        }
400
401 21
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
402 4
            $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
403 4
        }
404
405 21
        if ($this->isInterface()) {
406 3
            $abstractMethods = $this->getMethods();
407 3
        } else {
408 18
            $abstractMethods = $this->getMethods(\ReflectionMethod::IS_ABSTRACT);
409
        }
410 21
        if (!empty($abstractMethods)) {
411 5
            $modifiers += \ReflectionClass::IS_IMPLICIT_ABSTRACT;
412 5
        }
413
414 21
        return $modifiers;
415
    }
416
417
    /**
418
     * {@inheritDoc}
419
     */
420 96
    public function getName()
421
    {
422 96
        $namespaceName = $this->namespaceName ? $this->namespaceName . '\\' : '';
423
424 96
        return $namespaceName . $this->getShortName();
425
    }
426
427
    /**
428
     * {@inheritDoc}
429
     */
430 25
    public function getNamespaceName()
431
    {
432 25
        return $this->namespaceName;
433
    }
434
435
    /**
436
     * {@inheritDoc}
437
     */
438 91
    public function getParentClass()
439
    {
440 91
        if (!isset($this->parentClass)) {
441 91
            static $extendsField = 'extends';
442
443 91
            $parentClass = false;
444 91
            $hasExtends  = in_array($extendsField, $this->classLikeNode->getSubNodeNames());
445 91
            $extendsNode = $hasExtends ? $this->classLikeNode->$extendsField : null;
446 91
            if ($extendsNode instanceof FullyQualified) {
447 14
                $extendsName = $extendsNode->toString();
448 14
                $parentClass = class_exists($extendsName, false) ? new parent($extendsName) : new static($extendsName);
0 ignored issues
show
Unused Code introduced by
The call to ReflectionClassLikeTrait::__construct() has too many arguments starting with $extendsName.

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...
449 14
            }
450 91
            $this->parentClass = $parentClass;
451 91
        }
452
453 91
        return $this->parentClass;
454
    }
455
456
    /**
457
     * Retrieves reflected properties.
458
     *
459
     * @param int $filter The optional filter, for filtering desired property types.
460
     *                    It's configured using the ReflectionProperty constants, and defaults to all property types.
461
     *
462
     * @return array|\Go\ParserReflection\ReflectionProperty[]
463
     */
464 46
    public function getProperties($filter = null)
465
    {
466 46
        if (!isset($this->properties)) {
467 46
            $directProperties = ReflectionProperty::collectFromClassNode($this->classLikeNode, $this->getName());
468 46
            $parentProperties = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
469 17
                $reflectionProperties = [];
470 17
                foreach ($instance->getProperties() as $reflectionProperty) {
471 4
                    if (!$isParent || !$reflectionProperty->isPrivate()) {
472 4
                        $reflectionProperties[$reflectionProperty->name] = $reflectionProperty;
473 4
                    }
474 17
                }
475 17
                $result += $reflectionProperties;
476 46
            });
477 46
            $properties = $directProperties + $parentProperties;
478
479 46
            $this->properties = $properties;
480 46
        }
481
482
        // Without filter we can just return the full list
483 46
        if (!isset($filter)) {
484 46
            return array_values($this->properties);
485
        }
486
487 22
        $properties = [];
488 22
        foreach ($this->properties as $property) {
489 7
            if (!($filter & $property->getModifiers())) {
490 5
                continue;
491
            }
492 4
            $properties[] = $property;
493 22
        }
494
495 22
        return $properties;
496
    }
497
498
    /**
499
     * {@inheritdoc}
500
     */
501 3
    public function getProperty($name)
502
    {
503 3
        $properties = $this->getProperties();
504 3
        foreach ($properties as $property) {
505 3
            if ($property->getName() == $name) {
506 3
                return $property;
507
            }
508 3
        }
509
510
        return false;
511
    }
512
513
    /**
514
     * {@inheritDoc}
515
     */
516 96
    public function getShortName()
517
    {
518 96
        return $this->className;
519
    }
520
521 22
    public function getStartLine()
522
    {
523 22
        return $this->classLikeNode->getAttribute('startLine');
524
    }
525
526
    /**
527
     * Returns an array of trait aliases
528
     *
529
     * @link http://php.net/manual/en/reflectionclass.gettraitaliases.php
530
     *
531
     * @return array|null an array with new method names in keys and original names (in the format "TraitName::original") in
532
     * values.
533
     */
534 41
    public function getTraitAliases()
535
    {
536 41
        $aliases = [];
537 41
        $traits  = $this->getTraits();
538 41
        foreach ($this->traitAdaptations as $adaptation) {
539 2
            if ($adaptation instanceof TraitUseAdaptation\Alias) {
540 2
                $methodName = $adaptation->method;
541 2
                $traitName  = null;
542 2
                foreach ($traits as $trait) {
543 2
                    if ($trait->hasMethod($methodName)) {
544 2
                        $traitName = $trait->getName();
545 2
                        break;
546
                    }
547 2
                }
548 2
                $aliases[$adaptation->newName] = $traitName . '::'. $methodName;
549 2
            }
550 41
        }
551
552 41
        return $aliases;
553
    }
554
555
    /**
556
     * Returns an array of names of traits used by this class
557
     *
558
     * @link http://php.net/manual/en/reflectionclass.gettraitnames.php
559
     *
560
     * @return array
561
     */
562 22
    public function getTraitNames()
563
    {
564 22
        return array_keys($this->getTraits());
565
    }
566
567
    /**
568
     * Returns an array of traits used by this class
569
     *
570
     * @link http://php.net/manual/en/reflectionclass.gettraits.php
571
     *
572
     * @return array|\ReflectionClass[]
573
     */
574 91
    public function getTraits()
575
    {
576 91
        if (!isset($this->traits)) {
577 91
            $traitAdaptations = [];
578 91
            $this->traits     = ReflectionClass::collectTraitsFromClassNode($this->classLikeNode, $traitAdaptations);
579 91
            $this->traitAdaptations = $traitAdaptations;
580 91
        }
581
582 91
        return $this->traits;
583
    }
584
585
    /**
586
     * {@inheritDoc}
587
     */
588 4
    public function hasConstant($name)
589
    {
590 4
        $constants   = $this->getConstants();
591 4
        $hasConstant = isset($constants[$name]) || array_key_exists($constants, $name);
592
593 4
        return $hasConstant;
594
    }
595
596
    /**
597
     * {@inheritdoc}
598
     */
599 15
    public function hasMethod($name)
600
    {
601 15
        $methods = $this->getMethods();
602 15
        foreach ($methods as $method) {
603 9
            if ($method->getName() == $name) {
604 3
                return true;
605
            }
606 12
        }
607
608 12
        return false;
609
    }
610
611
    /**
612
     * {@inheritdoc}
613
     */
614
    public function hasProperty($name)
615
    {
616
        $properties = $this->getProperties();
617
        foreach ($properties as $property) {
618
            if ($property->getName() == $name) {
619
                return true;
620
            }
621
        }
622
623
        return false;
624
    }
625
626
    /**
627
     * {@inheritDoc}
628
     */
629 22
    public function implementsInterface($interfaceName)
630
    {
631 22
        $allInterfaces = $this->getInterfaces();
632
633 22
        return isset($allInterfaces[$interfaceName]);
634
    }
635
636
    /**
637
     * {@inheritDoc}
638
     */
639 22
    public function inNamespace()
640
    {
641 22
        return !empty($this->namespaceName);
642
    }
643
644
    /**
645
     * {@inheritDoc}
646
     */
647 22
    public function isAbstract()
648
    {
649 22
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
650 4
            return true;
651 18
        } elseif ($this->isInterface() && !empty($this->getMethods())) {
652 2
            return true;
653 16
        } elseif ($this->isTrait()) {
654 2
            return PHP_VERSION_ID < 70000 ? true : false;
655
        }
656
657 14
        return false;
658
    }
659
660
    /**
661
     * Currently, anonymous classes aren't supported for parsed reflection
662
     */
663
    public function isAnonymous()
664
    {
665
        return false;
666
    }
667
668
    /**
669
     * {@inheritDoc}
670
     */
671 22
    public function isCloneable()
672
    {
673 22
        if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
674 9
            return false;
675
        }
676
677 13
        if ($this->hasMethod('__clone')) {
678 1
            return $this->getMethod('__clone')->isPublic();
679
        }
680
681 12
        return true;
682
    }
683
684
    /**
685
     * {@inheritDoc}
686
     */
687 43
    public function isFinal()
688
    {
689 43
        $isFinal = $this->classLikeNode instanceof Class_ && $this->classLikeNode->isFinal();
690
691 43
        return $isFinal;
692
    }
693
694
    /**
695
     * {@inheritDoc}
696
     */
697
    public function isInstance($object)
698
    {
699
        if (!is_object($object)) {
700
            throw new \RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)));
701
        }
702
703
        $className = $this->getName();
704
705
        return $className === get_class($object) || is_subclass_of($object, $className);
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $className can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
706
    }
707
708
    /**
709
     * {@inheritDoc}
710
     */
711 22
    public function isInstantiable()
712
    {
713 22
        if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
714 9
            return false;
715
        }
716
717 13
        if (null === ($constructor = $this->getConstructor())) {
718 11
            return true;
719
        }
720
721 2
        return $constructor->isPublic();
722
    }
723
724
    /**
725
     * {@inheritDoc}
726
     */
727 43
    public function isInterface()
728
    {
729 43
        return ($this->classLikeNode instanceof Interface_);
730
    }
731
732
    /**
733
     * {@inheritDoc}
734
     */
735 22
    public function isInternal()
736
    {
737
        // never can be an internal method
738 22
        return false;
739
    }
740
741
    /**
742
     * {@inheritDoc}
743
     */
744 22
    public function isIterateable()
745
    {
746 22
        return $this->implementsInterface('Traversable');
747
    }
748
749
    /**
750
     * {@inheritDoc}
751
     */
752
    public function isSubclassOf($class)
753
    {
754
        if (is_object($class)) {
755
            if ($class instanceof ReflectionClass) {
756
                $class = $class->name;
757
            } else {
758
                $class = get_class($class);
759
            }
760
        }
761
762
        if (!$this->classLikeNode instanceof Class_) {
763
            return false;
764
        } else {
765
            $extends = $this->classLikeNode->extends;
766
            if ($extends && $extends->toString() == $class) {
767
                return true;
768
            }
769
        }
770
771
        $parent = $this->getParentClass();
772
773
        return false === $parent ? false : $parent->isSubclassOf($class);
774
    }
775
776
    /**
777
     * {@inheritDoc}
778
     */
779 43
    public function isTrait()
780
    {
781 43
        return ($this->classLikeNode instanceof Trait_);
782
    }
783
784
    /**
785
     * {@inheritDoc}
786
     */
787 22
    public function isUserDefined()
788
    {
789
        // always defined by user, because we parse the source code
790 22
        return true;
791
    }
792
793
    /**
794
     * Gets static properties
795
     *
796
     * @link http://php.net/manual/en/reflectionclass.getstaticproperties.php
797
     *
798
     * @return array
799
     */
800 23
    public function getStaticProperties()
801
    {
802
        // In runtime static properties can be changed in any time
803 23
        if ($this->isInitialized()) {
804 1
            return forward_static_call('parent::getStaticProperties');
805
        }
806
807 22
        $properties = [];
808
809 22
        $reflectionProperties = $this->getProperties(ReflectionProperty::IS_STATIC);
810 22
        foreach ($reflectionProperties as $reflectionProperty) {
811 4
            if (!$reflectionProperty instanceof ReflectionProperty) {
812 1
                if (!$reflectionProperty->isPublic()) {
813 1
                    $reflectionProperty->setAccessible(true);
814 1
                }
815 1
            }
816 4
            $properties[$reflectionProperty->getName()] = $reflectionProperty->getValue();
817 22
        }
818
819 22
        return $properties;
820
    }
821
822
    /**
823
     * Gets static property value
824
     *
825
     * @param string $name    The name of the static property for which to return a value.
826
     * @param mixed  $default A default value to return in case the class does not declare
827
     *                        a static property with the given name
828
     *
829
     * @return mixed
830
     * @throws ReflectionException If there is no such property and no default value was given
831
     */
832 1
    public function getStaticPropertyValue($name, $default = null)
833
    {
834 1
        $properties     = $this->getStaticProperties();
835 1
        $propertyExists = array_key_exists($name, $properties);
836
837 1
        if (!$propertyExists && func_num_args() === 1) {
838
            throw new ReflectionException("Static property does not exist and no default value is given");
839
        }
840
841 1
        return $propertyExists ? $properties[$name] : $default;
842
    }
843
844
845
    /**
846
     * Creates a new class instance from given arguments.
847
     *
848
     * @link http://php.net/manual/en/reflectionclass.newinstance.php
849
     * @param mixed $args Accepts a variable number of arguments which are passed to the class constructor
850
     *
851
     * @return object
852
     */
853 1
    public function newInstance($args = null)
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
854
    {
855 1
        $this->initializeInternalReflection();
856
857 1
        return call_user_func_array('parent::newInstance', func_get_args());
858
    }
859
860
    /**
861
     * Creates a new class instance from given arguments.
862
     *
863
     * @link http://php.net/manual/en/reflectionclass.newinstanceargs.php
864
     *
865
     * @param array $args The parameters to be passed to the class constructor as an array.
866
     *
867
     * @return object
868
     */
869 1
    public function newInstanceArgs(array $args = [])
870
    {
871 1
        $function = __FUNCTION__;
872 1
        $this->initializeInternalReflection();
873
874 1
        return parent::$function($args);
875
    }
876
877
    /**
878
     * Creates a new class instance without invoking the constructor.
879
     *
880
     * @link http://php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php
881
     *
882
     * @return object
883
     */
884 1
    public function newInstanceWithoutConstructor($args = null)
885
    {
886 1
        $function = __FUNCTION__;
887 1
        $this->initializeInternalReflection();
888
889 1
        return parent::$function($args);
890
    }
891
892
    /**
893
     * Sets static property value
894
     *
895
     * @link http://php.net/manual/en/reflectionclass.setstaticpropertyvalue.php
896
     *
897
     * @param string $name Property name
898
     * @param mixed $value New property value
899
     */
900 1
    public function setStaticPropertyValue($name, $value)
901
    {
902 1
        $this->initializeInternalReflection();
903
904 1
        forward_static_call('parent::setStaticPropertyValue', $name, $value);
905 1
    }
906
907 91
    private function recursiveCollect(\Closure $collector)
908
    {
909 91
        $result = array();
910
911 91
        $traits = $this->getTraits();
912 91
        foreach ($traits as $trait) {
913 14
            $collector($result, $trait, $isParent = false);
914 91
        }
915
916 91
        $parentClass = $this->getParentClass();
917 91
        if ($parentClass) {
918 14
            $collector($result, $parentClass, $isParent = true);
919 14
        }
920
921 91
        $interfaces = ReflectionClass::collectInterfacesFromClassNode($this->classLikeNode);
922 91
        foreach ($interfaces as $interface) {
923 8
            $collector($result, $interface, $isParent = true);
924 91
        }
925
926 91
        return $result;
927
    }
928
929
    /**
930
     * Returns list of constants from the class
931
     *
932
     * @return array
933
     */
934 23
    private function findConstants()
935
    {
936 23
        $constants        = array();
937 23
        $expressionSolver = new NodeExpressionResolver($this);
938
939
        // constants can be only top-level nodes in the class, so we can scan them directly
940 23
        foreach ($this->classLikeNode->stmts as $classLevelNode) {
941 19
            if ($classLevelNode instanceof ClassConst) {
942 5
                $nodeConstants = $classLevelNode->consts;
943 5
                if (!empty($nodeConstants)) {
944 5
                    foreach ($nodeConstants as $nodeConstant) {
945 5
                        $expressionSolver->process($nodeConstant->value);
946 5
                        $constants[$nodeConstant->name] = $expressionSolver->getValue();
947 5
                    }
948 5
                }
949 5
            }
950 23
        }
951
952 23
        return $constants;
953
    }
954
}
955