1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Reflection;
6:
7: use PhpParser\Node;
8: use PhpParser\Node\Stmt\ClassConst;
9: use ReflectionClassConstant as CoreReflectionClassConstant;
10: use PHPStan\BetterReflection\BetterReflection;
11: use PHPStan\BetterReflection\NodeCompiler\CompiledValue;
12: use PHPStan\BetterReflection\NodeCompiler\CompileNodeToValue;
13: use PHPStan\BetterReflection\NodeCompiler\CompilerContext;
14: use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClassConstant as ReflectionClassConstantAdapter;
15: use PHPStan\BetterReflection\Reflection\Annotation\AnnotationHelper;
16: use PHPStan\BetterReflection\Reflection\Attribute\ReflectionAttributeHelper;
17: use PHPStan\BetterReflection\Reflection\StringCast\ReflectionClassConstantStringCast;
18: use PHPStan\BetterReflection\Reflector\Reflector;
19: use PHPStan\BetterReflection\Util\CalculateReflectionColumn;
20: use PHPStan\BetterReflection\Util\GetLastDocComment;
21:
22: use function array_map;
23: use function assert;
24:
25: /** @psalm-immutable */
26: class ReflectionClassConstant
27: {
28: /** @var non-empty-string */
29: private $name;
30:
31: /** @var int-mask-of<ReflectionClassConstantAdapter::IS_*> */
32: private $modifiers;
33:
34: /**
35: * @var \PHPStan\BetterReflection\Reflection\ReflectionNamedType|\PHPStan\BetterReflection\Reflection\ReflectionUnionType|\PHPStan\BetterReflection\Reflection\ReflectionIntersectionType|null
36: */
37: private $type;
38:
39: /**
40: * @var \PhpParser\Node\Expr
41: */
42: private $value;
43:
44: /** @var non-empty-string|null */
45: private $docComment;
46:
47: /** @var list<ReflectionAttribute> */
48: private $attributes;
49:
50: /** @var positive-int */
51: private $startLine;
52:
53: /** @var positive-int */
54: private $endLine;
55:
56: /** @var positive-int */
57: private $startColumn;
58:
59: /** @var positive-int */
60: private $endColumn;
61:
62: /** @psalm-allow-private-mutation
63: * @var \PHPStan\BetterReflection\NodeCompiler\CompiledValue|null */
64: private $compiledValue = null;
65: /**
66: * @var \PHPStan\BetterReflection\Reflector\Reflector
67: */
68: private $reflector;
69: /**
70: * @var \PHPStan\BetterReflection\Reflection\ReflectionClass
71: */
72: private $declaringClass;
73: /**
74: * @var \PHPStan\BetterReflection\Reflection\ReflectionClass
75: */
76: private $implementingClass;
77: private function __construct(Reflector $reflector, ClassConst $node, int $positionInNode, ReflectionClass $declaringClass, ReflectionClass $implementingClass)
78: {
79: $this->reflector = $reflector;
80: $this->declaringClass = $declaringClass;
81: $this->implementingClass = $implementingClass;
82: $this->name = $node->consts[$positionInNode]->name->name;
83: $this->modifiers = $this->computeModifiers($node);
84: $this->type = $this->createType($node);
85: $this->value = $node->consts[$positionInNode]->value;
86: $this->docComment = GetLastDocComment::forNode($node);
87: $this->attributes = ReflectionAttributeHelper::createAttributes($reflector, $this, $node->attrGroups);
88: $startLine = $node->getStartLine();
89: assert($startLine > 0);
90: $endLine = $node->getEndLine();
91: assert($endLine > 0);
92: $this->startLine = $startLine;
93: $this->endLine = $endLine;
94: $this->startColumn = CalculateReflectionColumn::getStartColumn($declaringClass->getLocatedSource()->getSource(), $node);
95: $this->endColumn = CalculateReflectionColumn::getEndColumn($declaringClass->getLocatedSource()->getSource(), $node);
96: }
97: /**
98: * Create a reflection of a class's constant by Const Node
99: *
100: * @internal
101: */
102: public static function createFromNode(Reflector $reflector, ClassConst $node, int $positionInNode, ReflectionClass $declaringClass, ReflectionClass $implementingClass): self
103: {
104: return new self($reflector, $node, $positionInNode, $declaringClass, $implementingClass);
105: }
106:
107: /** @internal */
108: public function withImplementingClass(ReflectionClass $implementingClass): self
109: {
110: $clone = clone $this;
111: $clone->implementingClass = $implementingClass;
112:
113: $clone->attributes = array_map(static function (ReflectionAttribute $attribute) use ($clone) : ReflectionAttribute {
114: return $attribute->withOwner($clone);
115: }, $this->attributes);
116:
117: $this->compiledValue = null;
118:
119: return $clone;
120: }
121:
122: /**
123: * Get the name of the reflection (e.g. if this is a ReflectionClass this
124: * will be the class name).
125: *
126: * @return non-empty-string
127: */
128: public function getName(): string
129: {
130: return $this->name;
131: }
132:
133: /**
134: * @return \PHPStan\BetterReflection\Reflection\ReflectionNamedType|\PHPStan\BetterReflection\Reflection\ReflectionUnionType|\PHPStan\BetterReflection\Reflection\ReflectionIntersectionType|null
135: */
136: private function createType(ClassConst $node)
137: {
138: $type = $node->type;
139:
140: if ($type === null) {
141: return null;
142: }
143:
144: assert($type instanceof Node\Identifier || $type instanceof Node\Name || $type instanceof Node\NullableType || $type instanceof Node\UnionType || $type instanceof Node\IntersectionType);
145:
146: return ReflectionType::createFromNode($this->reflector, $this, $type);
147: }
148:
149: /**
150: * @return \PHPStan\BetterReflection\Reflection\ReflectionNamedType|\PHPStan\BetterReflection\Reflection\ReflectionUnionType|\PHPStan\BetterReflection\Reflection\ReflectionIntersectionType|null
151: */
152: public function getType()
153: {
154: return $this->type;
155: }
156:
157: public function hasType(): bool
158: {
159: return $this->type !== null;
160: }
161:
162: public function getValueExpression(): Node\Expr
163: {
164: return $this->value;
165: }
166:
167: /**
168: * Returns constant value
169: * @return mixed
170: */
171: public function getValue()
172: {
173: if ($this->compiledValue === null) {
174: $this->compiledValue = (new CompileNodeToValue())->__invoke($this->value, new CompilerContext($this->reflector, $this));
175: }
176:
177: return $this->compiledValue->value;
178: }
179:
180: /**
181: * Constant is public
182: */
183: public function isPublic(): bool
184: {
185: return ($this->modifiers & ReflectionClassConstantAdapter::IS_PUBLIC_COMPATIBILITY) === ReflectionClassConstantAdapter::IS_PUBLIC_COMPATIBILITY;
186: }
187:
188: /**
189: * Constant is private
190: */
191: public function isPrivate(): bool
192: {
193: // Private constant cannot be final
194: return $this->modifiers === ReflectionClassConstantAdapter::IS_PRIVATE_COMPATIBILITY;
195: }
196:
197: /**
198: * Constant is protected
199: */
200: public function isProtected(): bool
201: {
202: return ($this->modifiers & ReflectionClassConstantAdapter::IS_PROTECTED_COMPATIBILITY) === ReflectionClassConstantAdapter::IS_PROTECTED_COMPATIBILITY;
203: }
204:
205: public function isFinal(): bool
206: {
207: $final = ($this->modifiers & ReflectionClassConstantAdapter::IS_FINAL_COMPATIBILITY) === ReflectionClassConstantAdapter::IS_FINAL_COMPATIBILITY;
208: if ($final) {
209: return true;
210: }
211:
212: if (BetterReflection::$phpVersion >= 80100) {
213: return false;
214: }
215:
216: return $this->getDeclaringClass()->isInterface();
217: }
218:
219: /**
220: * Returns a bitfield of the access modifiers for this constant
221: *
222: * @return int-mask-of<ReflectionClassConstantAdapter::IS_*>
223: */
224: public function getModifiers(): int
225: {
226: return $this->modifiers;
227: }
228:
229: /**
230: * Get the line number that this constant starts on.
231: *
232: * @return positive-int
233: */
234: public function getStartLine(): int
235: {
236: return $this->startLine;
237: }
238:
239: /**
240: * Get the line number that this constant ends on.
241: *
242: * @return positive-int
243: */
244: public function getEndLine(): int
245: {
246: return $this->endLine;
247: }
248:
249: /** @return positive-int */
250: public function getStartColumn(): int
251: {
252: return $this->startColumn;
253: }
254:
255: /** @return positive-int */
256: public function getEndColumn(): int
257: {
258: return $this->endColumn;
259: }
260:
261: /**
262: * Get the declaring class
263: */
264: public function getDeclaringClass(): ReflectionClass
265: {
266: return $this->declaringClass;
267: }
268:
269: /**
270: * Get the class that implemented the method based on trait use.
271: */
272: public function getImplementingClass(): ReflectionClass
273: {
274: return $this->implementingClass;
275: }
276:
277: /** @return non-empty-string|null */
278: public function getDocComment(): ?string
279: {
280: return $this->docComment;
281: }
282:
283: public function isDeprecated(): bool
284: {
285: return AnnotationHelper::isDeprecated($this->getDocComment());
286: }
287:
288: /** @return non-empty-string */
289: public function __toString(): string
290: {
291: return ReflectionClassConstantStringCast::toString($this);
292: }
293:
294: /** @return list<ReflectionAttribute> */
295: public function getAttributes(): array
296: {
297: return $this->attributes;
298: }
299:
300: /** @return list<ReflectionAttribute> */
301: public function getAttributesByName(string $name): array
302: {
303: return ReflectionAttributeHelper::filterAttributesByName($this->getAttributes(), $name);
304: }
305:
306: /**
307: * @param class-string $className
308: *
309: * @return list<ReflectionAttribute>
310: */
311: public function getAttributesByInstance(string $className): array
312: {
313: return ReflectionAttributeHelper::filterAttributesByInstance($this->getAttributes(), $className);
314: }
315:
316: /** @return int-mask-of<ReflectionClassConstantAdapter::IS_*> */
317: private function computeModifiers(ClassConst $node): int
318: {
319: $modifiers = $node->isFinal() ? ReflectionClassConstantAdapter::IS_FINAL_COMPATIBILITY : 0;
320: $modifiers += $node->isPrivate() ? ReflectionClassConstantAdapter::IS_PRIVATE_COMPATIBILITY : 0;
321: $modifiers += $node->isProtected() ? ReflectionClassConstantAdapter::IS_PROTECTED_COMPATIBILITY : 0;
322: $modifiers += $node->isPublic() ? ReflectionClassConstantAdapter::IS_PUBLIC_COMPATIBILITY : 0;
323:
324: return $modifiers;
325: }
326: }
327: