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: 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: try {
107: return $this->betterReflectionMethod->getStartLine();
108: } catch (CodeLocationMissing $exception) {
109: return false;
110: }
111: }
112:
113: /**
114: * {@inheritDoc}
115: */
116: #[ReturnTypeWillChange]
117: public function getEndLine()
118: {
119: try {
120: return $this->betterReflectionMethod->getEndLine();
121: } catch (CodeLocationMissing $exception) {
122: return false;
123: }
124: }
125:
126: /** @psalm-suppress ImplementedReturnTypeMismatch */
127: public function getExtension(): ?CoreReflectionExtension
128: {
129: throw new NotImplemented('Not implemented');
130: }
131:
132: /**
133: * {@inheritDoc}
134: */
135: #[ReturnTypeWillChange]
136: public function getExtensionName()
137: {
138: return $this->betterReflectionMethod->getExtensionName() ?? '';
139: }
140:
141: /**
142: * {@inheritDoc}
143: */
144: #[ReturnTypeWillChange]
145: public function getFileName()
146: {
147: $fileName = $this->betterReflectionMethod->getFileName();
148:
149: return $fileName !== null ? FileHelper::normalizeSystemPath($fileName) : false;
150: }
151:
152: public function getName(): string
153: {
154: return $this->betterReflectionMethod->getName();
155: }
156:
157: public function getNamespaceName(): string
158: {
159: return $this->betterReflectionMethod->getNamespaceName() ?? '';
160: }
161:
162: public function getNumberOfParameters(): int
163: {
164: return $this->betterReflectionMethod->getNumberOfParameters();
165: }
166:
167: public function getNumberOfRequiredParameters(): int
168: {
169: return $this->betterReflectionMethod->getNumberOfRequiredParameters();
170: }
171:
172: /** @return list<ReflectionParameter> */
173: public function getParameters(): array
174: {
175: return array_map(static function (BetterReflectionParameter $parameter) : ReflectionParameter {
176: return new ReflectionParameter($parameter);
177: }, $this->betterReflectionMethod->getParameters());
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: */
343: public function setAccessible($accessible): void
344: {
345: }
346:
347: /**
348: * @param class-string|null $name
349: *
350: * @return list<ReflectionAttribute|FakeReflectionAttribute>
351: */
352: public function getAttributes(?string $name = null, int $flags = 0): array
353: {
354: if ($flags !== 0 && $flags !== ReflectionAttribute::IS_INSTANCEOF) {
355: throw new ValueError('Argument #2 ($flags) must be a valid attribute filter flag');
356: }
357:
358: if ($name !== null && $flags !== 0) {
359: $attributes = $this->betterReflectionMethod->getAttributesByInstance($name);
360: } elseif ($name !== null) {
361: $attributes = $this->betterReflectionMethod->getAttributesByName($name);
362: } else {
363: $attributes = $this->betterReflectionMethod->getAttributes();
364: }
365:
366: return array_map(static function (BetterReflectionAttribute $betterReflectionAttribute) {
367: return ReflectionAttributeFactory::create($betterReflectionAttribute);
368: }, $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: /** @return mixed[] */
383: public function getClosureUsedVariables(): array
384: {
385: throw new Exception\NotImplemented('Not implemented');
386: }
387:
388: /**
389: * @return mixed
390: */
391: public function __get(string $name)
392: {
393: if ($name === 'name') {
394: return $this->betterReflectionMethod->getName();
395: }
396:
397: if ($name === 'class') {
398: return $this->betterReflectionMethod->getImplementingClass()->getName();
399: }
400:
401: throw new OutOfBoundsException(sprintf('Property %s::$%s does not exist.', self::class, $name));
402: }
403:
404: public function getBetterReflection(): BetterReflectionMethod
405: {
406: return $this->betterReflectionMethod;
407: }
408: }
409: