1: | <?php |
2: | |
3: | declare(strict_types=1); |
4: | |
5: | namespace PHPStan\BetterReflection\Reflection\Adapter; |
6: | |
7: | use LogicException; |
8: | use OutOfBoundsException; |
9: | use PhpParser\Node\Expr; |
10: | use ReflectionClass as CoreReflectionClass; |
11: | use ReflectionFunctionAbstract as CoreReflectionFunctionAbstract; |
12: | use ReflectionParameter as CoreReflectionParameter; |
13: | use ReturnTypeWillChange; |
14: | use PHPStan\BetterReflection\Reflection\ReflectionAttribute as BetterReflectionAttribute; |
15: | use PHPStan\BetterReflection\Reflection\ReflectionIntersectionType as BetterReflectionIntersectionType; |
16: | use PHPStan\BetterReflection\Reflection\ReflectionMethod as BetterReflectionMethod; |
17: | use PHPStan\BetterReflection\Reflection\ReflectionNamedType as BetterReflectionNamedType; |
18: | use PHPStan\BetterReflection\Reflection\ReflectionParameter as BetterReflectionParameter; |
19: | use PHPStan\BetterReflection\Reflection\ReflectionType as BetterReflectionType; |
20: | use PHPStan\BetterReflection\Reflection\ReflectionUnionType as BetterReflectionUnionType; |
21: | use ValueError; |
22: | |
23: | use function array_map; |
24: | use function count; |
25: | use function sprintf; |
26: | use function strtolower; |
27: | |
28: | |
29: | final class ReflectionParameter extends CoreReflectionParameter |
30: | { |
31: | |
32: | |
33: | |
34: | private $betterReflectionParameter; |
35: | public function __construct(BetterReflectionParameter $betterReflectionParameter) |
36: | { |
37: | $this->betterReflectionParameter = $betterReflectionParameter; |
38: | unset($this->name); |
39: | } |
40: | |
41: | public function __toString(): string |
42: | { |
43: | return $this->betterReflectionParameter->__toString(); |
44: | } |
45: | |
46: | public function getName(): string |
47: | { |
48: | return $this->betterReflectionParameter->getName(); |
49: | } |
50: | |
51: | public function isPassedByReference(): bool |
52: | { |
53: | return $this->betterReflectionParameter->isPassedByReference(); |
54: | } |
55: | |
56: | public function canBePassedByValue(): bool |
57: | { |
58: | return $this->betterReflectionParameter->canBePassedByValue(); |
59: | } |
60: | |
61: | |
62: | |
63: | |
64: | public function getDeclaringFunction(): CoreReflectionFunctionAbstract |
65: | { |
66: | $function = $this->betterReflectionParameter->getDeclaringFunction(); |
67: | |
68: | if ($function instanceof BetterReflectionMethod) { |
69: | return new ReflectionMethod($function); |
70: | } |
71: | |
72: | return new ReflectionFunction($function); |
73: | } |
74: | |
75: | |
76: | public function getDeclaringClass(): ?CoreReflectionClass |
77: | { |
78: | $declaringClass = $this->betterReflectionParameter->getDeclaringClass(); |
79: | |
80: | if ($declaringClass === null) { |
81: | return null; |
82: | } |
83: | |
84: | return new ReflectionClass($declaringClass); |
85: | } |
86: | |
87: | |
88: | public function getClass(): ?CoreReflectionClass |
89: | { |
90: | $type = $this->betterReflectionParameter->getType(); |
91: | |
92: | if ($type === null) { |
93: | return null; |
94: | } |
95: | |
96: | if ($type instanceof BetterReflectionIntersectionType) { |
97: | return null; |
98: | } |
99: | |
100: | if ($type instanceof BetterReflectionNamedType) { |
101: | $classType = $type; |
102: | } else { |
103: | $unionTypes = $type->getTypes(); |
104: | |
105: | if (count($unionTypes) !== 2) { |
106: | return null; |
107: | } |
108: | |
109: | if (! $type->allowsNull()) { |
110: | return null; |
111: | } |
112: | |
113: | foreach ($unionTypes as $unionInnerType) { |
114: | if (! $unionInnerType instanceof BetterReflectionNamedType) { |
115: | return null; |
116: | } |
117: | |
118: | if ($unionInnerType->allowsNull()) { |
119: | continue; |
120: | } |
121: | |
122: | $classType = $unionInnerType; |
123: | break; |
124: | } |
125: | } |
126: | |
127: | try { |
128: | |
129: | return new ReflectionClass($classType->getClass()); |
130: | } catch (LogicException $exception) { |
131: | return null; |
132: | } |
133: | } |
134: | |
135: | public function isArray(): bool |
136: | { |
137: | return $this->isType($this->betterReflectionParameter->getType(), 'array'); |
138: | } |
139: | |
140: | public function isCallable(): bool |
141: | { |
142: | return $this->isType($this->betterReflectionParameter->getType(), 'callable'); |
143: | } |
144: | |
145: | |
146: | |
147: | |
148: | |
149: | private function isType($typeReflection, string $type): bool |
150: | { |
151: | if ($typeReflection === null) { |
152: | return false; |
153: | } |
154: | |
155: | if ($typeReflection instanceof BetterReflectionIntersectionType) { |
156: | return false; |
157: | } |
158: | |
159: | $isOneOfAllowedTypes = static function (BetterReflectionType $namedType, string ...$types): bool { |
160: | foreach ($types as $type) { |
161: | if ($namedType instanceof BetterReflectionNamedType && strtolower($namedType->getName()) === $type) { |
162: | return true; |
163: | } |
164: | } |
165: | |
166: | return false; |
167: | }; |
168: | |
169: | if ($typeReflection instanceof BetterReflectionUnionType) { |
170: | $unionTypes = $typeReflection->getTypes(); |
171: | |
172: | foreach ($unionTypes as $unionType) { |
173: | if (! $isOneOfAllowedTypes($unionType, $type, 'null')) { |
174: | return false; |
175: | } |
176: | } |
177: | |
178: | return true; |
179: | } |
180: | |
181: | return $isOneOfAllowedTypes($typeReflection, $type); |
182: | } |
183: | |
184: | public function allowsNull(): bool |
185: | { |
186: | return $this->betterReflectionParameter->allowsNull(); |
187: | } |
188: | |
189: | public function getPosition(): int |
190: | { |
191: | return $this->betterReflectionParameter->getPosition(); |
192: | } |
193: | |
194: | public function isOptional(): bool |
195: | { |
196: | return $this->betterReflectionParameter->isOptional(); |
197: | } |
198: | |
199: | public function isVariadic(): bool |
200: | { |
201: | return $this->betterReflectionParameter->isVariadic(); |
202: | } |
203: | |
204: | public function isDefaultValueAvailable(): bool |
205: | { |
206: | return $this->betterReflectionParameter->isDefaultValueAvailable(); |
207: | } |
208: | |
209: | |
210: | |
211: | |
212: | #[ReturnTypeWillChange] |
213: | public function getDefaultValue() |
214: | { |
215: | return $this->betterReflectionParameter->getDefaultValue(); |
216: | } |
217: | |
218: | |
219: | |
220: | |
221: | public function getDefaultValueExpr(): Expr |
222: | { |
223: | return $this->betterReflectionParameter->getDefaultValueExpression(); |
224: | } |
225: | |
226: | public function getDefaultValueExpression(): Expr |
227: | { |
228: | return $this->betterReflectionParameter->getDefaultValueExpression(); |
229: | } |
230: | |
231: | public function isDefaultValueConstant(): bool |
232: | { |
233: | return $this->betterReflectionParameter->isDefaultValueConstant(); |
234: | } |
235: | |
236: | public function getDefaultValueConstantName(): string |
237: | { |
238: | return $this->betterReflectionParameter->getDefaultValueConstantName(); |
239: | } |
240: | |
241: | public function hasType(): bool |
242: | { |
243: | return $this->betterReflectionParameter->hasType(); |
244: | } |
245: | |
246: | |
247: | |
248: | |
249: | public function getType(): ?\ReflectionType |
250: | { |
251: | return ReflectionType::fromTypeOrNull($this->betterReflectionParameter->getType()); |
252: | } |
253: | |
254: | public function isPromoted(): bool |
255: | { |
256: | return $this->betterReflectionParameter->isPromoted(); |
257: | } |
258: | |
259: | |
260: | |
261: | |
262: | |
263: | |
264: | public function getAttributes(?string $name = null, int $flags = 0): array |
265: | { |
266: | if ($flags !== 0 && $flags !== ReflectionAttribute::IS_INSTANCEOF) { |
267: | throw new ValueError('Argument #2 ($flags) must be a valid attribute filter flag'); |
268: | } |
269: | |
270: | if ($name !== null && $flags !== 0) { |
271: | $attributes = $this->betterReflectionParameter->getAttributesByInstance($name); |
272: | } elseif ($name !== null) { |
273: | $attributes = $this->betterReflectionParameter->getAttributesByName($name); |
274: | } else { |
275: | $attributes = $this->betterReflectionParameter->getAttributes(); |
276: | } |
277: | |
278: | return array_map(static function (BetterReflectionAttribute $betterReflectionAttribute) { |
279: | return ReflectionAttributeFactory::create($betterReflectionAttribute); |
280: | }, $attributes); |
281: | } |
282: | |
283: | |
284: | |
285: | |
286: | public function __get(string $name) |
287: | { |
288: | if ($name === 'name') { |
289: | return $this->betterReflectionParameter->getName(); |
290: | } |
291: | |
292: | throw new OutOfBoundsException(sprintf('Property %s::$%s does not exist.', self::class, $name)); |
293: | } |
294: | } |
295: | |