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