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