Completed
Push — master ( 50a0b2...b57357 )
by Alexander
02:06
created

ReflectionClassLikeTrait   F

Complexity

Total Complexity 155

Size/Duplication

Total Lines 925
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 12

Test Coverage

Coverage 77.01%

Importance

Changes 0
Metric Value
wmc 155
lcom 2
cbo 12
dl 0
loc 925
ccs 258
cts 335
cp 0.7701
rs 3.4285
c 0
b 0
f 0

50 Methods

Rating   Name   Duplication   Size   Complexity  
A getConstant() 0 8 2
D __toString() 0 89 16
A getConstants() 0 13 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 8
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
B recursiveCollect() 0 22 4
B findConstants() 0 19 6

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 6
    public function getConstant($name)
197
    {
198 6
        if ($this->hasConstant($name)) {
199 6
            return $this->constants[$name];
200
        }
201
202
        return false;
203
    }
204
205
    /**
206
     * {@inheritDoc}
207
     */
208 26
    public function getConstants()
209
    {
210 26
        if (!isset($this->constants)) {
211 26
            $directConstants = $this->findConstants();
212
            $parentConstants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
213 11
                $result += $instance->getConstants();
214 26
            });
215
216 26
            $this->constants = $directConstants + $parentConstants;
217
        }
218
219 26
        return $this->constants;
220
    }
221
222
    /**
223
     * {@inheritDoc}
224
     */
225 13
    public function getConstructor()
226
    {
227 13
        $constructor = $this->getMethod('__construct');
228 13
        if (!$constructor) {
229 11
            return null;
230
        }
231
232 2
        return $constructor;
233
    }
234
235
    /**
236
     * Gets default properties from a class (including inherited properties).
237
     *
238
     * @link http://php.net/manual/en/reflectionclass.getdefaultproperties.php
239
     *
240
     * @return array An array of default properties, with the key being the name of the property and the value being
241
     * the default value of the property or NULL if the property doesn't have a default value
242
     */
243 22
    public function getDefaultProperties()
244
    {
245 22
        $defaultValues = [];
246 22
        $properties    = $this->getProperties();
247 22
        $staticOrder   = [true, false];
248 22
        foreach ($staticOrder as $shouldBeStatic) {
249 22
            foreach ($properties as $property) {
250 7
                $isStaticProperty     = $property->isStatic();
251 7
                if ($shouldBeStatic !== $isStaticProperty) {
252 7
                    continue;
253
                }
254 7
                $propertyName         = $property->getName();
255 7
                $isInternalReflection = get_class($property) == \ReflectionProperty::class;
256
257 7
                if (!$isInternalReflection || $isStaticProperty) {
258 7
                    $defaultValues[$propertyName] = $property->getValue();
259 1
                } elseif (!$isStaticProperty) {
260
                    // Internal reflection and dynamic property
261 1
                    $classProperties = $property->getDeclaringClass()->getDefaultProperties();
262 22
                    $defaultValues[$propertyName] = $classProperties[$propertyName];
263
                }
264
            }
265
        }
266
267 22
        return $defaultValues;
268
    }
269
270
    /**
271
     * {@inheritDoc}
272
     */
273 22
    public function getDocComment()
274
    {
275 22
        $docComment = $this->classLikeNode->getDocComment();
276
277 22
        return $docComment ? $docComment->getText() : false;
278
    }
279
280 22
    public function getEndLine()
281
    {
282 22
        return $this->classLikeNode->getAttribute('endLine');
283
    }
284
285 22
    public function getExtension()
286
    {
287 22
        return null;
288
    }
289
290 22
    public function getExtensionName()
291
    {
292 22
        return false;
293
    }
294
295 11
    public function getFileName()
296
    {
297 11
        return $this->classLikeNode->getAttribute('fileName');
298
    }
299
300
    /**
301
     * {@inheritDoc}
302
     */
303 22
    public function getInterfaceNames()
304
    {
305 22
        return array_keys($this->getInterfaces());
306
    }
307
308
    /**
309
     * {@inheritDoc}
310
     */
311 22
    public function getInterfaces()
312
    {
313 22
        if (!isset($this->interfaceClasses)) {
314
            $this->interfaceClasses = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
315 9
                if ($instance->isInterface()) {
316 2
                    $result[$instance->name] = $instance;
317
                }
318 9
                $result += $instance->getInterfaces();
319 22
            });
320
        }
321
322 22
        return $this->interfaceClasses;
323
    }
324
325
    /**
326
     * {@inheritdoc}
327
     */
328 24
    public function getMethod($name)
329
    {
330 24
        $methods = $this->getMethods();
331 24
        foreach ($methods as $method) {
332 18
            if ($method->getName() == $name) {
333 18
                return $method;
334
            }
335
        }
336
337 11
        return false;
338
    }
339
340
    /**
341
     * Returns list of reflection methods
342
     *
343
     * @param null|integer $filter Optional filter
344
     *
345
     * @return array|\ReflectionMethod[]
346
     */
347 69
    public function getMethods($filter = null)
348
    {
349 69
        if (!isset($this->methods)) {
350 69
            $directMethods = ReflectionMethod::collectFromClassNode($this->classLikeNode, $this);
351
            $parentMethods = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
352 30
                $reflectionMethods = [];
353 30
                foreach ($instance->getMethods() as $reflectionMethod) {
354 20
                    if (!$isParent || !$reflectionMethod->isPrivate()) {
355 20
                        $reflectionMethods[$reflectionMethod->name] = $reflectionMethod;
356
                    }
357
                }
358 30
                $result += $reflectionMethods;
359 69
            });
360 69
            $methods = $directMethods + $parentMethods;
361
362 69
            $this->methods = $methods;
363
        }
364 69
        if (!isset($filter)) {
365 50
            return array_values($this->methods);
366
        }
367
368 19
        $methods = [];
369 19
        foreach ($this->methods as $method) {
370 12
            if (!($filter & $method->getModifiers())) {
371 10
                continue;
372
            }
373 4
            $methods[] = $method;
374
        }
375
376 19
        return $methods;
377
    }
378
379
    /**
380
     * Returns a bitfield of the access modifiers for this class.
381
     *
382
     * @link http://php.net/manual/en/reflectionclass.getmodifiers.php
383
     *
384
     * NB: this method is not fully compatible with original value because of hidden internal constants
385
     *
386
     * @return int
387
     */
388 21
    public function getModifiers()
389
    {
390 21
        $modifiers = 0;
391
392 21
        if ($this->isFinal()) {
393 1
            $modifiers += \ReflectionClass::IS_FINAL;
394
        }
395
396 21
        if (PHP_VERSION_ID < 70000 && $this->isTrait()) {
397
            $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
398
        }
399
400 21
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
401 4
            $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
402
        }
403
404 21
        if ($this->isInterface()) {
405 3
            $abstractMethods = $this->getMethods();
406
        } else {
407 18
            $abstractMethods = $this->getMethods(\ReflectionMethod::IS_ABSTRACT);
408
        }
409 21
        if (!empty($abstractMethods)) {
410 5
            $modifiers += \ReflectionClass::IS_IMPLICIT_ABSTRACT;
411
        }
412
413 21
        return $modifiers;
414
    }
415
416
    /**
417
     * {@inheritDoc}
418
     */
419 79
    public function getName()
420
    {
421 79
        $namespaceName = $this->namespaceName ? $this->namespaceName . '\\' : '';
422
423 79
        return $namespaceName . $this->getShortName();
424
    }
425
426
    /**
427
     * {@inheritDoc}
428
     */
429 29
    public function getNamespaceName()
430
    {
431 29
        return $this->namespaceName;
432
    }
433
434
    /**
435
     * {@inheritDoc}
436
     */
437 101
    public function getParentClass()
438
    {
439 101
        if (!isset($this->parentClass)) {
440 101
            static $extendsField = 'extends';
441
442 101
            $parentClass = false;
443 101
            $hasExtends  = in_array($extendsField, $this->classLikeNode->getSubNodeNames());
444 101
            $extendsNode = $hasExtends ? $this->classLikeNode->$extendsField : null;
445 101
            if ($extendsNode instanceof FullyQualified) {
446 22
                $extendsName = $extendsNode->toString();
447 22
                $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...
448
            }
449 101
            $this->parentClass = $parentClass;
450
        }
451
452 101
        return $this->parentClass;
453
    }
454
455
    /**
456
     * Retrieves reflected properties.
457
     *
458
     * @param int $filter The optional filter, for filtering desired property types.
459
     *                    It's configured using the ReflectionProperty constants, and defaults to all property types.
460
     *
461
     * @return array|\Go\ParserReflection\ReflectionProperty[]
462
     */
463 46
    public function getProperties($filter = null)
464
    {
465 46
        if (!isset($this->properties)) {
466 46
            $directProperties = ReflectionProperty::collectFromClassNode($this->classLikeNode, $this->getName());
467 46
            $parentProperties = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
468 17
                $reflectionProperties = [];
469 17
                foreach ($instance->getProperties() as $reflectionProperty) {
470 4
                    if (!$isParent || !$reflectionProperty->isPrivate()) {
471 4
                        $reflectionProperties[$reflectionProperty->name] = $reflectionProperty;
472
                    }
473
                }
474 17
                $result += $reflectionProperties;
475 46
            });
476 46
            $properties = $directProperties + $parentProperties;
477
478 46
            $this->properties = $properties;
479
        }
480
481
        // Without filter we can just return the full list
482 46
        if (!isset($filter)) {
483 46
            return array_values($this->properties);
484
        }
485
486 22
        $properties = [];
487 22
        foreach ($this->properties as $property) {
488 7
            if (!($filter & $property->getModifiers())) {
489 5
                continue;
490
            }
491 4
            $properties[] = $property;
492
        }
493
494 22
        return $properties;
495
    }
496
497
    /**
498
     * {@inheritdoc}
499
     */
500 3
    public function getProperty($name)
501
    {
502 3
        $properties = $this->getProperties();
503 3
        foreach ($properties as $property) {
504 3
            if ($property->getName() == $name) {
505 3
                return $property;
506
            }
507
        }
508
509
        return false;
510
    }
511
512
    /**
513
     * {@inheritDoc}
514
     */
515 79
    public function getShortName()
516
    {
517 79
        return $this->className;
518
    }
519
520 22
    public function getStartLine()
521
    {
522 22
        return $this->classLikeNode->getAttribute('startLine');
523
    }
524
525
    /**
526
     * Returns an array of trait aliases
527
     *
528
     * @link http://php.net/manual/en/reflectionclass.gettraitaliases.php
529
     *
530
     * @return array|null an array with new method names in keys and original names (in the format "TraitName::original") in
531
     * values.
532
     */
533 41
    public function getTraitAliases()
534
    {
535 41
        $aliases = [];
536 41
        $traits  = $this->getTraits();
537 41
        foreach ($this->traitAdaptations as $adaptation) {
538 2
            if ($adaptation instanceof TraitUseAdaptation\Alias) {
539 2
                $methodName = $adaptation->method;
540 2
                $traitName  = null;
541 2
                foreach ($traits as $trait) {
542 2
                    if ($trait->hasMethod($methodName)) {
543 2
                        $traitName = $trait->getName();
544 2
                        break;
545
                    }
546
                }
547 2
                $aliases[$adaptation->newName] = $traitName . '::'. $methodName;
548
            }
549
        }
550
551 41
        return $aliases;
552
    }
553
554
    /**
555
     * Returns an array of names of traits used by this class
556
     *
557
     * @link http://php.net/manual/en/reflectionclass.gettraitnames.php
558
     *
559
     * @return array
560
     */
561 22
    public function getTraitNames()
562
    {
563 22
        return array_keys($this->getTraits());
564
    }
565
566
    /**
567
     * Returns an array of traits used by this class
568
     *
569
     * @link http://php.net/manual/en/reflectionclass.gettraits.php
570
     *
571
     * @return array|\ReflectionClass[]
572
     */
573 101
    public function getTraits()
574
    {
575 101
        if (!isset($this->traits)) {
576 101
            $traitAdaptations = [];
577 101
            $this->traits     = ReflectionClass::collectTraitsFromClassNode($this->classLikeNode, $traitAdaptations);
578 101
            $this->traitAdaptations = $traitAdaptations;
579
        }
580
581 101
        return $this->traits;
582
    }
583
584
    /**
585
     * {@inheritDoc}
586
     */
587 7
    public function hasConstant($name)
588
    {
589 7
        $constants   = $this->getConstants();
590 7
        $hasConstant = isset($constants[$name]) || array_key_exists($name, $constants);
591
592 7
        return $hasConstant;
593
    }
594
595
    /**
596
     * {@inheritdoc}
597
     */
598 15
    public function hasMethod($name)
599
    {
600 15
        $methods = $this->getMethods();
601 15
        foreach ($methods as $method) {
602 9
            if ($method->getName() == $name) {
603 9
                return true;
604
            }
605
        }
606
607 12
        return false;
608
    }
609
610
    /**
611
     * {@inheritdoc}
612
     */
613
    public function hasProperty($name)
614
    {
615
        $properties = $this->getProperties();
616
        foreach ($properties as $property) {
617
            if ($property->getName() == $name) {
618
                return true;
619
            }
620
        }
621
622
        return false;
623
    }
624
625
    /**
626
     * {@inheritDoc}
627
     */
628 22
    public function implementsInterface($interfaceName)
629
    {
630 22
        $allInterfaces = $this->getInterfaces();
631
632 22
        return isset($allInterfaces[$interfaceName]);
633
    }
634
635
    /**
636
     * {@inheritDoc}
637
     */
638 22
    public function inNamespace()
639
    {
640 22
        return !empty($this->namespaceName);
641
    }
642
643
    /**
644
     * {@inheritDoc}
645
     */
646 22
    public function isAbstract()
647
    {
648 22
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
649 4
            return true;
650 18
        } elseif ($this->isInterface() && !empty($this->getMethods())) {
651 2
            return true;
652 16
        } elseif ($this->isTrait()) {
653 2
            return PHP_VERSION_ID < 70000 ? true : false;
654
        }
655
656 14
        return false;
657
    }
658
659
    /**
660
     * Currently, anonymous classes aren't supported for parsed reflection
661
     */
662
    public function isAnonymous()
663
    {
664
        return false;
665
    }
666
667
    /**
668
     * {@inheritDoc}
669
     */
670 22
    public function isCloneable()
671
    {
672 22
        if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
673 9
            return false;
674
        }
675
676 13
        if ($this->hasMethod('__clone')) {
677 1
            return $this->getMethod('__clone')->isPublic();
678
        }
679
680 12
        return true;
681
    }
682
683
    /**
684
     * {@inheritDoc}
685
     */
686 43
    public function isFinal()
687
    {
688 43
        $isFinal = $this->classLikeNode instanceof Class_ && $this->classLikeNode->isFinal();
689
690 43
        return $isFinal;
691
    }
692
693
    /**
694
     * {@inheritDoc}
695
     */
696
    public function isInstance($object)
697
    {
698
        if (!is_object($object)) {
699
            throw new \RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)));
700
        }
701
702
        $className = $this->getName();
703
704
        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...
705
    }
706
707
    /**
708
     * {@inheritDoc}
709
     */
710 22
    public function isInstantiable()
711
    {
712 22
        if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
713 9
            return false;
714
        }
715
716 13
        if (null === ($constructor = $this->getConstructor())) {
717 11
            return true;
718
        }
719
720 2
        return $constructor->isPublic();
721
    }
722
723
    /**
724
     * {@inheritDoc}
725
     */
726 43
    public function isInterface()
727
    {
728 43
        return ($this->classLikeNode instanceof Interface_);
729
    }
730
731
    /**
732
     * {@inheritDoc}
733
     */
734 22
    public function isInternal()
735
    {
736
        // never can be an internal method
737 22
        return false;
738
    }
739
740
    /**
741
     * {@inheritDoc}
742
     */
743 22
    public function isIterateable()
744
    {
745 22
        return $this->implementsInterface('Traversable');
746
    }
747
748
    /**
749
     * {@inheritDoc}
750
     */
751
    public function isSubclassOf($class)
752
    {
753
        if (is_object($class)) {
754
            if ($class instanceof ReflectionClass) {
755
                $class = $class->name;
756
            } else {
757
                $class = get_class($class);
758
            }
759
        }
760
761
        if (!$this->classLikeNode instanceof Class_) {
762
            return false;
763
        } else {
764
            $extends = $this->classLikeNode->extends;
765
            if ($extends && $extends->toString() == $class) {
766
                return true;
767
            }
768
        }
769
770
        $parent = $this->getParentClass();
771
772
        return false === $parent ? false : $parent->isSubclassOf($class);
773
    }
774
775
    /**
776
     * {@inheritDoc}
777
     */
778 22
    public function isTrait()
779
    {
780 22
        return ($this->classLikeNode instanceof Trait_);
781
    }
782
783
    /**
784
     * {@inheritDoc}
785
     */
786 22
    public function isUserDefined()
787
    {
788
        // always defined by user, because we parse the source code
789 22
        return true;
790
    }
791
792
    /**
793
     * Gets static properties
794
     *
795
     * @link http://php.net/manual/en/reflectionclass.getstaticproperties.php
796
     *
797
     * @return array
798
     */
799 23
    public function getStaticProperties()
800
    {
801
        // In runtime static properties can be changed in any time
802 23
        if ($this->isInitialized()) {
803 1
            return forward_static_call('parent::getStaticProperties');
804
        }
805
806 22
        $properties = [];
807
808 22
        $reflectionProperties = $this->getProperties(ReflectionProperty::IS_STATIC);
809 22
        foreach ($reflectionProperties as $reflectionProperty) {
810 4
            if (!$reflectionProperty instanceof ReflectionProperty) {
811 1
                if (!$reflectionProperty->isPublic()) {
812 1
                    $reflectionProperty->setAccessible(true);
813
                }
814
            }
815 4
            $properties[$reflectionProperty->getName()] = $reflectionProperty->getValue();
816
        }
817
818 22
        return $properties;
819
    }
820
821
    /**
822
     * Gets static property value
823
     *
824
     * @param string $name    The name of the static property for which to return a value.
825
     * @param mixed  $default A default value to return in case the class does not declare
826
     *                        a static property with the given name
827
     *
828
     * @return mixed
829
     * @throws ReflectionException If there is no such property and no default value was given
830
     */
831 1
    public function getStaticPropertyValue($name, $default = null)
832
    {
833 1
        $properties     = $this->getStaticProperties();
834 1
        $propertyExists = array_key_exists($name, $properties);
835
836 1
        if (!$propertyExists && func_num_args() === 1) {
837
            throw new ReflectionException("Static property does not exist and no default value is given");
838
        }
839
840 1
        return $propertyExists ? $properties[$name] : $default;
841
    }
842
843
844
    /**
845
     * Creates a new class instance from given arguments.
846
     *
847
     * @link http://php.net/manual/en/reflectionclass.newinstance.php
848
     * @param mixed $args Accepts a variable number of arguments which are passed to the class constructor
849
     *
850
     * @return object
851
     */
852 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...
853
    {
854 1
        $this->initializeInternalReflection();
855
856 1
        return call_user_func_array('parent::newInstance', func_get_args());
857
    }
858
859
    /**
860
     * Creates a new class instance from given arguments.
861
     *
862
     * @link http://php.net/manual/en/reflectionclass.newinstanceargs.php
863
     *
864
     * @param array $args The parameters to be passed to the class constructor as an array.
865
     *
866
     * @return object
867
     */
868 1
    public function newInstanceArgs(array $args = [])
869
    {
870 1
        $function = __FUNCTION__;
871 1
        $this->initializeInternalReflection();
872
873 1
        return parent::$function($args);
874
    }
875
876
    /**
877
     * Creates a new class instance without invoking the constructor.
878
     *
879
     * @link http://php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php
880
     *
881
     * @return object
882
     */
883 1
    public function newInstanceWithoutConstructor($args = null)
884
    {
885 1
        $function = __FUNCTION__;
886 1
        $this->initializeInternalReflection();
887
888 1
        return parent::$function($args);
889
    }
890
891
    /**
892
     * Sets static property value
893
     *
894
     * @link http://php.net/manual/en/reflectionclass.setstaticpropertyvalue.php
895
     *
896
     * @param string $name Property name
897
     * @param mixed $value New property value
898
     */
899 1
    public function setStaticPropertyValue($name, $value)
900
    {
901 1
        $this->initializeInternalReflection();
902
903 1
        forward_static_call('parent::setStaticPropertyValue', $name, $value);
904 1
    }
905
906 101
    private function recursiveCollect(\Closure $collector)
907
    {
908 101
        $result   = [];
909 101
        $isParent = true;
910
911 101
        $traits = $this->getTraits();
912 101
        foreach ($traits as $trait) {
913 14
            $collector($result, $trait, !$isParent);
914
        }
915
916 101
        $parentClass = $this->getParentClass();
917 101
        if ($parentClass) {
918 22
            $collector($result, $parentClass, $isParent);
919
        }
920
921 101
        $interfaces = ReflectionClass::collectInterfacesFromClassNode($this->classLikeNode);
922 101
        foreach ($interfaces as $interface) {
923 8
            $collector($result, $interface, $isParent);
924
        }
925
926 101
        return $result;
927
    }
928
929
    /**
930
     * Returns list of constants from the class
931
     *
932
     * @return array
933
     */
934 26
    private function findConstants()
935
    {
936 26
        $expressionSolver = new NodeExpressionResolver($this);
937
938
        // constants can be only top-level nodes in the class, so we can scan them directly
939 26
        foreach ($this->classLikeNode->stmts as $classLevelNode) {
940 22
            if ($classLevelNode instanceof ClassConst) {
941 8
                $nodeConstants = $classLevelNode->consts;
942 8
                if (!empty($nodeConstants)) {
943 8
                    foreach ($nodeConstants as $nodeConstant) {
944 8
                        $expressionSolver->process($nodeConstant->value);
945 22
                        $this->constants[$nodeConstant->name] = $expressionSolver->getValue();
946
                    }
947
                }
948
            }
949
        }
950
951 26
        return $this->constants ?: [];
952
    }
953
}
954