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