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