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