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