1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Reflection\Adapter;
6:
7: use OutOfBoundsException;
8: use PhpParser\Node\Expr;
9: use ReflectionClassConstant as CoreReflectionClassConstant;
10: use ReflectionType as CoreReflectionType;
11: use ReturnTypeWillChange;
12: use PHPStan\BetterReflection\Reflection\ReflectionAttribute as BetterReflectionAttribute;
13: use PHPStan\BetterReflection\Reflection\ReflectionClassConstant as BetterReflectionClassConstant;
14: use PHPStan\BetterReflection\Reflection\ReflectionEnumCase as BetterReflectionEnumCase;
15: use ValueError;
16:
17: use function array_map;
18: use function sprintf;
19:
20: /**
21: * @psalm-suppress PropertyNotSetInConstructor
22: * @psalm-immutable
23: */
24: final class ReflectionClassConstant extends CoreReflectionClassConstant
25: {
26:
27: /**
28: * @var BetterReflectionClassConstant|BetterReflectionEnumCase
29: */
30: private $betterClassConstantOrEnumCase;
31: /** @internal */
32: public const IS_PUBLIC_COMPATIBILITY = 1;
33:
34: /** @internal */
35: public const IS_PROTECTED_COMPATIBILITY = 2;
36:
37: /** @internal */
38: public const IS_PRIVATE_COMPATIBILITY = 4;
39:
40: /** @internal */
41: public const IS_FINAL_COMPATIBILITY = 32;
42:
43: /**
44: * @param BetterReflectionClassConstant|BetterReflectionEnumCase $betterClassConstantOrEnumCase
45: */
46: public function __construct($betterClassConstantOrEnumCase)
47: {
48: $this->betterClassConstantOrEnumCase = $betterClassConstantOrEnumCase;
49: unset($this->name);
50: unset($this->class);
51: }
52:
53: public function getName(): string
54: {
55: return $this->betterClassConstantOrEnumCase->getName();
56: }
57:
58: /**
59: * @return BetterReflectionClassConstant|BetterReflectionEnumCase
60: */
61: public function getBetterReflection()
62: {
63: return $this->betterClassConstantOrEnumCase;
64: }
65:
66: /** @psalm-mutation-free */
67: public function hasType(): bool
68: {
69: if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
70: return false;
71: }
72:
73: return $this->betterClassConstantOrEnumCase->hasType();
74: }
75:
76: /**
77: * @psalm-mutation-free
78: * @return ReflectionUnionType|ReflectionNamedType|ReflectionIntersectionType|null
79: */
80: public function getType(): ?CoreReflectionType
81: {
82: if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
83: return null;
84: }
85:
86: return ReflectionType::fromTypeOrNull($this->betterClassConstantOrEnumCase->getType());
87: }
88:
89: #[ReturnTypeWillChange]
90: public function getValue()
91: {
92: if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
93: throw new Exception\NotImplemented('Not implemented');
94: }
95:
96: return $this->betterClassConstantOrEnumCase->getValue();
97: }
98:
99: public function getValueExpression(): Expr
100: {
101: return $this->betterClassConstantOrEnumCase->getValueExpression();
102: }
103:
104: public function isPublic(): bool
105: {
106: if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
107: return true;
108: }
109:
110: return $this->betterClassConstantOrEnumCase->isPublic();
111: }
112:
113: public function isPrivate(): bool
114: {
115: if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
116: return false;
117: }
118:
119: return $this->betterClassConstantOrEnumCase->isPrivate();
120: }
121:
122: public function isProtected(): bool
123: {
124: if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
125: return false;
126: }
127:
128: return $this->betterClassConstantOrEnumCase->isProtected();
129: }
130:
131: public function getModifiers(): int
132: {
133: if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
134: return ReflectionClassConstant::IS_PUBLIC_COMPATIBILITY;
135: }
136:
137: return $this->betterClassConstantOrEnumCase->getModifiers();
138: }
139:
140: public function getDeclaringClass(): ReflectionClass
141: {
142: if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
143: return new ReflectionClass($this->betterClassConstantOrEnumCase->getDeclaringClass());
144: }
145:
146: return new ReflectionClass($this->betterClassConstantOrEnumCase->getImplementingClass());
147: }
148:
149: /**
150: * Returns the doc comment for this constant
151: *
152: * @return string|false
153: */
154: #[ReturnTypeWillChange]
155: public function getDocComment()
156: {
157: return $this->betterClassConstantOrEnumCase->getDocComment() ?? false;
158: }
159:
160: /**
161: * To string
162: *
163: * @link https://php.net/manual/en/reflector.tostring.php
164: *
165: * @return non-empty-string
166: */
167: public function __toString(): string
168: {
169: return $this->betterClassConstantOrEnumCase->__toString();
170: }
171:
172: /**
173: * @param class-string|null $name
174: *
175: * @return list<ReflectionAttribute|FakeReflectionAttribute>
176: */
177: public function getAttributes(?string $name = null, int $flags = 0): array
178: {
179: if ($flags !== 0 && $flags !== ReflectionAttribute::IS_INSTANCEOF) {
180: throw new ValueError('Argument #2 ($flags) must be a valid attribute filter flag');
181: }
182:
183: if ($name !== null && $flags !== 0) {
184: $attributes = $this->betterClassConstantOrEnumCase->getAttributesByInstance($name);
185: } elseif ($name !== null) {
186: $attributes = $this->betterClassConstantOrEnumCase->getAttributesByName($name);
187: } else {
188: $attributes = $this->betterClassConstantOrEnumCase->getAttributes();
189: }
190:
191: /** @psalm-suppress ImpureFunctionCall */
192: return array_map(static fn (BetterReflectionAttribute $betterReflectionAttribute) => ReflectionAttributeFactory::create($betterReflectionAttribute), $attributes);
193: }
194:
195: public function isFinal(): bool
196: {
197: if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
198: return true;
199: }
200:
201: return $this->betterClassConstantOrEnumCase->isFinal();
202: }
203:
204: public function isEnumCase(): bool
205: {
206: return $this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase;
207: }
208:
209: public function isDeprecated(): bool
210: {
211: return $this->betterClassConstantOrEnumCase->isDeprecated();
212: }
213:
214: /**
215: * @return mixed
216: */
217: public function __get(string $name)
218: {
219: if ($name === 'name') {
220: return $this->betterClassConstantOrEnumCase->getName();
221: }
222:
223: if ($name === 'class') {
224: return $this->getDeclaringClass()->getName();
225: }
226:
227: throw new OutOfBoundsException(sprintf('Property %s::$%s does not exist.', self::class, $name));
228: }
229: }
230: