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\EnumCase;
10: use PHPStan\BetterReflection\NodeCompiler\CompiledValue;
11: use PHPStan\BetterReflection\NodeCompiler\CompileNodeToValue;
12: use PHPStan\BetterReflection\NodeCompiler\CompilerContext;
13: use PHPStan\BetterReflection\Reflection\Annotation\AnnotationHelper;
14: use PHPStan\BetterReflection\Reflection\Attribute\ReflectionAttributeHelper;
15: use PHPStan\BetterReflection\Reflection\StringCast\ReflectionEnumCaseStringCast;
16: use PHPStan\BetterReflection\Reflector\Reflector;
17: use PHPStan\BetterReflection\Util\CalculateReflectionColumn;
18: use PHPStan\BetterReflection\Util\GetLastDocComment;
19:
20: use function assert;
21: use function is_int;
22: use function is_string;
23:
24: /** @psalm-immutable */
25: class ReflectionEnumCase
26: {
27: /** @var non-empty-string */
28: private $name;
29:
30: /**
31: * @var \PhpParser\Node\Expr|null
32: */
33: private $value;
34:
35: /** @var list<ReflectionAttribute> */
36: private $attributes;
37:
38: /** @var non-empty-string|null */
39: private $docComment;
40:
41: /** @var positive-int */
42: private $startLine;
43:
44: /** @var positive-int */
45: private $endLine;
46:
47: /** @var positive-int */
48: private $startColumn;
49:
50: /** @var positive-int */
51: private $endColumn;
52:
53: /** @psalm-allow-private-mutation
54: * @var \PHPStan\BetterReflection\NodeCompiler\CompiledValue|null */
55: private $compiledValue = null;
56: /**
57: * @var \PHPStan\BetterReflection\Reflector\Reflector
58: */
59: private $reflector;
60: /**
61: * @var \PHPStan\BetterReflection\Reflection\ReflectionEnum
62: */
63: private $enum;
64: private function __construct(Reflector $reflector, EnumCase $node, ReflectionEnum $enum)
65: {
66: $this->reflector = $reflector;
67: $this->enum = $enum;
68: $name = $node->name->toString();
69: assert($name !== '');
70: $this->name = $name;
71: $this->value = $node->expr;
72: $this->attributes = ReflectionAttributeHelper::createAttributes($reflector, $this, $node->attrGroups);
73: $this->docComment = GetLastDocComment::forNode($node);
74: $startLine = $node->getStartLine();
75: assert($startLine > 0);
76: $endLine = $node->getEndLine();
77: assert($endLine > 0);
78: $this->startLine = $startLine;
79: $this->endLine = $endLine;
80: $this->startColumn = CalculateReflectionColumn::getStartColumn($this->enum->getLocatedSource()->getSource(), $node);
81: $this->endColumn = CalculateReflectionColumn::getEndColumn($this->enum->getLocatedSource()->getSource(), $node);
82: }
83: /** @internal */
84: public static function createFromNode(Reflector $reflector, EnumCase $node, ReflectionEnum $enum): self
85: {
86: return new self($reflector, $node, $enum);
87: }
88:
89: /** @return non-empty-string */
90: public function getName(): string
91: {
92: return $this->name;
93: }
94:
95: /**
96: * @deprecated Use getValueExpression()
97: */
98: public function getValueExpr(): Node\Expr
99: {
100: return $this->getValueExpression();
101: }
102:
103: /**
104: * Check ReflectionEnum::isBacked() being true first to avoid throwing exception.
105: *
106: * @throws LogicException
107: */
108: public function getValueExpression(): Node\Expr
109: {
110: if ($this->value === null) {
111: throw new LogicException('This enum case does not have a value');
112: }
113:
114: return $this->value;
115: }
116:
117: /**
118: * @return string|int
119: */
120: public function getValue()
121: {
122: $value = $this->getCompiledValue()->value;
123: assert(is_string($value) || is_int($value));
124:
125: return $value;
126: }
127:
128: /**
129: * Check ReflectionEnum::isBacked() being true first to avoid throwing exception.
130: *
131: * @throws LogicException
132: */
133: private function getCompiledValue(): CompiledValue
134: {
135: if ($this->value === null) {
136: throw new LogicException('This enum case does not have a value');
137: }
138:
139: if ($this->compiledValue === null) {
140: $this->compiledValue = (new CompileNodeToValue())->__invoke($this->value, new CompilerContext($this->reflector, $this));
141: }
142:
143: return $this->compiledValue;
144: }
145:
146: /** @return positive-int */
147: public function getStartLine(): int
148: {
149: return $this->startLine;
150: }
151:
152: /** @return positive-int */
153: public function getEndLine(): int
154: {
155: return $this->endLine;
156: }
157:
158: /** @return positive-int */
159: public function getStartColumn(): int
160: {
161: return $this->startColumn;
162: }
163:
164: /** @return positive-int */
165: public function getEndColumn(): int
166: {
167: return $this->endColumn;
168: }
169:
170: public function getDeclaringEnum(): ReflectionEnum
171: {
172: return $this->enum;
173: }
174:
175: public function getDeclaringClass(): ReflectionClass
176: {
177: return $this->enum;
178: }
179:
180: /** @return non-empty-string|null */
181: public function getDocComment(): ?string
182: {
183: return $this->docComment;
184: }
185:
186: public function isDeprecated(): bool
187: {
188: return AnnotationHelper::isDeprecated($this->docComment);
189: }
190:
191: /** @return list<ReflectionAttribute> */
192: public function getAttributes(): array
193: {
194: return $this->attributes;
195: }
196:
197: /** @return list<ReflectionAttribute> */
198: public function getAttributesByName(string $name): array
199: {
200: return ReflectionAttributeHelper::filterAttributesByName($this->getAttributes(), $name);
201: }
202:
203: /**
204: * @param class-string $className
205: *
206: * @return list<ReflectionAttribute>
207: */
208: public function getAttributesByInstance(string $className): array
209: {
210: return ReflectionAttributeHelper::filterAttributesByInstance($this->getAttributes(), $className);
211: }
212:
213: /** @return non-empty-string */
214: public function __toString(): string
215: {
216: return ReflectionEnumCaseStringCast::toString($this);
217: }
218: }
219: