1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Reflection\Adapter;
6:
7: use Closure;
8: use OutOfBoundsException;
9: use ReflectionClass as CoreReflectionClass;
10: use ReflectionException as CoreReflectionException;
11: use ReflectionExtension as CoreReflectionExtension;
12: use ReflectionMethod as CoreReflectionMethod;
13: use ReflectionType as CoreReflectionType;
14: use ReturnTypeWillChange;
15: use PHPStan\BetterReflection\Reflection\Adapter\Exception\NotImplemented;
16: use PHPStan\BetterReflection\Reflection\Exception\MethodPrototypeNotFound;
17: use PHPStan\BetterReflection\Reflection\Exception\NoObjectProvided;
18: use PHPStan\BetterReflection\Reflection\ReflectionAttribute as BetterReflectionAttribute;
19: use PHPStan\BetterReflection\Reflection\ReflectionMethod as BetterReflectionMethod;
20: use PHPStan\BetterReflection\Reflection\ReflectionParameter as BetterReflectionParameter;
21: use PHPStan\BetterReflection\Util\FileHelper;
22: use Throwable;
23: use TypeError;
24: use ValueError;
25:
26: use function array_map;
27: use function sprintf;
28:
29: final class ReflectionMethod extends CoreReflectionMethod
30: {
31: /**
32: * @var BetterReflectionMethod
33: */
34: private $betterReflectionMethod;
35: public function __construct(BetterReflectionMethod $betterReflectionMethod)
36: {
37: $this->betterReflectionMethod = $betterReflectionMethod;
38: unset($this->name);
39: unset($this->class);
40: }
41:
42: public function __toString(): string
43: {
44: return $this->betterReflectionMethod->__toString();
45: }
46:
47: public function inNamespace(): bool
48: {
49: return $this->betterReflectionMethod->inNamespace();
50: }
51:
52: public function isClosure(): bool
53: {
54: return $this->betterReflectionMethod->isClosure();
55: }
56:
57: public function isDeprecated(): bool
58: {
59: return $this->betterReflectionMethod->isDeprecated();
60: }
61:
62: public function isInternal(): bool
63: {
64: return $this->betterReflectionMethod->isInternal();
65: }
66:
67: public function isUserDefined(): bool
68: {
69: return $this->betterReflectionMethod->isUserDefined();
70: }
71:
72: /**
73: * {@inheritDoc}
74: */
75: #[ReturnTypeWillChange]
76: public function getClosureThis()
77: {
78: throw new NotImplemented('Not implemented');
79: }
80:
81: public function getClosureScopeClass(): ?CoreReflectionClass
82: {
83: throw new NotImplemented('Not implemented');
84: }
85:
86: public function getClosureCalledClass(): ?CoreReflectionClass
87: {
88: throw new NotImplemented('Not implemented');
89: }
90:
91: /**
92: * {@inheritDoc}
93: */
94: #[ReturnTypeWillChange]
95: public function getDocComment()
96: {
97: return $this->betterReflectionMethod->getDocComment() ?? false;
98: }
99:
100: /**
101: * {@inheritDoc}
102: */
103: #[ReturnTypeWillChange]
104: public function getStartLine()
105: {
106: return $this->betterReflectionMethod->getStartLine();
107: }
108:
109: /**
110: * {@inheritDoc}
111: */
112: #[ReturnTypeWillChange]
113: public function getEndLine()
114: {
115: return $this->betterReflectionMethod->getEndLine();
116: }
117:
118: /** @psalm-suppress ImplementedReturnTypeMismatch */
119: public function getExtension(): ?CoreReflectionExtension
120: {
121: throw new NotImplemented('Not implemented');
122: }
123:
124: /**
125: * {@inheritDoc}
126: */
127: #[ReturnTypeWillChange]
128: public function getExtensionName()
129: {
130: return $this->betterReflectionMethod->getExtensionName() ?? '';
131: }
132:
133: /**
134: * {@inheritDoc}
135: */
136: #[ReturnTypeWillChange]
137: public function getFileName()
138: {
139: $fileName = $this->betterReflectionMethod->getFileName();
140:
141: return $fileName !== null ? FileHelper::normalizeSystemPath($fileName) : false;
142: }
143:
144: public function getName(): string
145: {
146: return $this->betterReflectionMethod->getName();
147: }
148:
149: public function getNamespaceName(): string
150: {
151: return $this->betterReflectionMethod->getNamespaceName() ?? '';
152: }
153:
154: public function getNumberOfParameters(): int
155: {
156: return $this->betterReflectionMethod->getNumberOfParameters();
157: }
158:
159: public function getNumberOfRequiredParameters(): int
160: {
161: return $this->betterReflectionMethod->getNumberOfRequiredParameters();
162: }
163:
164: /** @return list<ReflectionParameter> */
165: public function getParameters(): array
166: {
167: return array_map(static function (BetterReflectionParameter $parameter) : ReflectionParameter {
168: return new ReflectionParameter($parameter);
169: }, $this->betterReflectionMethod->getParameters());
170: }
171:
172: public function hasReturnType(): bool
173: {
174: return $this->betterReflectionMethod->hasReturnType();
175: }
176:
177: /** @return ReflectionUnionType|ReflectionNamedType|ReflectionIntersectionType|null */
178: public function getReturnType(): ?CoreReflectionType
179: {
180: return ReflectionType::fromTypeOrNull($this->betterReflectionMethod->getReturnType());
181: }
182:
183: public function getShortName(): string
184: {
185: return $this->betterReflectionMethod->getShortName();
186: }
187:
188: /** @return array<string, scalar> */
189: public function getStaticVariables(): array
190: {
191: throw new NotImplemented('Not implemented');
192: }
193:
194: public function returnsReference(): bool
195: {
196: return $this->betterReflectionMethod->returnsReference();
197: }
198:
199: public function isGenerator(): bool
200: {
201: return $this->betterReflectionMethod->isGenerator();
202: }
203:
204: public function isVariadic(): bool
205: {
206: return $this->betterReflectionMethod->isVariadic();
207: }
208:
209: public function isPublic(): bool
210: {
211: return $this->betterReflectionMethod->isPublic();
212: }
213:
214: public function isPrivate(): bool
215: {
216: return $this->betterReflectionMethod->isPrivate();
217: }
218:
219: public function isProtected(): bool
220: {
221: return $this->betterReflectionMethod->isProtected();
222: }
223:
224: public function isAbstract(): bool
225: {
226: return $this->betterReflectionMethod->isAbstract();
227: }
228:
229: public function isFinal(): bool
230: {
231: return $this->betterReflectionMethod->isFinal();
232: }
233:
234: public function isStatic(): bool
235: {
236: return $this->betterReflectionMethod->isStatic();
237: }
238:
239: public function isConstructor(): bool
240: {
241: return $this->betterReflectionMethod->isConstructor();
242: }
243:
244: public function isDestructor(): bool
245: {
246: return $this->betterReflectionMethod->isDestructor();
247: }
248:
249: /**
250: * {@inheritDoc}
251: */
252: public function getClosure($object = null): Closure
253: {
254: try {
255: return $this->betterReflectionMethod->getClosure($object);
256: } catch (NoObjectProvided $e) {
257: throw new ValueError($e->getMessage(), 0, $e);
258: } catch (Throwable $e) {
259: throw new CoreReflectionException($e->getMessage(), 0, $e);
260: }
261: }
262:
263: public function getModifiers(): int
264: {
265: return $this->betterReflectionMethod->getModifiers();
266: }
267:
268: /**
269: * @param object $object
270: * @param mixed $arg
271: * @param mixed ...$args
272: *
273: * @return mixed
274: *
275: * @throws CoreReflectionException
276: */
277: #[ReturnTypeWillChange]
278: public function invoke($object = null, $arg = null, ...$args)
279: {
280: try {
281: return $this->betterReflectionMethod->invoke($object, $arg, ...$args);
282: } catch (NoObjectProvided | TypeError $exception) {
283: return null;
284: } catch (Throwable $e) {
285: throw new CoreReflectionException($e->getMessage(), 0, $e);
286: }
287: }
288:
289: /**
290: * @param object $object
291: * @param mixed[] $args
292: *
293: * @return mixed
294: *
295: * @throws CoreReflectionException
296: */
297: #[ReturnTypeWillChange]
298: public function invokeArgs($object = null, array $args = [])
299: {
300: try {
301: return $this->betterReflectionMethod->invokeArgs($object, $args);
302: } catch (NoObjectProvided | TypeError $exception) {
303: return null;
304: } catch (Throwable $e) {
305: throw new CoreReflectionException($e->getMessage(), 0, $e);
306: }
307: }
308:
309: public function getDeclaringClass(): ReflectionClass
310: {
311: return new ReflectionClass($this->betterReflectionMethod->getImplementingClass());
312: }
313:
314: public function getPrototype(): ReflectionMethod
315: {
316: return new self($this->betterReflectionMethod->getPrototype());
317: }
318:
319: public function hasPrototype(): bool
320: {
321: try {
322: $this->betterReflectionMethod->getPrototype();
323:
324: return true;
325: } catch (MethodPrototypeNotFound $exception) {
326: return false;
327: }
328: }
329:
330: /**
331: * {@inheritDoc}
332: * @codeCoverageIgnore
333: * @infection-ignore-all
334: */
335: public function setAccessible($accessible): void
336: {
337: }
338:
339: /**
340: * @param class-string|null $name
341: *
342: * @return list<ReflectionAttribute|FakeReflectionAttribute>
343: */
344: public function getAttributes(?string $name = null, int $flags = 0): array
345: {
346: if ($flags !== 0 && $flags !== ReflectionAttribute::IS_INSTANCEOF) {
347: throw new ValueError('Argument #2 ($flags) must be a valid attribute filter flag');
348: }
349:
350: if ($name !== null && $flags !== 0) {
351: $attributes = $this->betterReflectionMethod->getAttributesByInstance($name);
352: } elseif ($name !== null) {
353: $attributes = $this->betterReflectionMethod->getAttributesByName($name);
354: } else {
355: $attributes = $this->betterReflectionMethod->getAttributes();
356: }
357:
358: return array_map(static function (BetterReflectionAttribute $betterReflectionAttribute) {
359: return ReflectionAttributeFactory::create($betterReflectionAttribute);
360: }, $attributes);
361: }
362:
363: public function hasTentativeReturnType(): bool
364: {
365: return $this->betterReflectionMethod->hasTentativeReturnType();
366: }
367:
368: /** @return ReflectionUnionType|ReflectionNamedType|ReflectionIntersectionType|null */
369: public function getTentativeReturnType(): ?CoreReflectionType
370: {
371: return ReflectionType::fromTypeOrNull($this->betterReflectionMethod->getTentativeReturnType());
372: }
373:
374: /** @return mixed[] */
375: public function getClosureUsedVariables(): array
376: {
377: throw new Exception\NotImplemented('Not implemented');
378: }
379:
380: /**
381: * @return mixed
382: */
383: public function __get(string $name)
384: {
385: if ($name === 'name') {
386: return $this->betterReflectionMethod->getName();
387: }
388:
389: if ($name === 'class') {
390: return $this->betterReflectionMethod->getImplementingClass()->getName();
391: }
392:
393: throw new OutOfBoundsException(sprintf('Property %s::$%s does not exist.', self::class, $name));
394: }
395:
396: public function getBetterReflection(): BetterReflectionMethod
397: {
398: return $this->betterReflectionMethod;
399: }
400: }
401: