Completed
Pull Request — master (#82)
by Loren
05:32
created

ReflectionClassLikeTrait::getParentClass()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 6.0073

Importance

Changes 0
Metric Value
dl 0
loc 26
c 0
b 0
f 0
ccs 16
cts 17
cp 0.9412
rs 8.439
cc 6
eloc 18
nc 7
nop 0
crap 6.0073
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\ReflectionExtension;
16
use Go\ParserReflection\ReflectionMethod;
17
use Go\ParserReflection\ReflectionProperty;
18
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
19
use PhpParser\Node\Name\FullyQualified;
20
use PhpParser\Node\Stmt\Class_;
21
use PhpParser\Node\Stmt\ClassConst;
22
use PhpParser\Node\Stmt\ClassLike;
23
use PhpParser\Node\Stmt\Interface_;
24
use PhpParser\Node\Stmt\Trait_;
25
use PhpParser\Node\Stmt\TraitUseAdaptation;
26
27
/**
28
 * General class-like reflection
29
 */
30
trait ReflectionClassLikeTrait
31
{
32
    use InitializationTrait;
33
34
    /**
35
     * @var ClassLike
36
     */
37
    protected $classLikeNode;
38
39
    /**
40
     * Short name of the class, without namespace
41
     *
42
     * @var string
43
     */
44
    protected $className;
45
46
    /**
47
     * List of all constants from the class
48
     *
49
     * @var array
50
     */
51
    protected $constants;
52
53
    /**
54
     * Interfaces, empty array or null if not initialized yet
55
     *
56
     * @var \ReflectionClass[]|array|null
57
     */
58
    protected $interfaceClasses;
59
60
    /**
61
     * List of traits, empty array or null if not initialized yet
62
     *
63
     * @var  \ReflectionClass[]|array|null
64
     */
65
    protected $traits;
66
67
    /**
68
     * Additional list of trait adaptations
69
     *
70
     * @var TraitUseAdaptation[]|array
71
     */
72
    protected $traitAdaptations;
73
74
    /**
75
     * @var array|ReflectionMethod[]
76
     */
77
    protected $methods;
78
79
    /**
80
     * Namespace name
81
     *
82
     * @var string
83
     */
84
    protected $namespaceName = '';
85
86
    /**
87
     * Parent class, or false if not present, null if uninitialized yet
88
     *
89
     * @var \ReflectionClass|false|null
90
     */
91
    protected $parentClass;
92
93
    /**
94
     * @var array|ReflectionProperty[]
95
     */
96
    protected $properties;
97
98
    /**
99
     * Returns the string representation of the ReflectionClass object.
100
     *
101
     * @return string
102
     */
103 2
    public function __toString()
104
    {
105 2
        $isObject = $this instanceof \ReflectionObject;
106
107 2
        $staticProperties = $staticMethods = $defaultProperties = $dynamicProperties = $methods = [];
108
109 2
        $format  = "%s [ <user> %sclass %s%s%s ] {\n";
110 2
        $format .= "  @@ %s %d-%d\n\n";
111 2
        $format .= "  - Constants [%d] {%s\n  }\n\n";
112 2
        $format .= "  - Static properties [%d] {%s\n  }\n\n";
113 2
        $format .= "  - Static methods [%d] {%s\n  }\n\n";
114 2
        $format .= "  - Properties [%d] {%s\n  }\n\n";
115 2
        $format .= ($isObject ? "  - Dynamic properties [%d] {%s\n  }\n\n" : '%s%s');
116 2
        $format .= "  - Methods [%d] {%s\n  }\n";
117 2
        $format .= "}\n";
118
119 2
        foreach ($this->getProperties() as $property) {
120
            if ($property->isStatic()) {
121
                $staticProperties[] = $property;
122
            } elseif ($property->isDefault()) {
123
                $defaultProperties[] = $property;
124
            } else {
125
                $dynamicProperties[] = $property;
126
            }
127
        }
128
129 2
        foreach ($this->getMethods() as $method) {
130 2
            if ($method->isStatic()) {
131 1
                $staticMethods[] = $method;
132
            } else {
133 2
                $methods[] = $method;
134
            }
135
        }
136
137
        $buildString = function (array $items, $indentLevel = 4) {
138 2
            if (!count($items)) {
139 2
                return '';
140
            }
141 2
            $indent = "\n" . str_repeat(' ', $indentLevel);
142 2
            return $indent . implode($indent, explode("\n", implode("\n", $items)));
143 2
        };
144
        $buildConstants = function (array $items, $indentLevel = 4) {
145 2
            $str = '';
146 2
            foreach ($items as $name => $value) {
147 1
                $str .= "\n" . str_repeat(' ', $indentLevel);
148 1
                $str .= sprintf(
149 1
                    'Constant [ %s %s ] { %s }',
150 1
                    gettype($value),
151 1
                    $name,
152 1
                    $value
153
                );
154
            }
155 2
            return $str;
156 2
        };
157 2
        $interfaceNames = $this->getInterfaceNames();
158 2
        $parentClass    = $this->getParentClass();
159 2
        $modifiers      = '';
160 2
        if ($this->isAbstract()) {
161 2
            $modifiers = 'abstract ';
162 1
        } elseif ($this->isFinal()) {
163
            $modifiers = 'final ';
164
        };
165
166 2
        $string = sprintf(
167 2
            $format,
168 2
            ($isObject ? 'Object of class' : 'Class'),
169 2
            $modifiers,
170 2
            $this->getName(),
171 2
            false !== $parentClass ? (' extends ' . $parentClass->getName()) : '',
172 2
            $interfaceNames ? (' implements ' . implode(', ', $interfaceNames)) : '',
173 2
            $this->getFileName(),
174 2
            $this->getStartLine(),
175 2
            $this->getEndLine(),
176 2
            count($this->getConstants()),
177 2
            $buildConstants($this->getConstants()),
178 2
            count($staticProperties),
179 2
            $buildString($staticProperties),
180 2
            count($staticMethods),
181 2
            $buildString($staticMethods),
182 2
            count($defaultProperties),
183 2
            $buildString($defaultProperties),
184 2
            $isObject ? count($dynamicProperties) : '',
185 2
            $isObject ? $buildString($dynamicProperties) : '',
186 2
            count($methods),
187 2
            $buildString($methods)
188
        );
189
190 2
        return $string;
191
    }
192
193
194
    /**
195
     * {@inheritDoc}
196
     */
197 14
    public function getConstant($name)
198
    {
199 14
        if ($this->hasConstant($name)) {
200 14
            return $this->constants[$name];
201
        }
202
203
        return false;
204
    }
205
206
    /**
207
     * {@inheritDoc}
208
     */
209 77
    public function getConstants()
210
    {
211 77
        if (!isset($this->constants)) {
212 77
            if ($this->classLikeNode) {
213
                $this->constants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
214 18
                    $result += $instance->getConstants();
215 69
                });
216 69
                $this->collectSelfConstants();
217
            } else {
218 8
                $this->initializeInternalReflection();
219 8
                $this->constants = parent::getConstants();
220
            }
221
        }
222
223 77
        return $this->constants;
224
    }
225
226
    /**
227
     * {@inheritDoc}
228
     */
229 44
    public function getConstructor()
230
    {
231 44
        $constructor = $this->getMethod('__construct');
232 44
        if (!$constructor) {
233 36
            return null;
234
        }
235
236 8
        return $constructor;
237
    }
238
239
    /**
240
     * Gets default properties from a class (including inherited properties).
241
     *
242
     * @link http://php.net/manual/en/reflectionclass.getdefaultproperties.php
243
     *
244
     * @return array An array of default properties, with the key being the name of the property and the value being
245
     * the default value of the property or NULL if the property doesn't have a default value
246
     */
247 64
    public function getDefaultProperties()
248
    {
249 64
        if (!$this->classLikeNode) {
250
            // This handles differences in access control permissions.
251 6
            $this->initializeInternalReflection();
252 6
            return parent::getDefaultProperties();
253
        }
254 58
        $defaultValues = [];
255 58
        $properties    = $this->getProperties();
256 58
        $staticOrder   = [true, false];
257 58
        foreach ($staticOrder as $shouldBeStatic) {
258 58
            foreach ($properties as $property) {
259 14
                $isStaticProperty     = $property->isStatic();
260 14
                if ($shouldBeStatic !== $isStaticProperty) {
261 14
                    continue;
262
                }
263 14
                $propertyName         = $property->getName();
264 14
                $isInternalReflection = get_class($property) == \ReflectionProperty::class;
265
266 14
                if (!$isInternalReflection || $isStaticProperty) {
267 14
                    $defaultValues[$propertyName] = $property->getValue();
268
                } elseif (!$isStaticProperty) {
269
                    // Internal reflection and dynamic property
270
                    $classProperties = $property->getDeclaringClass()->getDefaultProperties();
271 58
                    $defaultValues[$propertyName] = $classProperties[$propertyName];
272
                }
273
            }
274
        }
275
276 58
        return $defaultValues;
277
    }
278
279
    /**
280
     * {@inheritDoc}
281
     */
282 64
    public function getDocComment()
283
    {
284 64
        if (!$this->classLikeNode) {
285 6
            $this->initializeInternalReflection();
286 6
            return parent::getDocComment();
287
        }
288 58
        $docComment = $this->classLikeNode->getDocComment();
289
290 58
        return $docComment ? $docComment->getText() : false;
291
    }
292
293 66
    public function getEndLine()
294
    {
295 66
        if (!$this->classLikeNode) {
296 8
            $this->initializeInternalReflection();
297 8
            return parent::getEndLine();
298
        }
299 58
        return $this->classLikeNode->getAttribute('endLine');
300
    }
301
302 71
    public function getExtension()
303
    {
304 71
        $extName = $this->getExtensionName();
305 71
        if (!$extName) {
306 58
            return null;
307
        }
308
        // The purpose of Go\ParserReflection\ReflectionExtension is
309
        // to behave exactly like \ReflectionExtension, but return
310
        // Go\ParserReflection\ReflectionFunction and
311
        // Go\ParserReflection\ReflectionClass where apropriate.
312 13
        return new ReflectionExtension($extName);
313
    }
314
315 135
    public function getExtensionName()
316
    {
317 135
        if (!$this->classLikeNode) {
318 19
            $this->initializeInternalReflection();
319 19
            return parent::getExtensionName();
320
        }
321 116
        return false;
322
    }
323
324 46
    public function getFileName()
325
    {
326 46
        if (!$this->classLikeNode) {
327
            // If we got here, we're probably a built-in class, and filename
328
            // is probably false.
329 16
            $this->initializeInternalReflection();
330 16
            return parent::getFileName();
331
        }
332 30
        return $this->classLikeNode->getAttribute('fileName');
333
    }
334
335
    /**
336
     * {@inheritDoc}
337
     */
338 67
    public function getInterfaceNames()
339
    {
340 67
        return array_keys($this->getInterfaces());
341
    }
342
343
    /**
344
     * {@inheritDoc}
345
     */
346 131
    public function getInterfaces()
347
    {
348 131
        if (!isset($this->interfaceClasses)) {
349 67
            if ($this->classLikeNode) {
350
                $this->interfaceClasses = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
351 13
                    if ($instance->isInterface()) {
352 2
                        $result[$instance->name] = $instance;
353
                    }
354 13
                    $result += $instance->getInterfaces();
355 59
                });
356
            } else {
357 8
                $this->initializeInternalReflection();
358
                // We need the native implementation of getInterfaceNames() here.
359 8
                $ifaceNames = parent::getInterfaceNames();
0 ignored issues
show
Comprehensibility Bug introduced by Loren Osborn
It seems like you call parent on a different method (getInterfaceNames() instead of getInterfaces()). Are you sure this is correct? If so, you might want to change this to $this->getInterfaceNames().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
360 8
                $this->interfaceClasses = [];
361 8
                foreach ($ifaceNames as $ifaceName) {
362 4
                    $this->interfaceClasses[$ifaceName] = new ReflectionClass($ifaceName);
363
                }
364
            }
365
        }
366
367 131
        return $this->interfaceClasses;
368
    }
369
370
    /**
371
     * {@inheritdoc}
372
     * @param string $name
373
     */
374 6137
    public function getMethod($name)
375
    {
376 6137
        $methods = $this->getMethods();
377 6137
        foreach ($methods as $method) {
378 6118
            if ($method->getName() == $name) {
379 6118
                return $method;
380
            }
381
        }
382
383 66
        return false;
384
    }
385
386
    /**
387
     * Returns list of reflection methods
388
     *
389
     * @param null|integer $filter Optional filter
390
     *
391
     * @return array|\ReflectionMethod[]
392
     */
393 6308
    public function getMethods($filter = null)
394
    {
395 6308
        if (!isset($this->methods)) {
396 233
            if ($this->classLikeNode) {
397 207
                $directMethods = ReflectionMethod::collectFromClassNode($this->classLikeNode, $this);
398
                $parentMethods = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
399 45
                    $reflectionMethods = [];
400 45
                    foreach ($instance->getMethods() as $reflectionMethod) {
401 21
                        if (!$isParent || !$reflectionMethod->isPrivate()) {
402 21
                            $reflectionMethods[$reflectionMethod->name] = $reflectionMethod;
403
                        }
404
                    }
405 45
                    $result += $reflectionMethods;
406 207
                });
407 207
                $methods = $directMethods + $parentMethods;
408
409 207
                $this->methods = $methods;
410
            } else {
411 26
                $this->initializeInternalReflection();
412 26
                $methodRefs = parent::getMethods();
413 26
                $this->methods = [];
414 26
                foreach ($methodRefs as $eachMethod) {
415 24
                    $this->methods[$eachMethod->getName()] = new ReflectionMethod(
416 24
                        $this->getName(),
417 24
                        $eachMethod->getName()
418
                    );
419
                }
420
            }
421
        }
422 6308
        if (!isset($filter)) {
423 6267
            return array_values($this->methods);
424
        }
425
426 53
        $methods = [];
427 53
        foreach ($this->methods as $method) {
428 31
            if (!($filter & $method->getModifiers())) {
429 29
                continue;
430
            }
431 7
            $methods[] = $method;
432
        }
433
434 53
        return $methods;
435
    }
436
437
    /**
438
     * Returns a bitfield of the access modifiers for this class.
439
     *
440
     * @link http://php.net/manual/en/reflectionclass.getmodifiers.php
441
     *
442
     * NB: this method is not fully compatible with original value because of hidden internal constants
443
     *
444
     * @return int
445
     */
446 64
    public function getModifiers()
447
    {
448 64
        if (!$this->classLikeNode) {
449 6
            $this->initializeInternalReflection();
450 6
            return parent::getModifiers();
451
        }
452 58
        $modifiers = 0;
453
454 58
        if ($this->isFinal()) {
455 2
            $modifiers += \ReflectionClass::IS_FINAL;
456
        }
457
458 58
        if (PHP_VERSION_ID < 70000 && $this->isTrait()) {
459
            $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
460
        }
461
462 58
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
463 8
            $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
464
        }
465
466 58
        if ($this->isInterface()) {
467 6
            $abstractMethods = $this->getMethods();
468
        } else {
469 52
            $abstractMethods = $this->getMethods(\ReflectionMethod::IS_ABSTRACT);
470
        }
471 58
        if (!empty($abstractMethods)) {
472 10
            $modifiers += \ReflectionClass::IS_IMPLICIT_ABSTRACT;
473
        }
474
475 58
        return $modifiers;
476
    }
477
478
    /**
479
     * {@inheritDoc}
480
     */
481 8382
    public function getName()
482
    {
483 8382
        $namespaceName = $this->namespaceName ? $this->namespaceName . '\\' : '';
484
485 8382
        return $namespaceName . $this->getShortName();
486
    }
487
488
    /**
489
     * {@inheritDoc}
490
     */
491 94
    public function getNamespaceName()
492
    {
493 94
        return $this->namespaceName;
494
    }
495
496
    /**
497
     * {@inheritDoc}
498
     */
499 593
    public function getParentClass()
500
    {
501 593
        if (!isset($this->parentClass)) {
502 315
            static $extendsField = 'extends';
503
504 315
            if ($this->classLikeNode) {
505 313
                $parentClass = false;
506 313
                $hasExtends  = in_array($extendsField, $this->classLikeNode->getSubNodeNames());
507 313
                $extendsNode = $hasExtends ? $this->classLikeNode->$extendsField : null;
508 313
                if ($extendsNode instanceof FullyQualified) {
509 48
                    $extendsName = $extendsNode->toString();
510 48
                    $parentClass = new ReflectionClass($extendsName);
511
                }
512 313
                $this->parentClass = $parentClass;
513
            } else {
514 2
                $this->initializeInternalReflection();
515 2
                $nativeRefParentClass = parent::getParentClass();
516 2
                if (!$nativeRefParentClass) {
517 2
                    return false;
518
                }
519
                $this->parentClass = new ReflectionClass($nativeRefParentClass->getName());
520
            }
521
        }
522
523 591
        return $this->parentClass;
524
    }
525
526
    /**
527
     * Retrieves reflected properties.
528
     *
529
     * @param int $filter The optional filter, for filtering desired property types.
530
     *                    It's configured using the ReflectionProperty constants, and defaults to all property types.
531
     *
532
     * @return array|\Go\ParserReflection\ReflectionProperty[]
533
     */
534 732
    public function getProperties($filter = null)
535
    {
536 732
        if (!isset($this->properties)) {
537 143
            if ($this->classLikeNode) {
538 133
                $directProperties = ReflectionProperty::collectFromClassNode($this->classLikeNode, $this->getName());
539 133
                $parentProperties = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
540 26
                    $reflectionProperties = [];
541 26
                    foreach ($instance->getProperties() as $reflectionProperty) {
542 6
                        if (!$isParent || !$reflectionProperty->isPrivate()) {
543 6
                            $reflectionProperties[$reflectionProperty->name] = $reflectionProperty;
544
                        }
545
                    }
546 26
                    $result += $reflectionProperties;
547 133
                });
548 133
                $properties = $directProperties + $parentProperties;
549
550 133
                $this->properties = $properties;
551
            } else {
552 10
                $this->initializeInternalReflection();
553 10
                $propertyRefs = parent::getProperties();
554 10
                $this->properties = [];
555 10
                foreach ($propertyRefs as $eachProperty) {
556 4
                    $this->properties[$eachProperty->getName()] = new ReflectionProperty(
557 4
                        $this->getName(),
558 4
                        $eachProperty->getName()
559
                    );
560
                }
561
            }
562
        }
563
564
        // Without filter we can just return the full list
565 732
        if (!isset($filter)) {
566 709
            return array_values($this->properties);
567
        }
568
569 29
        $properties = [];
570 29
        foreach ($this->properties as $property) {
571 7
            if (!($filter & $property->getModifiers())) {
572 5
                continue;
573
            }
574 5
            $properties[] = $property;
575
        }
576
577 29
        return $properties;
578
    }
579
580
    /**
581
     * {@inheritdoc}
582
     */
583 579
    public function getProperty($name)
584
    {
585 579
        $properties = $this->getProperties();
586 579
        foreach ($properties as $property) {
587 579
            if ($property->getName() == $name) {
588 579
                return $property;
589
            }
590
        }
591
592
        return false;
593
    }
594
595
    /**
596
     * {@inheritDoc}
597
     */
598 8382
    public function getShortName()
599
    {
600 8382
        return $this->className;
601
    }
602
603 66
    public function getStartLine()
604
    {
605 66
        if (!$this->classLikeNode) {
606 8
            $this->initializeInternalReflection();
607 8
            return parent::getStartLine();
608
        }
609 58
        return $this->classLikeNode->getAttribute('startLine');
610
    }
611
612
    /**
613
     * Returns an array of trait aliases
614
     *
615
     * @link http://php.net/manual/en/reflectionclass.gettraitaliases.php
616
     *
617
     * @return array|null an array with new method names in keys and original names (in the format "TraitName::original") in
618
     * values.
619
     */
620 128
    public function getTraitAliases()
621
    {
622 128
        if (!$this->classLikeNode) {
623 12
            $this->initializeInternalReflection();
624 12
            return parent::getTraitAliases();
625
        }
626 116
        $aliases = [];
627 116
        $traits  = $this->getTraits();
628 116
        foreach ($this->traitAdaptations as $adaptation) {
629
            if ($adaptation instanceof TraitUseAdaptation\Alias) {
630
                $methodName = $adaptation->method;
631
                $traitName  = null;
632
                foreach ($traits as $trait) {
633
                    if ($trait->hasMethod($methodName)) {
634
                        $traitName = $trait->getName();
635
                        break;
636
                    }
637
                }
638
                $aliases[$adaptation->newName] = $traitName . '::'. $methodName;
639
            }
640
        }
641
642 116
        return $aliases;
643
    }
644
645
    /**
646
     * Returns an array of names of traits used by this class
647
     *
648
     * @link http://php.net/manual/en/reflectionclass.gettraitnames.php
649
     *
650
     * @return array
651
     */
652 64
    public function getTraitNames()
653
    {
654 64
        return array_keys($this->getTraits());
655
    }
656
657
    /**
658
     * Returns an array of traits used by this class
659
     *
660
     * @link http://php.net/manual/en/reflectionclass.gettraits.php
661
     *
662
     * @return array|\ReflectionClass[]
663
     */
664 585
    public function getTraits()
665
    {
666 585
        if (!isset($this->traits)) {
667 319
            if ($this->classLikeNode) {
668 313
                $traitAdaptations = [];
669 313
                $this->traits     = ReflectionClass::collectTraitsFromClassNode($this->classLikeNode, $traitAdaptations);
670 313
                $this->traitAdaptations = $traitAdaptations;
671
            } else {
672 6
                $this->initializeInternalReflection();
673 6
                $nativeTraitRefs = parent::getTraits();
674 6
                $this->traits = [];
675 6
                foreach ($nativeTraitRefs as $eachTrait) {
676
                    $this->traits[$eachTrait->getName()] = new ReflectionClass($eachTrait->getName());
677
                }
678
            }
679
        }
680
681 585
        return $this->traits;
682
    }
683
684
    /**
685
     * {@inheritDoc}
686
     */
687 15
    public function hasConstant($name)
688
    {
689 15
        $constants   = $this->getConstants();
690 15
        $hasConstant = isset($constants[$name]) || array_key_exists($name, $constants);
691
692 15
        return $hasConstant;
693
    }
694
695
    /**
696
     * {@inheritdoc}
697
     * @param string $name
698
     */
699 45
    public function hasMethod($name)
700
    {
701 45
        $methods = $this->getMethods();
702 45
        foreach ($methods as $method) {
703 26
            if ($method->getName() == $name) {
704 26
                return true;
705
            }
706
        }
707
708 40
        return false;
709
    }
710
711
    /**
712
     * {@inheritdoc}
713
     */
714
    public function hasProperty($name)
715
    {
716
        $properties = $this->getProperties();
717
        foreach ($properties as $property) {
718
            if ($property->getName() == $name) {
719
                return true;
720
            }
721
        }
722
723
        return false;
724
    }
725
726
    /**
727
     * {@inheritDoc}
728
     * @param string $interfaceName
729
     */
730 64
    public function implementsInterface($interfaceName)
731
    {
732 64
        $allInterfaces = $this->getInterfaces();
733
734 64
        return isset($allInterfaces[$interfaceName]);
735
    }
736
737
    /**
738
     * {@inheritDoc}
739
     */
740 64
    public function inNamespace()
741
    {
742 64
        return !empty($this->namespaceName);
743
    }
744
745
    /**
746
     * {@inheritDoc}
747
     */
748 170
    public function isAbstract()
749
    {
750 170
        if (!$this->classLikeNode) {
751 20
            $this->initializeInternalReflection();
752 20
            return parent::isAbstract();
753
        }
754 150
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
755 24
            return true;
756 126
        } elseif ($this->isInterface() && !empty($this->getMethods())) {
757 4
            return true;
758 122
        } elseif ($this->isTrait()) {
759 6
            return PHP_VERSION_ID < 70000 ? true : false;
760
        }
761
762 116
        return false;
763
    }
764
765
    /**
766
     * Currently, anonymous classes aren't supported for parsed reflection
767
     */
768
    public function isAnonymous()
769
    {
770
        return false;
771
    }
772
773
    /**
774
     * {@inheritDoc}
775
     */
776 64
    public function isCloneable()
777
    {
778 64
        if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
779 20
            return false;
780
        }
781
782 44
        if ($this->hasMethod('__clone')) {
783 4
            return $this->getMethod('__clone')->isPublic();
784
        }
785
786 40
        return true;
787
    }
788
789
    /**
790
     * {@inheritDoc}
791
     */
792 123
    public function isFinal()
793
    {
794 123
        if (!$this->classLikeNode) {
795 7
            $this->initializeInternalReflection();
796 7
            return parent::isFinal();
797
        }
798 116
        $isFinal = $this->classLikeNode instanceof Class_ && $this->classLikeNode->isFinal();
799
800 116
        return $isFinal;
801
    }
802
803
    /**
804
     * {@inheritDoc}
805
     */
806
    public function isInstance($object)
807
    {
808
        if (!is_object($object)) {
809
            throw new \RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)));
810
        }
811
812
        $className = $this->getName();
813
        return $className === get_class($object) || is_subclass_of($object, $className);
0 ignored issues
show
Bug introduced by Lisachenko Alexander
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...
814
    }
815
816
    /**
817
     * {@inheritDoc}
818
     */
819 64
    public function isInstantiable()
820
    {
821 64
        if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
822 20
            return false;
823
        }
824
825 44
        if (null === ($constructor = $this->getConstructor())) {
826 36
            return true;
827
        }
828
829 8
        return $constructor->isPublic();
830
    }
831
832
    /**
833
     * {@inheritDoc}
834
     */
835 574
    public function isInterface()
836
    {
837 574
        if (!$this->classLikeNode) {
838 18
            $this->initializeInternalReflection();
839 18
            return parent::isInterface();
840
        }
841 556
        return ($this->classLikeNode instanceof Interface_);
842
    }
843
844
    /**
845
     * {@inheritDoc}
846
     */
847 64
    public function isInternal()
848
    {
849 64
        if (!$this->classLikeNode) {
850 6
            $this->initializeInternalReflection();
851 6
            return parent::isInternal();
852
        }
853 58
        return false;
854
    }
855
856
    /**
857
     * {@inheritDoc}
858
     */
859 64
    public function isIterateable()
860
    {
861 64
        return $this->implementsInterface('Traversable');
862
    }
863
864
    /**
865
     * {@inheritDoc}
866
     */
867
    public function isSubclassOf($class)
868
    {
869
        if (!$this->classLikeNode) {
870
            $this->initializeInternalReflection();
871
            return parent::isSubclassOf($class);
872
        }
873
        if (is_object($class)) {
874
            if ($class instanceof ReflectionClass) {
875
                $class = $class->name;
876
            } else {
877
                $class = get_class($class);
878
            }
879
        }
880
881
        if (!$this->classLikeNode instanceof Class_) {
882
            return false;
883
        } else {
884
            $extends = $this->classLikeNode->extends;
885
            if ($extends && $extends->toString() == $class) {
886
                return true;
887
            }
888
        }
889
890
        $parent = $this->getParentClass();
891
892
        return false === $parent ? false : $parent->isSubclassOf($class);
893
    }
894
895
    /**
896
     * {@inheritDoc}
897
     */
898 229
    public function isTrait()
899
    {
900 229
        if (!$this->classLikeNode) {
901 18
            $this->initializeInternalReflection();
902 18
            return parent::isTrait();
903
        }
904 211
        return ($this->classLikeNode instanceof Trait_);
905
    }
906
907
    /**
908
     * {@inheritDoc}
909
     */
910 64
    public function isUserDefined()
911
    {
912 64
        if (!$this->classLikeNode) {
913 6
            $this->initializeInternalReflection();
914 6
            return parent::isUserDefined();
915
        }
916 58
        return true;
917
    }
918
919
    /**
920
     * Gets static properties
921
     *
922
     * @link http://php.net/manual/en/reflectionclass.getstaticproperties.php
923
     *
924
     * @return array
925
     */
926 65
    public function getStaticProperties()
927
    {
928
        // In runtime static properties can be changed in any time
929 65
        if ($this->wasIncluded() || !$this->classLikeNode) {
930 36
            $this->initializeInternalReflection();
931 36
            return parent::getStaticProperties();
932
        }
933
934 29
        $properties = [];
935
936 29
        $reflectionProperties = $this->getProperties(ReflectionProperty::IS_STATIC);
937 29
        foreach ($reflectionProperties as $reflectionProperty) {
938 5
            if (!$reflectionProperty instanceof ReflectionProperty) {
939
                if (!$reflectionProperty->isPublic()) {
940
                    $reflectionProperty->setAccessible(true);
941
                }
942
            }
943 5
            $properties[$reflectionProperty->getName()] = $reflectionProperty->getValue();
944
        }
945
946 29
        return $properties;
947
    }
948
949
    /**
950
     * Gets static property value
951
     *
952
     * @param string $name    The name of the static property for which to return a value.
953
     * @param mixed  $default A default value to return in case the class does not declare
954
     *                        a static property with the given name
955
     *
956
     * @return mixed
957
     * @throws ReflectionException If there is no such property and no default value was given
958
     */
959 1
    public function getStaticPropertyValue($name, $default = null)
960
    {
961 1
        $properties     = $this->getStaticProperties();
962 1
        $propertyExists = array_key_exists($name, $properties);
963
964 1
        if (!$propertyExists && func_num_args() === 1) {
965
            throw new ReflectionException("Static property does not exist and no default value is given");
966
        }
967
968 1
        return $propertyExists ? $properties[$name] : $default;
969
    }
970
971
972
    /**
973
     * Creates a new class instance from given arguments.
974
     *
975
     * @link http://php.net/manual/en/reflectionclass.newinstance.php
976
     *
977
     * Signature was hacked to support both 5.6, 7.1.x and 7.2.0 versions
978
     * @see https://3v4l.org/hW9O9
979
     * @see https://3v4l.org/sWT3j
980
     * @see https://3v4l.org/eeVf8
981
     *
982
     * @param mixed $arg First argument
983
     * @param mixed $args Accepts a variable number of arguments which are passed to the class constructor
984
     *
985
     * @return object
986
     */
987 1
    public function newInstance($arg = null, ...$args)
988
    {
989 1
        $args = array_slice(array_merge([$arg], $args), 0, \func_num_args());
990 1
        $this->initializeInternalReflection();
991
992 1
        return parent::newInstance(...$args);
993
    }
994
995
    /**
996
     * Creates a new class instance from given arguments.
997
     *
998
     * @link http://php.net/manual/en/reflectionclass.newinstanceargs.php
999
     *
1000
     * @param array $args The parameters to be passed to the class constructor as an array.
1001
     *
1002
     * @return object
1003
     */
1004 1
    public function newInstanceArgs(array $args = [])
1005
    {
1006 1
        $function = __FUNCTION__;
1007 1
        $this->initializeInternalReflection();
1008
1009 1
        return parent::$function($args);
1010
    }
1011
1012
    /**
1013
     * Creates a new class instance without invoking the constructor.
1014
     *
1015
     * @link http://php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php
1016
     *
1017
     * @return object
1018
     */
1019 1
    public function newInstanceWithoutConstructor($args = null)
1020
    {
1021 1
        $function = __FUNCTION__;
1022 1
        $this->initializeInternalReflection();
1023
1024 1
        return parent::$function($args);
1025
    }
1026
1027
    /**
1028
     * Sets static property value
1029
     *
1030
     * @link http://php.net/manual/en/reflectionclass.setstaticpropertyvalue.php
1031
     *
1032
     * @param string $name Property name
1033
     * @param mixed $value New property value
1034
     */
1035 1
    public function setStaticPropertyValue($name, $value)
1036
    {
1037 1
        $this->initializeInternalReflection();
1038
1039 1
        parent::setStaticPropertyValue($name, $value);
1040 1
    }
1041
1042 463
    private function recursiveCollect(\Closure $collector)
1043
    {
1044 463
        $result   = [];
1045 463
        $isParent = true;
1046
1047 463
        $traits = $this->getTraits();
1048 463
        foreach ($traits as $trait) {
1049 16
            $collector($result, $trait, !$isParent);
1050
        }
1051
1052 463
        $parentClass = $this->getParentClass();
1053 463
        if ($parentClass) {
1054 70
            $collector($result, $parentClass, $isParent);
1055
        }
1056
1057 463
        $interfaces = ReflectionClass::collectInterfacesFromClassNode($this->classLikeNode);
1058 463
        foreach ($interfaces as $interface) {
1059 14
            $collector($result, $interface, $isParent);
1060
        }
1061
1062 463
        return $result;
1063
    }
1064
1065
    /**
1066
     * Collects list of constants from the class itself
1067
     */
1068 69
    private function collectSelfConstants()
1069
    {
1070 69
        $expressionSolver = new NodeExpressionResolver($this);
1071 69
        $localConstants   = [];
1072
1073
        // constants can be only top-level nodes in the class, so we can scan them directly
1074 69
        foreach ($this->classLikeNode->stmts as $classLevelNode) {
1075 61
            if ($classLevelNode instanceof ClassConst) {
1076 27
                $nodeConstants = $classLevelNode->consts;
1077 27
                if (!empty($nodeConstants)) {
1078 27
                    foreach ($nodeConstants as $nodeConstant) {
1079 27
                        $expressionSolver->process($nodeConstant->value);
1080 27
                        $localConstants[$nodeConstant->name] = $expressionSolver->getValue();
1081 61
                        $this->constants = $localConstants + $this->constants;
1082
                    }
1083
                }
1084
            }
1085
        }
1086 69
    }
1087
1088
    /**
1089
     * Has class been loaded by PHP.
1090
     *
1091
     * @return bool
1092
     *     If class file was included.
1093
     */
1094 207
    public function wasIncluded()
1095
    {
1096
        return
1097 207
            interface_exists($this->getName(), false) ||
1098 189
            trait_exists($this->getName(), false) ||
1099 207
            class_exists($this->getName(), false);
1100
    }
1101
}
1102