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