Completed
Push — master ( 5f68cd...6c5557 )
by Alexander
02:53
created

ReflectionClassLikeTrait::getStaticProperties()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5
Metric Value
dl 0
loc 21
ccs 14
cts 14
cp 1
rs 8.7624
cc 5
eloc 11
nc 5
nop 0
crap 5
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
25
/**
26
 * General class-like reflection
27
 */
28
trait ReflectionClassLikeTrait
29
{
30
    use InitializationTrait;
31
32
    /**
33
     * @var ClassLike
34
     */
35
    protected $classLikeNode;
36
37
    /**
38
     * Short name of the class, without namespace
39
     *
40
     * @var string
41
     */
42
    protected $className;
43
44
    /**
45
     * List of all constants from the class
46
     *
47
     * @var array
48
     */
49
    protected $constants;
50
51
    /**
52
     * Interfaces, empty array or null if not initialized yet
53
     *
54
     * @var \ReflectionClass[]|array|null
55
     */
56
    protected $interfaceClasses;
57
58
    /**
59
     * List of traits, empty array or null if not initialized yet
60
     *
61
     * @var  \ReflectionClass[]|array|null
62
     */
63
    protected $traits;
64
65
    /**
66
     * @var array|ReflectionMethod[]
67
     */
68
    protected $methods;
69
70
    /**
71
     * Namespace name
72
     *
73
     * @var string
74
     */
75
    protected $namespaceName = '';
76
77
    /**
78
     * Parent class, or false if not present, null if uninitialized yet
79
     *
80
     * @var \ReflectionClass|false|null
81
     */
82
    protected $parentClass;
83
84
    /**
85
     * @var array|ReflectionProperty[]
86
     */
87
    protected $properties;
88
89
    /**
90
     * Emulating original behaviour of reflection
91
     */
92
    public function __debugInfo()
93
    {
94
        return array(
95
            'name' => $this->getName()
96
        );
97
    }
98
99
    /**
100
     * Returns the string representation of the ReflectionClass object.
101
     *
102
     * @return string
103
     */
104
    public function __toString()
105
    {
106
        $isObject = $this instanceof \ReflectionObject;
107
108
        $staticProperties = $staticMethods = $defaultProperties = $dynamicProperties = $methods = [];
109
110
        $format  = "%s [ <user> %sclass %s%s%s ] {\n";
111
        $format .= "  @@ %s %d-%d\n\n";
112
        $format .= "  - Constants [%d] {%s\n  }\n\n";
113
        $format .= "  - Static properties [%d] {%s\n  }\n\n";
114
        $format .= "  - Static methods [%d] {%s\n  }\n\n";
115
        $format .= "  - Properties [%d] {%s\n  }\n\n";
116
        $format .= ($isObject ? "  - Dynamic properties [%d] {%s\n  }\n\n" : '%s%s');
117
        $format .= "  - Methods [%d] {%s\n  }\n";
118
        $format .= "}\n";
119
120
        foreach ($this->getProperties() as $property) {
121
            if ($property->isStatic()) {
122
                $staticProperties[] = $property;
123
            } elseif ($property->isDefault()) {
124
                $defaultProperties[] = $property;
125
            } else {
126
                $dynamicProperties[] = $property;
127
            }
128
        }
129
130
        foreach ($this->getMethods() as $method) {
131
            if ($method->isStatic()) {
132
                $staticMethods[] = $method;
133
            } else {
134
                $methods[] = $method;
135
            }
136
        }
137
138
        $buildString = function (array $items, $indentLevel = 4) {
139
            if (!count($items)) {
140
                return '';
141
            }
142
            $indent = "\n" . str_repeat(' ', $indentLevel);
143
            return $indent . implode($indent, explode("\n", implode("\n", $items)));
144
        };
145
        $buildConstants = function (array $items, $indentLevel = 4) {
146
            $str = '';
147
            foreach ($items as $name => $value) {
148
                $str .= "\n" . str_repeat(' ', $indentLevel);
149
                $str .= sprintf(
150
                    'Constant [ %s %s ] { %s }',
151
                    gettype($value),
152
                    $name,
153
                    $value
154
                );
155
            }
156
            return $str;
157
        };
158
        $interfaceNames = $this->getInterfaceNames();
159
        $parentClass    = $this->getParentClass();
160
        $modifiers      = '';
161
        if ($this->isAbstract()) {
162
            $modifiers = 'abstract ';
163
        } elseif ($this->isFinal()) {
164
            $modifiers = 'final ';
165
        };
166
167
        $string = sprintf(
168
            $format,
169
            ($isObject ? 'Object of class' : 'Class'),
170
            $modifiers,
171
            $this->getName(),
172
            false !== $parentClass ? (' extends ' . $parentClass->getName()) : '',
173
            $interfaceNames ? (' implements ' . implode(', ', $interfaceNames)) : '',
174
            $this->getFileName(),
175
            $this->getStartLine(),
176
            $this->getEndLine(),
177
            count($this->getConstants()),
178
            $buildConstants($this->getConstants()),
179
            count($staticProperties),
180
            $buildString($staticProperties),
181
            count($staticMethods),
182
            $buildString($staticMethods),
183
            count($defaultProperties),
184
            $buildString($defaultProperties),
185
            $isObject ? count($dynamicProperties) : '',
186
            $isObject ? $buildString($dynamicProperties) : '',
187
            count($methods),
188
            $buildString($methods)
189
        );
190
191
        return $string;
192
    }
193
194
195
    /**
196
     * {@inheritDoc}
197
     */
198 3
    public function getConstant($name)
199
    {
200 3
        if ($this->hasConstant($name)) {
201 3
            return $this->constants[$name];
202
        }
203
204
        return false;
205
    }
206
207
    /**
208
     * {@inheritDoc}
209
     */
210 3
    public function getConstants()
211
    {
212 3
        if (!isset($this->constants)) {
213 3
            $directConstants = $this->findConstants();
214
            $parentConstants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
215 2
                $result += $instance->getConstants();
216 3
            });
217 3
            $constants = $directConstants + $parentConstants;
218
219 3
            $this->constants = $constants;
220 3
        }
221
222 3
        return $this->constants;
223
    }
224
225
    /**
226
     * {@inheritDoc}
227
     */
228 2
    public function getConstructor()
229
    {
230 2
        $constructor = $this->getMethod('__construct');
231 2
        if (!$constructor) {
232 2
            return null;
233
        }
234
235 1
        return $constructor;
236
    }
237
238
    /**
239
     * {@inheritDoc}
240
     */
241 2
    public function getDocComment()
242
    {
243 2
        return $this->classLikeNode->getDocComment() ?: false;
244
    }
245
246 2
    public function getEndLine()
247
    {
248 2
        return $this->classLikeNode->getAttribute('endLine');
249
    }
250
251 2
    public function getExtension()
252
    {
253 2
        return null;
254
    }
255
256 2
    public function getExtensionName()
257
    {
258 2
        return false;
259
    }
260
261 5
    public function getFileName()
262
    {
263 5
        return $this->classLikeNode->getAttribute('fileName');
264
    }
265
266
    /**
267
     * {@inheritDoc}
268
     */
269 2
    public function getInterfaceNames()
270
    {
271 2
        return array_keys($this->getInterfaces());
272
    }
273
274
    /**
275
     * {@inheritDoc}
276
     */
277 2
    public function getInterfaces()
278
    {
279 2
        if (!isset($this->interfaceClasses)) {
280
            $this->interfaceClasses = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
281 2
                if ($instance->isInterface()) {
282 1
                    $result[$instance->name] = $instance;
283 1
                }
284 2
                $result += $instance->getInterfaces();
285 2
            });
286 2
        }
287
288 2
        return $this->interfaceClasses;
289
    }
290
291
    /**
292
     * {@inheritdoc}
293
     */
294 5
    public function getMethod($name)
295
    {
296 5
        $methods = $this->getMethods();
297 5
        foreach ($methods as $method) {
298 4
            if ($method->getName() == $name) {
299 4
                return $method;
300
            }
301 3
        }
302
303 2
        return false;
304
    }
305
306
    /**
307
     * Returns list of reflection methods
308
     *
309
     * @return ReflectionMethod[]|array
310
     */
311 6
    public function getMethods($filter = null)
0 ignored issues
show
Unused Code introduced by
The parameter $filter 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...
312
    {
313 6
        if (!isset($this->methods)) {
314 6
            $directMethods = ReflectionMethod::collectFromClassNode($this->classLikeNode, $this->getName());
315
            $parentMethods = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
316 3
                $result = array_merge($result, $instance->getMethods());
317 6
            });
318 6
            $methods = array_merge($directMethods, $parentMethods);
319
320 6
            $this->methods = $methods;
321 6
        }
322
        // TODO: Implement filtration of methods
323
324 6
        return $this->methods;
325
    }
326
327
    /**
328
     * {@inheritDoc}
329
     */
330 13
    public function getName()
331
    {
332 13
        $namespaceName = $this->namespaceName ? $this->namespaceName . '\\' : '';
333
334 13
        return $namespaceName . $this->getShortName();
335
    }
336
337
    /**
338
     * {@inheritDoc}
339
     */
340 5
    public function getNamespaceName()
341
    {
342 5
        return $this->namespaceName;
343
    }
344
345
    /**
346
     * {@inheritDoc}
347
     */
348 9
    public function getParentClass()
349
    {
350 9
        if (!isset($this->parentClass)) {
351 9
            static $extendsField = 'extends';
352
353 9
            $parentClass = false;
354 9
            $hasExtends  = in_array($extendsField, $this->classLikeNode->getSubNodeNames());
355 9
            $extendsNode = $hasExtends ? $this->classLikeNode->$extendsField : null;
356 9
            if ($extendsNode instanceof FullyQualified) {
357 3
                $extendsName = $extendsNode->toString();
358 3
                $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...
359 3
            }
360 9
            $this->parentClass = $parentClass;
361 9
        }
362
363 9
        return $this->parentClass;
364
    }
365
366
    /**
367
     * Retrieves reflected properties.
368
     *
369
     * @param int $filter The optional filter, for filtering desired property types.
370
     *                    It's configured using the ReflectionProperty constants, and defaults to all property types.
371
     *
372
     * @return array|\Go\ParserReflection\ReflectionProperty[]
373
     */
374 5
    public function getProperties($filter = null)
375
    {
376 5
        if (!isset($this->properties)) {
377 5
            $directProperties = ReflectionProperty::collectFromClassNode($this->classLikeNode, $this->getName());
378 5
            $parentProperties = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
379 2
                $reflectionProperties = [];
380 2
                foreach ($instance->getProperties() as $reflectionProperty) {
381 1
                    if (!$reflectionProperty->isPrivate()) {
382 1
                        $reflectionProperties[] = $reflectionProperty;
383 1
                    }
384 2
                }
385 2
                $result = array_merge($result, $reflectionProperties);
386 5
            });
387 5
            $properties = array_merge($directProperties, $parentProperties);
388
389 5
            $this->properties = $properties;
390 5
        }
391
392
        // Without filter we can just return the full list
393 5
        if (!isset($filter)) {
394 3
            return $this->properties;
395
        }
396
397 2
        $properties = [];
398 2
        foreach ($this->properties as $property) {
399 1
            if (($filter & ReflectionProperty::IS_STATIC) && !($property->isStatic())) {
400 1
                continue;
401
            }
402 1
            if (($filter & ReflectionProperty::IS_PUBLIC) && !($property->isPublic())) {
403
                continue;
404
            }
405 1
            if (($filter & ReflectionProperty::IS_PROTECTED) && !($property->isProtected())) {
406
                continue;
407
            }
408 1
            if (($filter & ReflectionProperty::IS_PRIVATE) && !($property->isPrivate())) {
409
                continue;
410
            }
411 1
            $properties[] = $property;
412 2
        }
413
414 2
        return $properties;
415
    }
416
417
    /**
418
     * {@inheritdoc}
419
     */
420 3
    public function getProperty($name)
421
    {
422 3
        $properties = $this->getProperties();
423 3
        foreach ($properties as $property) {
424 3
            if ($property->getName() == $name) {
425 3
                return $property;
426
            }
427 3
        }
428
429
        return false;
430
    }
431
432
    /**
433
     * {@inheritDoc}
434
     */
435 13
    public function getShortName()
436
    {
437 13
        return $this->className;
438
    }
439
440 2
    public function getStartLine()
441
    {
442 2
        return $this->classLikeNode->getAttribute('startLine');
443
    }
444
445
    /**
446
     * Returns an array of names of traits used by this class
447
     *
448
     * @link http://php.net/manual/en/reflectionclass.gettraitnames.php
449
     *
450
     * @return array
451
     */
452 2
    public function getTraitNames()
453
    {
454 2
        return array_keys($this->getTraits());
455
    }
456
457
    /**
458
     * Returns an array of traits used by this class
459
     *
460
     * @link http://php.net/manual/en/reflectionclass.gettraits.php
461
     *
462
     * @return array|\ReflectionClass[]
463
     */
464 2
    public function getTraits()
465
    {
466 2
        if (!isset($this->traits)) {
467 2
            $this->traits = ReflectionClass::collectTraitsFromClassNode($this->classLikeNode);
468 2
        }
469
470 2
        return $this->traits;
471
    }
472
473
    /**
474
     * {@inheritDoc}
475
     */
476 3
    public function hasConstant($name)
477
    {
478 3
        $constants   = $this->getConstants();
479 3
        $hasConstant = isset($constants[$name]) || array_key_exists($constants, $name);
480
481 3
        return $hasConstant;
482
    }
483
484
    /**
485
     * {@inheritdoc}
486
     */
487 4
    public function hasMethod($name)
488
    {
489 4
        $methods = $this->getMethods();
490 4
        foreach ($methods as $method) {
491 3
            if ($method->getName() == $name) {
492 3
                return true;
493
            }
494 2
        }
495
496 2
        return false;
497
    }
498
499
    /**
500
     * {@inheritdoc}
501
     */
502
    public function hasProperty($name)
503
    {
504
        $properties = $this->getProperties();
505
        foreach ($properties as $property) {
506
            if ($property->getName() == $name) {
507
                return true;
508
            }
509
        }
510
511
        return false;
512
    }
513
514
    /**
515
     * {@inheritDoc}
516
     */
517 2
    public function implementsInterface($interfaceName)
518
    {
519 2
        $allInterfaces = $this->getInterfaces();
520
521 2
        return isset($allInterfaces[$interfaceName]);
522
    }
523
524
    /**
525
     * {@inheritDoc}
526
     */
527 2
    public function inNamespace()
528
    {
529 2
        return !empty($this->namespaceName);
530
    }
531
532
    /**
533
     * {@inheritDoc}
534
     */
535 2
    public function isAbstract()
536
    {
537 2
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
538 1
            return true;
539 2
        } elseif ($this->isInterface() && !empty($this->getMethods())) {
540
            return true;
541
        }
542
543 2
        return false;
544
    }
545
546
    /**
547
     * Currently, anonymous classes aren't supported for parsed reflection
548
     */
549
    public function isAnonymous()
550
    {
551
        return false;
552
    }
553
554
    /**
555
     * {@inheritDoc}
556
     */
557 2
    public function isCloneable()
558
    {
559 2
        if ($this->isInterface() || $this->isAbstract()) {
560 1
            return false;
561
        }
562
563 2
        if ($this->hasMethod('__clone')) {
564 1
            return $this->getMethod('__clone')->isPublic();
565
        }
566
567 2
        return true;
568
    }
569
570
    /**
571
     * {@inheritDoc}
572
     */
573 2
    public function isFinal()
574
    {
575 2
        $isFinal = $this->classLikeNode instanceof Class_ && $this->classLikeNode->isFinal();
576
577 2
        return $isFinal;
578
    }
579
580
    /**
581
     * {@inheritDoc}
582
     */
583
    public function isInstance($object)
584
    {
585
        if (!is_object($object)) {
586
            throw new \RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)));
587
        }
588
589
        $className = $this->getName();
590
591
        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...
592
    }
593
594
    /**
595
     * {@inheritDoc}
596
     */
597 2
    public function isInstantiable()
598
    {
599 2
        if ($this->isInterface() || $this->isAbstract()) {
600 1
            return false;
601
        }
602
603 2
        if (null === ($constructor = $this->getConstructor())) {
604 2
            return true;
605
        }
606
607 1
        return $constructor->isPublic();
608
    }
609
610
    /**
611
     * {@inheritDoc}
612
     */
613 2
    public function isInterface()
614
    {
615 2
        return ($this->classLikeNode instanceof Interface_);
616
    }
617
618
    /**
619
     * {@inheritDoc}
620
     */
621 2
    public function isInternal()
622
    {
623
        // never can be an internal method
624 2
        return false;
625
    }
626
627
    /**
628
     * {@inheritDoc}
629
     */
630 2
    public function isIterateable()
631
    {
632 2
        return $this->implementsInterface('Traversable');
633
    }
634
635
    /**
636
     * {@inheritDoc}
637
     */
638
    public function isSubclassOf($class)
639
    {
640
        if (is_object($class)) {
641
            if ($class instanceof ReflectionClass) {
642
                $class = $class->name;
643
            } else {
644
                $class = get_class($class);
645
            }
646
        }
647
648
        if (!$this->classLikeNode instanceof Class_) {
649
            return false;
650
        } else {
651
            $extends = $this->classLikeNode->extends;
652
            if ($extends && $extends->toString() == $class) {
653
                return true;
654
            }
655
        }
656
657
        $parent = $this->getParentClass();
658
659
        return false === $parent ? false : $parent->isSubclassOf($class);
660
    }
661
662
    /**
663
     * {@inheritDoc}
664
     */
665 2
    public function isTrait()
666
    {
667 2
        return ($this->classLikeNode instanceof Trait_);
668
    }
669
670
    /**
671
     * {@inheritDoc}
672
     */
673 2
    public function isUserDefined()
674
    {
675
        // always defined by user, because we parse the source code
676 2
        return true;
677
    }
678
679
    /**
680
     * Gets static properties
681
     *
682
     * @link http://php.net/manual/en/reflectionclass.getstaticproperties.php
683
     *
684
     * @return array
685
     */
686 3
    public function getStaticProperties()
687
    {
688
        // In runtime static properties can be changed in any time
689 3
        if ($this->isInitialized()) {
690 1
            return forward_static_call('parent::getStaticProperties');
691
        }
692
693 2
        $properties = [];
694
695 2
        $reflectionProperties = $this->getProperties(ReflectionProperty::IS_STATIC);
696 2
        foreach ($reflectionProperties as $reflectionProperty) {
697 1
            if (!$reflectionProperty instanceof ReflectionProperty) {
698 1
                if (!$reflectionProperty->isPublic()) {
699 1
                    $reflectionProperty->setAccessible(true);
700 1
                }
701 1
            }
702 1
            $properties[$reflectionProperty->getName()] = $reflectionProperty->getValue();
703 2
        }
704
705 2
        return $properties;
706
    }
707
708
    /**
709
     * Gets static property value
710
     *
711
     * @param string $name    The name of the static property for which to return a value.
712
     * @param mixed  $default A default value to return in case the class does not declare
713
     *                        a static property with the given name
714
     *
715
     * @return mixed
716
     * @throws ReflectionException If there is no such property and no default value was given
717
     */
718 1
    public function getStaticPropertyValue($name, $default = null)
719
    {
720 1
        $properties     = $this->getStaticProperties();
721 1
        $propertyExists = array_key_exists($name, $properties);
722
723 1
        if (!$propertyExists && func_num_args() === 1) {
724
            throw new ReflectionException("Static property does not exist and no default value is given");
725
        }
726
727 1
        return $propertyExists ? $properties[$name] : $default;
728
    }
729
730
731
    /**
732
     * Creates a new class instance from given arguments.
733
     *
734
     * @link http://php.net/manual/en/reflectionclass.newinstance.php
735
     * @param mixed $args Accepts a variable number of arguments which are passed to the class constructor
736
     *
737
     * @return object
738
     */
739 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...
740
    {
741 1
        $this->initializeInternalReflection();
742
743 1
        return call_user_func_array('parent::newInstance', func_get_args());
744
    }
745
746
    /**
747
     * Creates a new class instance from given arguments.
748
     *
749
     * @link http://php.net/manual/en/reflectionclass.newinstanceargs.php
750
     *
751
     * @param array $args The parameters to be passed to the class constructor as an array.
752
     *
753
     * @return object
754
     */
755 1
    public function newInstanceArgs(array $args = [])
756
    {
757 1
        $function = __FUNCTION__;
758 1
        $this->initializeInternalReflection();
759
760 1
        return parent::$function($args);
761
    }
762
763
    /**
764
     * Creates a new class instance without invoking the constructor.
765
     *
766
     * @link http://php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php
767
     *
768
     * @return object
769
     */
770 1
    public function newInstanceWithoutConstructor($args = null)
771
    {
772 1
        $function = __FUNCTION__;
773 1
        $this->initializeInternalReflection();
774
775 1
        return parent::$function($args);
776
    }
777
778
    /**
779
     * Sets static property value
780
     *
781
     * @link http://php.net/manual/en/reflectionclass.setstaticpropertyvalue.php
782
     *
783
     * @param string $name Property name
784
     * @param mixed $value New property value
785
     */
786 1
    public function setStaticPropertyValue($name, $value)
787
    {
788 1
        $this->initializeInternalReflection();
789
790 1
        forward_static_call('parent::setStaticPropertyValue', $name, $value);
791 1
    }
792
793 9
    private function recursiveCollect(\Closure $collector)
794
    {
795 9
        $result = array();
796
797 9
        $parentClass = $this->getParentClass();
798 9
        if ($parentClass) {
799 3
            $collector($result, $parentClass);
800 3
        }
801
802 9
        $interfaces = ReflectionClass::collectInterfacesFromClassNode($this->classLikeNode);
803 9
        foreach ($interfaces as $interface) {
804 1
            $collector($result, $interface);
805 9
        }
806
807 9
        return $result;
808
    }
809
810
    /**
811
     * Returns list of constants from the class
812
     *
813
     * @return array
814
     */
815 3
    private function findConstants()
816
    {
817 3
        $constants        = array();
818 3
        $expressionSolver = new NodeExpressionResolver($this);
819
820
        // constants can be only top-level nodes in the class, so we can scan them directly
821 3
        foreach ($this->classLikeNode->stmts as $classLevelNode) {
822 3
            if ($classLevelNode instanceof ClassConst) {
823 3
                $nodeConstants = $classLevelNode->consts;
824 3
                if (!empty($nodeConstants)) {
825 3
                    foreach ($nodeConstants as $nodeConstant) {
826 3
                        $expressionSolver->process($nodeConstant->value);
827 3
                        $constants[$nodeConstant->name] = $expressionSolver->getValue();
828 3
                    }
829 3
                }
830 3
            }
831 3
        }
832
833 3
        return $constants;
834
    }
835
}
836