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