blob: 7dab190c33591b6b0c9103d60bfaca35534fb36d [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3declare(strict_types=1);
4
5namespace Carbon\PHPStan;
6
7use Closure;
8use PHPStan\Reflection\Php\BuiltinMethodReflection;
9use PHPStan\TrinaryLogic;
10use ReflectionClass;
11use ReflectionFunction;
12use ReflectionMethod;
13use ReflectionParameter;
14use ReflectionType;
15use stdClass;
16use Throwable;
17
18final class Macro implements BuiltinMethodReflection
19{
20 /**
21 * The class name.
22 *
23 * @var class-string
24 */
25 private $className;
26
27 /**
28 * The method name.
29 *
30 * @var string
31 */
32 private $methodName;
33
34 /**
35 * The reflection function/method.
36 *
37 * @var ReflectionFunction|ReflectionMethod
38 */
39 private $reflectionFunction;
40
41 /**
42 * The parameters.
43 *
44 * @var ReflectionParameter[]
45 */
46 private $parameters;
47
48 /**
49 * The is static.
50 *
51 * @var bool
52 */
53 private $static = false;
54
55 /**
56 * Macro constructor.
57 *
58 * @param string $className
59 * @phpstan-param class-string $className
60 *
61 * @param string $methodName
62 * @param callable $macro
63 */
64 public function __construct(string $className, string $methodName, $macro)
65 {
66 $this->className = $className;
67 $this->methodName = $methodName;
68 $this->reflectionFunction = \is_array($macro)
69 ? new ReflectionMethod($macro[0], $macro[1])
70 : new ReflectionFunction($macro);
71 $this->parameters = $this->reflectionFunction->getParameters();
72
73 if ($this->reflectionFunction->isClosure()) {
74 try {
75 /** @var Closure $closure */
76 $closure = $this->reflectionFunction->getClosure();
77 $boundClosure = Closure::bind($closure, new stdClass);
78 $this->static = (!$boundClosure || (new ReflectionFunction($boundClosure))->getClosureThis() === null);
79 } catch (Throwable $e) {
80 $this->static = true;
81 }
82 }
83 }
84
85 /**
86 * {@inheritdoc}
87 */
88 public function getDeclaringClass(): ReflectionClass
89 {
90 return new ReflectionClass($this->className);
91 }
92
93 /**
94 * {@inheritdoc}
95 */
96 public function isPrivate(): bool
97 {
98 return false;
99 }
100
101 /**
102 * {@inheritdoc}
103 */
104 public function isPublic(): bool
105 {
106 return true;
107 }
108
109 /**
110 * {@inheritdoc}
111 */
112 public function isFinal(): bool
113 {
114 return false;
115 }
116
117 /**
118 * {@inheritdoc}
119 */
120 public function isInternal(): bool
121 {
122 return false;
123 }
124
125 /**
126 * {@inheritdoc}
127 */
128 public function isAbstract(): bool
129 {
130 return false;
131 }
132
133 /**
134 * {@inheritdoc}
135 */
136 public function isStatic(): bool
137 {
138 return $this->static;
139 }
140
141 /**
142 * {@inheritdoc}
143 */
144 public function getDocComment(): ?string
145 {
146 return $this->reflectionFunction->getDocComment() ?: null;
147 }
148
149 /**
150 * {@inheritdoc}
151 */
152 public function getFileName()
153 {
154 return $this->reflectionFunction->getFileName();
155 }
156
157 /**
158 * {@inheritdoc}
159 */
160 public function getName(): string
161 {
162 return $this->methodName;
163 }
164
165 /**
166 * {@inheritdoc}
167 */
168 public function getParameters(): array
169 {
170 return $this->parameters;
171 }
172
173 /**
174 * {@inheritdoc}
175 */
176 public function getReturnType(): ?ReflectionType
177 {
178 return $this->reflectionFunction->getReturnType();
179 }
180
181 /**
182 * {@inheritdoc}
183 */
184 public function getStartLine()
185 {
186 return $this->reflectionFunction->getStartLine();
187 }
188
189 /**
190 * {@inheritdoc}
191 */
192 public function getEndLine()
193 {
194 return $this->reflectionFunction->getEndLine();
195 }
196
197 /**
198 * {@inheritdoc}
199 */
200 public function isDeprecated(): TrinaryLogic
201 {
202 return TrinaryLogic::createFromBoolean(
203 $this->reflectionFunction->isDeprecated() ||
204 preg_match('/@deprecated/i', $this->getDocComment() ?: '')
205 );
206 }
207
208 /**
209 * {@inheritdoc}
210 */
211 public function isVariadic(): bool
212 {
213 return $this->reflectionFunction->isVariadic();
214 }
215
216 /**
217 * {@inheritdoc}
218 */
219 public function getPrototype(): BuiltinMethodReflection
220 {
221 return $this;
222 }
223
224 /**
225 * {@inheritdoc}
226 */
227 public function getReflection(): ?ReflectionMethod
228 {
229 return $this->reflectionFunction instanceof ReflectionMethod
230 ? $this->reflectionFunction
231 : null;
232 }
233}