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: foreach ($node->args as $argNo => $arg) {
46: $this->arguments[(($argName = $arg->name) ? $argName->toString() : null) ?? $argNo] = $arg->value;
47: }
48: }
49:
50: /** @internal
51: * @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 */
52: public function withOwner($owner): self
53: {
54: $clone = clone $this;
55: $clone->owner = $owner;
56:
57: return $clone;
58: }
59:
60: /** @return non-empty-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 function (Node\Expr $value) use ($compiler, $context) {
86: return $compiler->__invoke($value, $context)->value;
87: }, $this->arguments);
88: }
89:
90: /** @return int-mask-of<Attribute::TARGET_*> */
91: public function getTarget(): int
92: {
93: switch (true) {
94: case $this->owner instanceof ReflectionClass:
95: return Attribute::TARGET_CLASS;
96: case $this->owner instanceof ReflectionFunction:
97: return Attribute::TARGET_FUNCTION;
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: