This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * Spiral Framework. Cycle ProxyFactory |
||
5 | * |
||
6 | * @license MIT |
||
7 | * @author Valentin V (Vvval) |
||
8 | */ |
||
9 | |||
10 | declare(strict_types=1); |
||
11 | |||
12 | namespace Cycle\ORM\Promise\Declaration\Extractor; |
||
13 | |||
14 | use Cycle\ORM\Promise\Traverser; |
||
15 | use Cycle\ORM\Promise\Visitor\Declaration\FetchMethods; |
||
16 | use PhpParser\Builder\Param; |
||
17 | use PhpParser\Node; |
||
18 | use PhpParser\Parser; |
||
19 | use PhpParser\ParserFactory; |
||
20 | use ReflectionClass; |
||
21 | use ReflectionException; |
||
22 | use ReflectionMethod; |
||
23 | use ReflectionNamedType; |
||
24 | use ReflectionParameter; |
||
25 | use ReflectionType; |
||
26 | |||
27 | final class Methods |
||
28 | { |
||
29 | private const MAGIC_METHODS = [ |
||
30 | '__construct', |
||
31 | '__destruct', |
||
32 | '__call', |
||
33 | '__callStatic', |
||
34 | '__get', |
||
35 | '__set', |
||
36 | '__isset', |
||
37 | '__unset', |
||
38 | '__sleep', |
||
39 | '__wakeup', |
||
40 | '__toString', |
||
41 | '__invoke', |
||
42 | '__set_state', |
||
43 | '__debugInfo', |
||
44 | ]; |
||
45 | |||
46 | private const RESERVED_UNQUALIFIED_RETURN_TYPES = ['self', 'static', 'object']; |
||
47 | |||
48 | /** @varTraverser */ |
||
49 | private $traverser; |
||
50 | |||
51 | /** @var Parser */ |
||
52 | private $parser; |
||
53 | |||
54 | /** @var array */ |
||
55 | private $extendedMethodsCache = []; |
||
56 | |||
57 | /** |
||
58 | * @param Traverser $traverser |
||
59 | * @param Parser|null $parser |
||
60 | */ |
||
61 | public function __construct( |
||
62 | Traverser $traverser, |
||
63 | Parser $parser = null |
||
64 | ) { |
||
65 | $this->traverser = $traverser; |
||
66 | $this->parser = $parser ?? (new ParserFactory())->create(ParserFactory::ONLY_PHP7); |
||
67 | } |
||
68 | |||
69 | /** |
||
70 | * @param ReflectionClass $reflection |
||
71 | * @return array |
||
72 | * @throws ReflectionException |
||
73 | */ |
||
74 | public function getMethods(ReflectionClass $reflection): array |
||
75 | { |
||
76 | $parents = [$reflection->name => $reflection]; |
||
77 | foreach ($reflection->getMethods() as $method) { |
||
78 | $class = $method->getDeclaringClass(); |
||
79 | $parents[$class->name] = $class; |
||
80 | } |
||
81 | |||
82 | $methodNodes = $this->getExtendedMethodNodes($parents); |
||
83 | $methods = []; |
||
84 | |||
85 | foreach ($reflection->getMethods() as $method) { |
||
86 | if ($this->isIgnoredMethod($method)) { |
||
87 | continue; |
||
88 | } |
||
89 | |||
90 | $methods[] = new Node\Stmt\ClassMethod($method->name, [ |
||
91 | 'flags' => $this->packFlags($method), |
||
92 | 'returnType' => $this->defineReturnType($method), |
||
93 | 'params' => $this->packParams($method), |
||
94 | 'byRef' => $method->returnsReference(), |
||
95 | 'stmts' => !empty($methodNodes[$method->name]) ? $methodNodes[$method->name]->stmts : [] |
||
96 | ]); |
||
97 | } |
||
98 | |||
99 | return $methods; |
||
100 | } |
||
101 | |||
102 | /** |
||
103 | * @param ReflectionMethod $method |
||
104 | * @return bool |
||
105 | */ |
||
106 | private function isIgnoredMethod(ReflectionMethod $method): bool |
||
107 | { |
||
108 | return $method->isPrivate() |
||
109 | || $method->isStatic() |
||
110 | || $method->isFinal() |
||
111 | || $method->isAbstract() |
||
112 | || $this->isMagicMethod($method->name); |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * @param string $name |
||
117 | * @return bool |
||
118 | */ |
||
119 | private function isMagicMethod(string $name): bool |
||
120 | { |
||
121 | return in_array($name, self::MAGIC_METHODS, true); |
||
122 | } |
||
123 | |||
124 | /** |
||
125 | * @param ReflectionMethod $method |
||
126 | * @return int |
||
127 | */ |
||
128 | private function packFlags(ReflectionMethod $method): int |
||
129 | { |
||
130 | $flags = []; |
||
131 | if ($method->isPublic()) { |
||
132 | $flags[] = Node\Stmt\Class_::MODIFIER_PUBLIC; |
||
133 | } elseif ($method->isProtected()) { |
||
134 | $flags[] = Node\Stmt\Class_::MODIFIER_PROTECTED; |
||
135 | } |
||
136 | |||
137 | return array_reduce($flags, static function ($a, $b) { |
||
138 | return $a | $b; |
||
139 | }, 0); |
||
140 | } |
||
141 | |||
142 | /** |
||
143 | * @param ReflectionMethod $method |
||
144 | * @return Node|null |
||
145 | */ |
||
146 | private function defineReturnType(ReflectionMethod $method): ?Node |
||
147 | { |
||
148 | if (!$method->hasReturnType()) { |
||
149 | return null; |
||
150 | } |
||
151 | |||
152 | $name = $this->defineType($method, $method->getReturnType()); |
||
153 | |||
154 | return $name !== null ? new Node\Identifier($name) : null; |
||
155 | } |
||
156 | |||
157 | /** |
||
158 | * @param ReflectionMethod $method |
||
159 | * @return array |
||
160 | * @throws ReflectionException |
||
161 | */ |
||
162 | private function packParams(ReflectionMethod $method): array |
||
163 | { |
||
164 | $params = []; |
||
165 | foreach ($method->getParameters() as $parameter) { |
||
166 | $param = new Param($parameter->name); |
||
167 | |||
168 | if ($parameter->isDefaultValueAvailable()) { |
||
169 | $param->setDefault($parameter->getDefaultValue()); |
||
170 | } |
||
171 | |||
172 | if ($parameter->isVariadic()) { |
||
173 | $param->makeVariadic(); |
||
174 | } |
||
175 | |||
176 | if ($parameter->isPassedByReference()) { |
||
177 | $param->makeByRef(); |
||
178 | } |
||
179 | |||
180 | $type = $this->defineParamType($parameter, $method); |
||
181 | if ($type !== null) { |
||
182 | $param->setType($type); |
||
183 | } |
||
184 | |||
185 | $params[] = $param->getNode(); |
||
186 | } |
||
187 | |||
188 | return $params; |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * @param ReflectionParameter $parameter |
||
193 | * @param ReflectionMethod $method |
||
194 | * @return string|null |
||
195 | */ |
||
196 | private function defineParamType(ReflectionParameter $parameter, ReflectionMethod $method): ?string |
||
197 | { |
||
198 | if (!$parameter->hasType()) { |
||
199 | return null; |
||
200 | } |
||
201 | |||
202 | return $this->defineType($method, $parameter->getType()); |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * @param ReflectionMethod $method |
||
207 | * @param ReflectionType|null $type |
||
208 | * @return string|null |
||
209 | */ |
||
210 | private function defineType(ReflectionMethod $method, ?ReflectionType $type): ?string |
||
211 | { |
||
212 | if ($type instanceof ReflectionNamedType) { |
||
213 | return $this->defineSingularType($method, $type); |
||
214 | } |
||
215 | |||
216 | if ($type instanceof \ReflectionUnionType) { |
||
217 | $types = array_map( |
||
218 | function (ReflectionNamedType $type) use ($method): ?string { |
||
219 | return $this->defineSingularType($method, $type); |
||
220 | }, |
||
221 | $type->getTypes() |
||
222 | ); |
||
223 | |||
224 | if ($types) { |
||
0 ignored issues
–
show
|
|||
225 | return implode('|', array_filter($types)); |
||
226 | } |
||
227 | } |
||
228 | |||
229 | return null; |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * @param ReflectionMethod $method |
||
234 | * @param ReflectionNamedType $type |
||
235 | * @return string|null |
||
236 | */ |
||
237 | private function defineSingularType(ReflectionMethod $method, ReflectionNamedType $type): ?string |
||
238 | { |
||
239 | $name = $type->getName(); |
||
240 | $name = $this->replacedSelfTypeName($method, $name); |
||
241 | |||
242 | if ($this->typeShouldBeQualified($type, $name)) { |
||
243 | $name = '\\' . $name; |
||
244 | } |
||
245 | |||
246 | if ($name !== 'mixed' && $type->allowsNull()) { |
||
247 | $name = "?$name"; |
||
248 | } |
||
249 | |||
250 | return $name; |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * @param ReflectionMethod $method |
||
255 | * @param string $name |
||
256 | * @return string |
||
257 | */ |
||
258 | private function replacedSelfTypeName(ReflectionMethod $method, string $name): string |
||
259 | { |
||
260 | return $name === 'self' ? $method->class : $name; |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * @param ReflectionType $returnType |
||
265 | * @param string $name |
||
266 | * @return bool |
||
267 | */ |
||
268 | private function typeShouldBeQualified(ReflectionType $returnType, string $name): bool |
||
269 | { |
||
270 | if (in_array($name, self::RESERVED_UNQUALIFIED_RETURN_TYPES, true)) { |
||
271 | return false; |
||
272 | } |
||
273 | |||
274 | return !$returnType->isBuiltin(); |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * @param ReflectionClass[] $reflections |
||
279 | * @return Node\Stmt\ClassMethod[] |
||
280 | */ |
||
281 | private function getExtendedMethodNodes(array $reflections): array |
||
282 | { |
||
283 | $nodes = []; |
||
284 | foreach ($reflections as $reflection) { |
||
285 | $fileName = $reflection->getFileName(); |
||
286 | if (!$fileName) { |
||
287 | continue; |
||
288 | } |
||
289 | |||
290 | if (!isset($this->extendedMethodsCache[$fileName])) { |
||
291 | $this->extendedMethodsCache[$fileName] = $this->fetchReflectionMethods($fileName); |
||
292 | } |
||
293 | |||
294 | foreach ($this->extendedMethodsCache[$fileName] as $name => $method) { |
||
295 | if (!isset($nodes[$name])) { |
||
296 | $nodes[$name] = $method; |
||
297 | } |
||
298 | } |
||
299 | } |
||
300 | |||
301 | return $nodes; |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * @param string $fileName |
||
306 | * @return array |
||
307 | */ |
||
308 | private function fetchReflectionMethods(string $fileName): array |
||
309 | { |
||
310 | if (!is_file($fileName)) { |
||
311 | return []; |
||
312 | } |
||
313 | |||
314 | $methods = new FetchMethods(); |
||
315 | $this->traverser->traverse( |
||
316 | $this->parser->parse(file_get_contents($fileName)), |
||
317 | $methods |
||
318 | ); |
||
319 | |||
320 | return $methods->getMethods(); |
||
321 | } |
||
322 | } |
||
323 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.