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