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: *
102: * @psalm-mutation-free
103: */
104: #[ReturnTypeWillChange]
105: public function getStartLine()
106: {
107: return $this->betterReflectionFunction->getStartLine();
108: }
109:
110: /**
111: * {@inheritDoc}
112: *
113: * @psalm-mutation-free
114: */
115: #[ReturnTypeWillChange]
116: public function getEndLine()
117: {
118: return $this->betterReflectionFunction->getEndLine();
119: }
120:
121: /** @psalm-suppress ImplementedReturnTypeMismatch */
122: public function getExtension(): ?CoreReflectionExtension
123: {
124: throw new NotImplemented('Not implemented');
125: }
126:
127: /**
128: * {@inheritDoc}
129: */
130: #[ReturnTypeWillChange]
131: public function getExtensionName()
132: {
133: return $this->betterReflectionFunction->getExtensionName() ?? false;
134: }
135:
136: /**
137: * {@inheritDoc}
138: */
139: #[ReturnTypeWillChange]
140: public function getFileName()
141: {
142: $fileName = $this->betterReflectionFunction->getFileName();
143:
144: return $fileName !== null ? FileHelper::normalizeSystemPath($fileName) : false;
145: }
146:
147: public function getName(): string
148: {
149: return $this->betterReflectionFunction->getName();
150: }
151:
152: public function getNamespaceName(): string
153: {
154: return $this->betterReflectionFunction->getNamespaceName() ?? '';
155: }
156:
157: public function getNumberOfParameters(): int
158: {
159: return $this->betterReflectionFunction->getNumberOfParameters();
160: }
161:
162: public function getNumberOfRequiredParameters(): int
163: {
164: return $this->betterReflectionFunction->getNumberOfRequiredParameters();
165: }
166:
167: /** @return list<ReflectionParameter> */
168: public function getParameters(): array
169: {
170: return array_map(
171: static fn (BetterReflectionParameter $parameter): ReflectionParameter => ReflectionParameterFactory::create($parameter),
172: $this->betterReflectionFunction->getParameters(),
173: );
174: }
175:
176: public function hasReturnType(): bool
177: {
178: return $this->betterReflectionFunction->hasReturnType();
179: }
180:
181: /** @return ReflectionUnionType|ReflectionNamedType|ReflectionIntersectionType|null */
182: public function getReturnType(): ?CoreReflectionType
183: {
184: return ReflectionType::fromTypeOrNull($this->betterReflectionFunction->getReturnType());
185: }
186:
187: public function getShortName(): string
188: {
189: return $this->betterReflectionFunction->getShortName();
190: }
191:
192: /** @return array<string, scalar> */
193: public function getStaticVariables(): array
194: {
195: throw new NotImplemented('Not implemented');
196: }
197:
198: public function returnsReference(): bool
199: {
200: return $this->betterReflectionFunction->returnsReference();
201: }
202:
203: public function isGenerator(): bool
204: {
205: return $this->betterReflectionFunction->isGenerator();
206: }
207:
208: public function isVariadic(): bool
209: {
210: return $this->betterReflectionFunction->isVariadic();
211: }
212:
213: public function isDisabled(): bool
214: {
215: return $this->betterReflectionFunction->isDisabled();
216: }
217:
218: /**
219: * @param mixed $arg
220: * @param mixed ...$args
221: *
222: * @return mixed
223: *
224: * @throws CoreReflectionException
225: */
226: #[ReturnTypeWillChange]
227: public function invoke($arg = null, ...$args)
228: {
229: try {
230: return $this->betterReflectionFunction->invoke(...func_get_args());
231: } catch (Throwable $e) {
232: throw new CoreReflectionException($e->getMessage(), 0, $e);
233: }
234: }
235:
236: /** @param mixed[] $args */
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: