Completed
Push — master ( c4d2d4...eae51e )
by Alexander
9s
created

src/Traits/ReflectionClassLikeTrait.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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