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