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