1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Reflection;
6:
7: use Attribute;
8: use LogicException;
9: use PhpParser\Node;
10: use PHPStan\BetterReflection\NodeCompiler\CompileNodeToValue;
11: use PHPStan\BetterReflection\NodeCompiler\CompilerContext;
12: use PHPStan\BetterReflection\Reflection\StringCast\ReflectionAttributeStringCast;
13: use PHPStan\BetterReflection\Reflector\Reflector;
14:
15: use function array_map;
16: use function assert;
17:
18: /** @psalm-immutable */
19: class ReflectionAttribute
20: {
21: /** @var non-empty-string */
22: private $name;
23:
24: /** @var array<int|string, Node\Expr> */
25: private $arguments = [];
26: /**
27: * @var \PHPStan\BetterReflection\Reflector\Reflector
28: */
29: private $reflector;
30: /**
31: * @var \PHPStan\BetterReflection\Reflection\ReflectionClass|\PHPStan\BetterReflection\Reflection\ReflectionMethod|\PHPStan\BetterReflection\Reflection\ReflectionFunction|\PHPStan\BetterReflection\Reflection\ReflectionClassConstant|\PHPStan\BetterReflection\Reflection\ReflectionEnumCase|\PHPStan\BetterReflection\Reflection\ReflectionProperty|\PHPStan\BetterReflection\Reflection\ReflectionParameter
32: */
33: private $owner;
34: /**
35: * @var bool
36: */
37: private $isRepeated;
38: /** @internal
39: * @param \PHPStan\BetterReflection\Reflection\ReflectionClass|\PHPStan\BetterReflection\Reflection\ReflectionMethod|\PHPStan\BetterReflection\Reflection\ReflectionFunction|\PHPStan\BetterReflection\Reflection\ReflectionClassConstant|\PHPStan\BetterReflection\Reflection\ReflectionEnumCase|\PHPStan\BetterReflection\Reflection\ReflectionProperty|\PHPStan\BetterReflection\Reflection\ReflectionParameter $owner */
40: public function __construct(Reflector $reflector, Node\Attribute $node, $owner, bool $isRepeated)
41: {
42: $this->reflector = $reflector;
43: $this->owner = $owner;
44: $this->isRepeated = $isRepeated;
45: $name = $node->name->toString();
46: assert($name !== '');
47: $this->name = $name;
48: foreach ($node->args as $argNo => $arg) {
49: $this->arguments[(($argName = $arg->name) ? $argName->toString() : null) ?? $argNo] = $arg->value;
50: }
51: }
52:
53: /** @internal
54: * @param \PHPStan\BetterReflection\Reflection\ReflectionClass|\PHPStan\BetterReflection\Reflection\ReflectionMethod|\PHPStan\BetterReflection\Reflection\ReflectionFunction|\PHPStan\BetterReflection\Reflection\ReflectionClassConstant|\PHPStan\BetterReflection\Reflection\ReflectionEnumCase|\PHPStan\BetterReflection\Reflection\ReflectionProperty|\PHPStan\BetterReflection\Reflection\ReflectionParameter $owner */
55: public function withOwner($owner): self
56: {
57: $clone = clone $this;
58: $clone->owner = $owner;
59:
60: return $clone;
61: }
62:
63: /** @return non-empty-string */
64: public function getName(): string
65: {
66: return $this->name;
67: }
68:
69: public function getClass(): ReflectionClass
70: {
71: return $this->reflector->reflectClass($this->getName());
72: }
73:
74: /** @return array<int|string, Node\Expr> */
75: public function getArgumentsExpressions(): array
76: {
77: return $this->arguments;
78: }
79:
80: /**
81: * @deprecated Use getArgumentsExpressions()
82: * @return array<int|string, mixed>
83: */
84: public function getArguments(): array
85: {
86: $compiler = new CompileNodeToValue();
87: $context = new CompilerContext($this->reflector, $this->owner);
88:
89: return array_map(static function (Node\Expr $value) use ($compiler, $context) {
90: return $compiler->__invoke($value, $context)->value;
91: }, $this->arguments);
92: }
93:
94: /** @return int-mask-of<Attribute::TARGET_*> */
95: public function getTarget(): int
96: {
97: switch (true) {
98: case $this->owner instanceof ReflectionClass:
99: return Attribute::TARGET_CLASS;
100: case $this->owner instanceof ReflectionFunction:
101: return Attribute::TARGET_FUNCTION;
102: case $this->owner instanceof ReflectionMethod:
103: return Attribute::TARGET_METHOD;
104: case $this->owner instanceof ReflectionProperty:
105: return Attribute::TARGET_PROPERTY;
106: case $this->owner instanceof ReflectionClassConstant:
107: return Attribute::TARGET_CLASS_CONSTANT;
108: case $this->owner instanceof ReflectionEnumCase:
109: return Attribute::TARGET_CLASS_CONSTANT;
110: case $this->owner instanceof ReflectionParameter:
111: return Attribute::TARGET_PARAMETER;
112: default:
113: throw new LogicException('unknown owner');
114: }
115: }
116:
117: public function isRepeated(): bool
118: {
119: return $this->isRepeated;
120: }
121:
122: /** @return non-empty-string */
123: public function __toString(): string
124: {
125: return ReflectionAttributeStringCast::toString($this);
126: }
127: }
128: