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