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 ReflectionFunction as CoreReflectionFunction;
13: use ReflectionType as CoreReflectionType;
14: use ReturnTypeWillChange;
15: use PHPStan\BetterReflection\Reflection\Adapter\Exception\NotImplemented;
16: use PHPStan\BetterReflection\Reflection\ReflectionAttribute as BetterReflectionAttribute;
17: use PHPStan\BetterReflection\Reflection\ReflectionFunction as BetterReflectionFunction;
18: use PHPStan\BetterReflection\Reflection\ReflectionParameter as BetterReflectionParameter;
19: use PHPStan\BetterReflection\Util\FileHelper;
20: use Throwable;
21: use ValueError;
22:
23: use function array_map;
24: use function func_get_args;
25: use function sprintf;
26:
27: final class ReflectionFunction extends CoreReflectionFunction
28: {
29: private BetterReflectionFunction $betterReflectionFunction;
30: public function __construct(BetterReflectionFunction $betterReflectionFunction)
31: {
32: $this->betterReflectionFunction = $betterReflectionFunction;
33: unset($this->name);
34: }
35:
36: public function __toString(): string
37: {
38: return $this->betterReflectionFunction->__toString();
39: }
40:
41: public function getBetterReflection(): BetterReflectionFunction
42: {
43: return $this->betterReflectionFunction;
44: }
45:
46: public function inNamespace(): bool
47: {
48: return $this->betterReflectionFunction->inNamespace();
49: }
50:
51: public function isClosure(): bool
52: {
53: return $this->betterReflectionFunction->isClosure();
54: }
55:
56: public function isDeprecated(): bool
57: {
58: return $this->betterReflectionFunction->isDeprecated();
59: }
60:
61: public function isInternal(): bool
62: {
63: return $this->betterReflectionFunction->isInternal();
64: }
65:
66: public function isUserDefined(): bool
67: {
68: return $this->betterReflectionFunction->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->betterReflectionFunction->getDocComment() ?? false;
97: }
98:
99: /**
100: * {@inheritDoc}
101: * @psalm-mutation-free
102: */
103: #[ReturnTypeWillChange]
104: public function getStartLine()
105: {
106: return $this->betterReflectionFunction->getStartLine();
107: }
108:
109: /**
110: * {@inheritDoc}
111: * @psalm-mutation-free
112: */
113: #[ReturnTypeWillChange]
114: public function getEndLine()
115: {
116: return $this->betterReflectionFunction->getEndLine();
117: }
118:
119: /** @psalm-suppress ImplementedReturnTypeMismatch */
120: public function getExtension(): ?CoreReflectionExtension
121: {
122: throw new NotImplemented('Not implemented');
123: }
124:
125: /**
126: * {@inheritDoc}
127: */
128: #[ReturnTypeWillChange]
129: public function getExtensionName()
130: {
131: return $this->betterReflectionFunction->getExtensionName() ?? false;
132: }
133:
134: /**
135: * {@inheritDoc}
136: */
137: #[ReturnTypeWillChange]
138: public function getFileName()
139: {
140: $fileName = $this->betterReflectionFunction->getFileName();
141:
142: return $fileName !== null ? FileHelper::normalizeSystemPath($fileName) : false;
143: }
144:
145: public function getName(): string
146: {
147: return $this->betterReflectionFunction->getName();
148: }
149:
150: public function getNamespaceName(): string
151: {
152: return $this->betterReflectionFunction->getNamespaceName() ?? '';
153: }
154:
155: public function getNumberOfParameters(): int
156: {
157: return $this->betterReflectionFunction->getNumberOfParameters();
158: }
159:
160: public function getNumberOfRequiredParameters(): int
161: {
162: return $this->betterReflectionFunction->getNumberOfRequiredParameters();
163: }
164:
165: /** @return list<ReflectionParameter> */
166: public function getParameters(): array
167: {
168: return array_map(
169: static fn (BetterReflectionParameter $parameter): ReflectionParameter => new ReflectionParameter($parameter),
170: $this->betterReflectionFunction->getParameters(),
171: );
172: }
173:
174: public function hasReturnType(): bool
175: {
176: return $this->betterReflectionFunction->hasReturnType();
177: }
178:
179: /** @return ReflectionUnionType|ReflectionNamedType|ReflectionIntersectionType|null */
180: public function getReturnType(): ?CoreReflectionType
181: {
182: return ReflectionType::fromTypeOrNull($this->betterReflectionFunction->getReturnType());
183: }
184:
185: public function getShortName(): string
186: {
187: return $this->betterReflectionFunction->getShortName();
188: }
189:
190: /** @return array<string, scalar> */
191: public function getStaticVariables(): array
192: {
193: throw new NotImplemented('Not implemented');
194: }
195:
196: public function returnsReference(): bool
197: {
198: return $this->betterReflectionFunction->returnsReference();
199: }
200:
201: public function isGenerator(): bool
202: {
203: return $this->betterReflectionFunction->isGenerator();
204: }
205:
206: public function isVariadic(): bool
207: {
208: return $this->betterReflectionFunction->isVariadic();
209: }
210:
211: public function isDisabled(): bool
212: {
213: return $this->betterReflectionFunction->isDisabled();
214: }
215:
216: /**
217: * @param mixed $arg
218: * @param mixed ...$args
219: *
220: * @return mixed
221: *
222: * @throws CoreReflectionException
223: */
224: #[ReturnTypeWillChange]
225: public function invoke($arg = null, ...$args)
226: {
227: try {
228: return $this->betterReflectionFunction->invoke(...func_get_args());
229: } catch (Throwable $e) {
230: throw new CoreReflectionException($e->getMessage(), 0, $e);
231: }
232: }
233:
234: /**
235: * @param mixed[] $args
236: */
237: #[ReturnTypeWillChange]
238: public function invokeArgs(array $args)
239: {
240: try {
241: return $this->betterReflectionFunction->invokeArgs($args);
242: } catch (Throwable $e) {
243: throw new CoreReflectionException($e->getMessage(), 0, $e);
244: }
245: }
246:
247: public function getClosure(): Closure
248: {
249: return $this->betterReflectionFunction->getClosure();
250: }
251:
252: /**
253: * @return never
254: *
255: * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
256: */
257: public function getClosureUsedVariables(): array
258: {
259: throw new Exception\NotImplemented('Not implemented');
260: }
261:
262: public function hasTentativeReturnType(): bool
263: {
264: return $this->betterReflectionFunction->hasTentativeReturnType();
265: }
266:
267: /** @return ReflectionUnionType|ReflectionNamedType|ReflectionIntersectionType|null */
268: public function getTentativeReturnType(): ?CoreReflectionType
269: {
270: return ReflectionType::fromTypeOrNull($this->betterReflectionFunction->getTentativeReturnType());
271: }
272:
273: public function isStatic(): bool
274: {
275: return $this->betterReflectionFunction->isStatic();
276: }
277:
278: /**
279: * @param class-string|null $name
280: *
281: * @return list<ReflectionAttribute|FakeReflectionAttribute>
282: */
283: public function getAttributes(?string $name = null, int $flags = 0): array
284: {
285: if ($flags !== 0 && $flags !== ReflectionAttribute::IS_INSTANCEOF) {
286: throw new ValueError('Argument #2 ($flags) must be a valid attribute filter flag');
287: }
288:
289: if ($name !== null && $flags !== 0) {
290: $attributes = $this->betterReflectionFunction->getAttributesByInstance($name);
291: } elseif ($name !== null) {
292: $attributes = $this->betterReflectionFunction->getAttributesByName($name);
293: } else {
294: $attributes = $this->betterReflectionFunction->getAttributes();
295: }
296:
297: return array_map(static fn (BetterReflectionAttribute $betterReflectionAttribute) => ReflectionAttributeFactory::create($betterReflectionAttribute), $attributes);
298: }
299:
300: /**
301: * @return mixed
302: */
303: public function __get(string $name)
304: {
305: if ($name === 'name') {
306: return $this->betterReflectionFunction->getName();
307: }
308:
309: throw new OutOfBoundsException(sprintf('Property %s::$%s does not exist.', self::class, $name));
310: }
311:
312: public function isAnonymous(): bool
313: {
314: return $this->betterReflectionFunction->isClosure();
315: }
316: }
317: