1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Reflection;
6:
7: use LogicException;
8: use PhpParser\Node;
9: use PhpParser\Node\Stmt\Class_ as ClassNode;
10: use PhpParser\Node\Stmt\Enum_ as EnumNode;
11: use PhpParser\Node\Stmt\Interface_ as InterfaceNode;
12: use PhpParser\Node\Stmt\Trait_ as TraitNode;
13: use PHPStan\BetterReflection\Reflector\Reflector;
14: use PHPStan\BetterReflection\SourceLocator\Located\LocatedSource;
15:
16: use function array_combine;
17: use function array_filter;
18: use function array_key_exists;
19: use function array_map;
20: use function assert;
21:
22: class ReflectionEnum extends ReflectionClass
23: {
24: /**
25: * @var \PHPStan\BetterReflection\Reflection\ReflectionNamedType|null
26: */
27: private $backingType;
28:
29: /** @var array<non-empty-string, ReflectionEnumCase> */
30: private $cases;
31: /**
32: * @var \PHPStan\BetterReflection\Reflector\Reflector
33: */
34: private $reflector;
35: /** @phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod.Found */
36: private function __construct(Reflector $reflector, EnumNode $node, LocatedSource $locatedSource, ?string $namespace = null)
37: {
38: $this->reflector = $reflector;
39: parent::__construct($reflector, $node, $locatedSource, $namespace);
40: $this->backingType = $this->createBackingType($node);
41: $this->cases = $this->createCases($node);
42: }
43: /**
44: * @internal
45: * @param ClassNode|InterfaceNode|TraitNode|EnumNode $node
46: * @return $this
47: */
48: public static function createFromNode(Reflector $reflector, $node, LocatedSource $locatedSource, ?string $namespace = null): \PHPStan\BetterReflection\Reflection\ReflectionClass
49: {
50: $node = $node;
51: assert($node instanceof EnumNode);
52: return new self($reflector, $node, $locatedSource, $namespace);
53: }
54:
55: /** @param non-empty-string $name */
56: public function hasCase(string $name): bool
57: {
58: return array_key_exists($name, $this->cases);
59: }
60:
61: /** @param non-empty-string $name */
62: public function getCase(string $name): ?\PHPStan\BetterReflection\Reflection\ReflectionEnumCase
63: {
64: return $this->cases[$name] ?? null;
65: }
66:
67: /** @return array<non-empty-string, ReflectionEnumCase> */
68: public function getCases(): array
69: {
70: return $this->cases;
71: }
72:
73: /** @return array<non-empty-string, ReflectionEnumCase> */
74: private function createCases(EnumNode $node): array
75: {
76: $enumCasesNodes = array_filter($node->stmts, static function (Node\Stmt $stmt) : bool {
77: return $stmt instanceof Node\Stmt\EnumCase;
78: });
79:
80: return array_combine(array_map(static function (Node\Stmt\EnumCase $enumCaseNode): string {
81: $enumCaseName = $enumCaseNode->name->toString();
82: assert($enumCaseName !== '');
83:
84: return $enumCaseName;
85: }, $enumCasesNodes), array_map(function (Node\Stmt\EnumCase $enumCaseNode) : ReflectionEnumCase {
86: return ReflectionEnumCase::createFromNode($this->reflector, $enumCaseNode, $this);
87: }, $enumCasesNodes));
88: }
89:
90: public function isBacked(): bool
91: {
92: return $this->backingType !== null;
93: }
94:
95: public function getBackingType(): ReflectionNamedType
96: {
97: if ($this->backingType === null) {
98: throw new LogicException('This enum does not have a backing type available');
99: }
100:
101: return $this->backingType;
102: }
103:
104: private function createBackingType(EnumNode $node): ?\PHPStan\BetterReflection\Reflection\ReflectionNamedType
105: {
106: if ($node->scalarType === null) {
107: return null;
108: }
109:
110: $backingType = ReflectionNamedType::createFromNode($this->reflector, $this, $node->scalarType);
111: assert($backingType instanceof ReflectionNamedType);
112:
113: return $backingType;
114: }
115: }
116: