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 | declare(strict_types=1); |
||
3 | |||
4 | namespace WyriHaximus\ApiClient\Tools; |
||
5 | |||
6 | use Aura\Cli\Context; |
||
7 | use Aura\Cli\Stdio; |
||
8 | use Doctrine\Common\Inflector\Inflector; |
||
9 | use Exception; |
||
10 | use PhpParser\Builder\Method; |
||
11 | use PhpParser\Builder\Property; |
||
12 | use PhpParser\BuilderFactory; |
||
13 | use PhpParser\PrettyPrinter; |
||
14 | use PhpParser\Node; |
||
15 | use Symfony\Component\Yaml\Yaml; |
||
16 | use Symfony\CS\Config\Config; |
||
17 | use Symfony\CS\ConfigAwareInterface; |
||
18 | use Symfony\CS\ConfigInterface; |
||
19 | use Symfony\CS\FileCacheManager; |
||
20 | use Symfony\CS\Fixer; |
||
21 | use Symfony\CS\FixerInterface; |
||
22 | use WyriHaximus\ApiClient\Annotations\Collection; |
||
23 | use WyriHaximus\ApiClient\Annotations\Nested; |
||
24 | use WyriHaximus\ApiClient\Annotations\Rename; |
||
25 | use WyriHaximus\ApiClient\Resource\ResourceInterface; |
||
26 | |||
27 | class ResourceGenerator |
||
28 | { |
||
29 | /** |
||
30 | * @var Context |
||
31 | */ |
||
32 | protected $context; |
||
33 | |||
34 | /** |
||
35 | * @var Stdio |
||
36 | */ |
||
37 | protected $stdio; |
||
38 | |||
39 | /** |
||
40 | * @var array |
||
41 | */ |
||
42 | protected $definitions = []; |
||
43 | |||
44 | /** |
||
45 | * @var string |
||
46 | */ |
||
47 | protected $path; |
||
48 | |||
49 | /** |
||
50 | * @var Fixer |
||
51 | */ |
||
52 | protected $fixer; |
||
53 | |||
54 | /** |
||
55 | * @var array |
||
56 | */ |
||
57 | protected $fixers; |
||
58 | |||
59 | 2 | public function __construct(Context $context, Stdio $stdio) |
|
60 | { |
||
61 | 2 | $this->context = $context; |
|
62 | 2 | $this->stdio = $stdio; |
|
63 | |||
64 | 2 | $this->setUpArguments(); |
|
65 | 2 | $this->setUpFixers(); |
|
66 | 2 | } |
|
67 | |||
68 | 2 | protected function setUpArguments() |
|
69 | { |
||
70 | 2 | $getOpt = $this->context->getopt([]); |
|
71 | 2 | $i = 0; |
|
72 | do { |
||
73 | 2 | $i++; |
|
74 | 2 | $opt = $getOpt->get($i); |
|
75 | 2 | if ($opt === null) { |
|
76 | 2 | break; |
|
77 | } |
||
78 | 2 | $this->definitions[] = $opt; |
|
79 | 2 | } while (true); |
|
80 | 2 | $this->path = array_pop($this->definitions); |
|
81 | 2 | } |
|
82 | |||
83 | 2 | protected function setUpFixers() |
|
84 | { |
||
85 | 2 | $this->fixer = new Fixer(); |
|
86 | 2 | $this->fixer->registerCustomFixers([ |
|
87 | 2 | new Fixer\Symfony\ExtraEmptyLinesFixer(), |
|
88 | 2 | new Fixer\Symfony\SingleBlankLineBeforeNamespaceFixer(), |
|
89 | 2 | new Fixer\PSR0\Psr0Fixer(), |
|
90 | 2 | new Fixer\PSR1\EncodingFixer(), |
|
91 | 2 | new Fixer\PSR1\ShortTagFixer(), |
|
92 | 2 | new Fixer\PSR2\BracesFixer(), |
|
93 | 2 | new Fixer\PSR2\ElseifFixer(), |
|
94 | 2 | new Fixer\PSR2\EofEndingFixer(), |
|
95 | 2 | new Fixer\PSR2\FunctionCallSpaceFixer(), |
|
96 | 2 | new Fixer\PSR2\FunctionDeclarationFixer(), |
|
97 | 2 | new Fixer\PSR2\IndentationFixer(), |
|
98 | 2 | new Fixer\PSR2\LineAfterNamespaceFixer(), |
|
99 | 2 | new Fixer\PSR2\LinefeedFixer(), |
|
100 | 2 | new Fixer\PSR2\LowercaseConstantsFixer(), |
|
101 | 2 | new Fixer\PSR2\LowercaseKeywordsFixer(), |
|
102 | 2 | new Fixer\PSR2\MethodArgumentSpaceFixer(), |
|
103 | 2 | new Fixer\PSR2\MultipleUseFixer(), |
|
104 | 2 | new Fixer\PSR2\ParenthesisFixer(), |
|
105 | 2 | new Fixer\PSR2\PhpClosingTagFixer(), |
|
106 | 2 | new Fixer\PSR2\SingleLineAfterImportsFixer(), |
|
107 | 2 | new Fixer\PSR2\TrailingSpacesFixer(), |
|
108 | 2 | new Fixer\PSR2\VisibilityFixer(), |
|
109 | 2 | new Fixer\Contrib\NewlineAfterOpenTagFixer(), |
|
110 | 2 | new EmptyLineAboveDocblocksFixer(), |
|
111 | ]); |
||
112 | 2 | $config = Config::create()-> |
|
113 | 2 | fixers($this->fixer->getFixers()) |
|
114 | ; |
||
115 | 2 | $this->fixer->addConfig($config); |
|
116 | 2 | $this->fixers = $this->prepareFixers($config); |
|
117 | 2 | } |
|
118 | |||
119 | 1 | public function run() |
|
120 | { |
||
121 | 1 | $this->checkValidity(); |
|
122 | |||
123 | 1 | foreach ($this->definitions as $definition) { |
|
124 | 1 | $this->stdio->outln('-----'); |
|
125 | 1 | $this->stdio->outln('- Definition: ' . $definition); |
|
126 | 1 | $this->stdio->outln('-----'); |
|
127 | 1 | $this->generateFromDefinition($definition); |
|
128 | 1 | $this->stdio->outln('-----'); |
|
129 | } |
||
130 | 1 | } |
|
131 | |||
132 | 1 | public function checkValidity() |
|
133 | { |
||
134 | 1 | if (count($this->definitions) < 1) { |
|
135 | throw new \InvalidArgumentException('Not enough arguments'); |
||
136 | } |
||
137 | |||
138 | 1 | if ($this->path === null) { |
|
139 | throw new \InvalidArgumentException('No path set'); |
||
140 | } |
||
141 | |||
142 | 1 | if (!file_exists($this->path)) { |
|
143 | throw new \InvalidArgumentException('Path "' . $this->path . '" doesn\'t exist'); |
||
144 | } |
||
145 | |||
146 | 1 | if (!is_dir($this->path)) { |
|
147 | throw new \InvalidArgumentException('Path "' . $this->path . '" isn\'t a directory'); |
||
148 | } |
||
149 | |||
150 | 1 | foreach ($this->definitions as $definition) { |
|
151 | 1 | if (!file_exists($definition)) { |
|
152 | 1 | throw new \InvalidArgumentException('Definition "' . $definition . '" doesn\'t exist'); |
|
153 | } |
||
154 | } |
||
155 | 1 | } |
|
156 | |||
157 | 1 | public function generateFromDefinition(string $definition) |
|
158 | { |
||
159 | 1 | $yaml = $this->readYaml($definition); |
|
160 | |||
161 | 1 | $namespacePadding = explode('\\', $yaml['class']); |
|
162 | 1 | $namespace = explode('\\', $yaml['namespace']); |
|
163 | |||
164 | 1 | $yaml['class'] = array_pop($namespacePadding); |
|
165 | 1 | $yaml['namespace'] = implode('\\', array_merge($namespace, $namespacePadding)); |
|
166 | |||
167 | 1 | $namespacePathPadding = implode(DIRECTORY_SEPARATOR, $namespacePadding); |
|
168 | 1 | $baseClass = implode( |
|
169 | 1 | '\\', |
|
170 | array_merge( |
||
171 | $namespace, |
||
172 | $namespacePadding, |
||
173 | [ |
||
174 | 1 | $yaml['class'] |
|
175 | ] |
||
176 | ) |
||
177 | ); |
||
178 | |||
179 | |||
180 | 1 | if (isset($yaml['rename'])) { |
|
181 | 1 | foreach ($yaml['rename'] as $key => $resource) { |
|
182 | 1 | $yaml['properties'][$resource] = $yaml['properties'][$key]; |
|
183 | 1 | unset($yaml['properties'][$key]); |
|
184 | } |
||
185 | } |
||
186 | |||
187 | |||
188 | 1 | $this->stdio->out('Interface: generating'); |
|
189 | 1 | $this->save( |
|
190 | 1 | $this->path . |
|
191 | 1 | DIRECTORY_SEPARATOR . |
|
192 | 1 | $namespacePathPadding . |
|
193 | 1 | DIRECTORY_SEPARATOR, |
|
194 | 1 | $yaml['class'] . |
|
195 | 1 | 'Interface.php', |
|
196 | 1 | $this->createInterface($yaml) |
|
197 | ); |
||
198 | |||
199 | 1 | $this->stdio->out('Base class: generating'); |
|
200 | 1 | $this->save( |
|
201 | 1 | $this->path . |
|
202 | 1 | DIRECTORY_SEPARATOR . |
|
203 | 1 | $namespacePathPadding . |
|
204 | 1 | DIRECTORY_SEPARATOR, |
|
205 | 1 | $yaml['class'] . |
|
206 | 1 | '.php', |
|
207 | 1 | $this->createBaseClass($yaml) |
|
208 | ); |
||
209 | |||
210 | 1 | $this->stdio->out('Async class: generating'); |
|
211 | 1 | $this->save( |
|
212 | 1 | $this->path . |
|
213 | 1 | DIRECTORY_SEPARATOR . |
|
214 | 1 | 'Async' . |
|
215 | 1 | DIRECTORY_SEPARATOR . |
|
216 | 1 | $namespacePathPadding . |
|
217 | 1 | DIRECTORY_SEPARATOR, |
|
218 | 1 | $yaml['class'] . |
|
219 | 1 | '.php', |
|
220 | 1 | $this->createExtendingClass( |
|
221 | implode( |
||
222 | 1 | '\\', |
|
223 | array_merge( |
||
224 | $namespace, |
||
225 | [ |
||
226 | 1 | 'Async', |
|
227 | ], |
||
228 | $namespacePadding |
||
229 | ) |
||
230 | ), |
||
231 | 1 | $yaml['class'], |
|
232 | $baseClass |
||
233 | ) |
||
234 | ); |
||
235 | |||
236 | 1 | $this->stdio->out('Sync class: generating'); |
|
237 | 1 | $this->save( |
|
238 | 1 | $this->path . |
|
239 | 1 | DIRECTORY_SEPARATOR . |
|
240 | 1 | 'Sync' . |
|
241 | 1 | DIRECTORY_SEPARATOR . |
|
242 | 1 | $namespacePathPadding . |
|
243 | 1 | DIRECTORY_SEPARATOR, |
|
244 | 1 | $yaml['class'] . |
|
245 | 1 | '.php', |
|
246 | 1 | $this->createExtendingClass( |
|
247 | implode( |
||
248 | 1 | '\\', |
|
249 | array_merge( |
||
250 | $namespace, |
||
251 | [ |
||
252 | 1 | 'Sync', |
|
253 | ], |
||
254 | $namespacePadding |
||
255 | ) |
||
256 | ), |
||
257 | 1 | $yaml['class'], |
|
258 | $baseClass |
||
259 | ) |
||
260 | ); |
||
261 | 1 | } |
|
262 | |||
263 | 1 | protected function readYaml(string $filename): array |
|
264 | { |
||
265 | 1 | return Yaml::parse(file_get_contents($filename)); |
|
266 | } |
||
267 | |||
268 | 1 | protected function createBaseClass(array $yaml): string |
|
269 | { |
||
270 | 1 | $factory = new BuilderFactory; |
|
271 | |||
272 | 1 | $class = $factory->class($yaml['class']) |
|
273 | 1 | ->implement($yaml['class'] . 'Interface') |
|
274 | 1 | ->makeAbstract(); |
|
275 | |||
276 | 1 | $docBlock = []; |
|
277 | |||
278 | 1 | View Code Duplication | if (isset($yaml['collection'])) { |
279 | 1 | $nestedResources = []; |
|
280 | 1 | foreach ($yaml['collection'] as $key => $resource) { |
|
281 | 1 | $nestedResources[] = $key . '="' . $resource . '"'; |
|
282 | } |
||
283 | 1 | $docBlock[] = '@Collection(' . implode(', ', $nestedResources) . ')'; |
|
284 | } |
||
285 | |||
286 | 1 | View Code Duplication | if (isset($yaml['nested'])) { |
287 | 1 | $nestedResources = []; |
|
288 | 1 | foreach ($yaml['nested'] as $key => $resource) { |
|
289 | 1 | $nestedResources[] = $key . '="' . $resource . '"'; |
|
290 | } |
||
291 | 1 | $docBlock[] = '@Nested(' . implode(', ', $nestedResources) . ')'; |
|
292 | } |
||
293 | |||
294 | 1 | View Code Duplication | if (isset($yaml['rename'])) { |
295 | 1 | $nestedResources = []; |
|
296 | 1 | foreach ($yaml['rename'] as $key => $resource) { |
|
297 | 1 | $nestedResources[] = $resource . '="' . $key . '"'; |
|
298 | } |
||
299 | 1 | $docBlock[] = '@Rename(' . implode(', ', $nestedResources) . ')'; |
|
300 | } |
||
301 | |||
302 | 1 | if (count($docBlock) > 0) { |
|
303 | 1 | $class->setDocComment("/**\r\n * " . implode("\r\n * ", $docBlock) . "\r\n */"); |
|
304 | } |
||
305 | |||
306 | 1 | $class->addStmt( |
|
307 | 1 | new Node\Stmt\TraitUse([ |
|
308 | 1 | new Node\Name('TransportAwareTrait') |
|
309 | ]) |
||
310 | ); |
||
311 | |||
312 | 1 | foreach ($yaml['properties'] as $name => $details) { |
|
313 | 1 | $type = $details; |
|
314 | 1 | if (is_array($details)) { |
|
315 | 1 | $type = $details['type']; |
|
316 | } |
||
317 | 1 | $class->addStmt($this->createProperty($factory, $type, $name, $details)); |
|
318 | 1 | $class->addStmt($this->createMethod($factory, $type, $name, $details)); |
|
319 | } |
||
320 | |||
321 | 1 | $stmt = $factory->namespace($yaml['namespace']); |
|
322 | 1 | if (isset($yaml['collection'])) { |
|
323 | 1 | $stmt = $stmt->addStmt( |
|
324 | 1 | $factory->use(Collection::class) |
|
325 | ); |
||
326 | } |
||
327 | 1 | if (isset($yaml['nested'])) { |
|
328 | 1 | $stmt = $stmt->addStmt( |
|
329 | 1 | $factory->use(Nested::class) |
|
330 | ); |
||
331 | } |
||
332 | 1 | if (isset($yaml['rename'])) { |
|
333 | 1 | $stmt = $stmt->addStmt( |
|
334 | 1 | $factory->use(Rename::class) |
|
335 | ); |
||
336 | } |
||
337 | $stmt |
||
338 | 1 | ->addStmt($factory->use('WyriHaximus\ApiClient\Resource\TransportAwareTrait')) |
|
339 | 1 | ->addStmt($class) |
|
340 | ; |
||
341 | |||
342 | 1 | $node = $stmt->getNode(); |
|
343 | |||
344 | 1 | $prettyPrinter = new PrettyPrinter\Standard(); |
|
345 | 1 | return $prettyPrinter->prettyPrintFile([ |
|
346 | 1 | $node |
|
347 | 1 | ]) . PHP_EOL; |
|
348 | } |
||
349 | |||
350 | 1 | protected function createInterface(array $yaml): string |
|
351 | { |
||
352 | 1 | $factory = new BuilderFactory; |
|
353 | |||
354 | 1 | $class = $factory->interface($yaml['class'] . 'Interface') |
|
355 | 1 | ->extend('ResourceInterface'); |
|
356 | |||
357 | 1 | foreach ($yaml['properties'] as $name => $details) { |
|
358 | 1 | $type = $details; |
|
359 | 1 | if (is_array($details)) { |
|
360 | 1 | $type = $details['type']; |
|
361 | } |
||
362 | 1 | $class->addStmt($this->createMethod($factory, $type, $name, $details)); |
|
363 | } |
||
364 | |||
365 | 1 | $node = $factory->namespace($yaml['namespace']) |
|
366 | 1 | ->addStmt($factory->use(ResourceInterface::class)) |
|
367 | 1 | ->addStmt($class) |
|
368 | 1 | ->getNode() |
|
369 | ; |
||
370 | |||
371 | 1 | $prettyPrinter = new PrettyPrinter\Standard(); |
|
372 | 1 | return $prettyPrinter->prettyPrintFile([ |
|
373 | 1 | $node |
|
374 | 1 | ]) . PHP_EOL; |
|
375 | } |
||
376 | |||
377 | 1 | protected function createProperty(BuilderFactory $factory, string $type, string $name, $details): Property |
|
378 | { |
||
379 | 1 | $property = $factory->property($name) |
|
380 | 1 | ->makeProtected() |
|
381 | 1 | ->setDocComment('/** |
|
382 | 1 | * @var ' . $type . ' |
|
383 | 1 | */'); |
|
384 | 1 | if (isset($details['default'])) { |
|
385 | 1 | $property->setDefault($details['default']); |
|
386 | } |
||
387 | |||
388 | 1 | return $property; |
|
389 | } |
||
390 | |||
391 | 1 | protected function createMethod(BuilderFactory $factory, string $type, string $name, $details): Method |
|
392 | { |
||
393 | 1 | return $factory->method(Inflector::camelize($name)) |
|
394 | 1 | ->makePublic() |
|
395 | 1 | ->setReturnType($type) |
|
396 | 1 | ->setDocComment('/** |
|
397 | 1 | * @return ' . $type . ' |
|
398 | 1 | */') |
|
399 | 1 | ->addStmt( |
|
400 | 1 | new Node\Stmt\Return_( |
|
401 | 1 | new Node\Expr\PropertyFetch( |
|
402 | 1 | new Node\Expr\Variable('this'), |
|
403 | $name |
||
404 | ) |
||
405 | ) |
||
406 | ); |
||
407 | } |
||
408 | |||
409 | 1 | protected function createExtendingClass(string $namespace, string $className, string $baseClass): string |
|
410 | { |
||
411 | 1 | $factory = new BuilderFactory; |
|
412 | |||
413 | 1 | $class = $factory->class($className) |
|
414 | 1 | ->extend('Base' . $className); |
|
415 | |||
416 | 1 | $class->addStmt($factory->method('refresh') |
|
417 | 1 | ->makePublic() |
|
418 | 1 | ->setReturnType($className) |
|
419 | 1 | ->addStmt( |
|
420 | 1 | new Node\Stmt\Return_( |
|
421 | 1 | new Node\Expr\MethodCall( |
|
422 | 1 | new Node\Expr\Variable('this'), |
|
423 | 1 | 'wait', |
|
424 | [ |
||
0 ignored issues
–
show
|
|||
425 | 1 | new Node\Expr\MethodCall( |
|
426 | 1 | new Node\Expr\Variable('this'), |
|
427 | 1 | 'callAsync', |
|
428 | [ |
||
0 ignored issues
–
show
array(new \PhpParser\Nod...lar\String_('refresh')) is of type array<integer,object<Php...de\\Scalar\\String_>"}> , but the function expects a array<integer,object<PhpParser\Node\Arg>> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
Loading history...
|
|||
429 | 1 | new Node\Scalar\String_('refresh'), |
|
430 | ] |
||
431 | ), |
||
432 | ] |
||
433 | ) |
||
434 | ) |
||
435 | )); |
||
436 | |||
437 | 1 | $node = $factory->namespace($namespace) |
|
438 | 1 | ->addStmt($factory->use($baseClass)->as('Base' . $className)) |
|
439 | 1 | ->addStmt($class) |
|
440 | |||
441 | 1 | ->getNode() |
|
442 | ; |
||
443 | |||
444 | 1 | $prettyPrinter = new PrettyPrinter\Standard(); |
|
445 | 1 | return $prettyPrinter->prettyPrintFile([ |
|
446 | 1 | $node |
|
447 | 1 | ]) . PHP_EOL; |
|
448 | } |
||
449 | |||
450 | 1 | protected function save(string $directory, string $fileName, string $fileContents) |
|
451 | { |
||
452 | 1 | $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $fileName); |
|
453 | 1 | if (file_exists($directory . $fileName)) { |
|
454 | $this->stdio->outln(', exists!'); |
||
455 | return; |
||
456 | } |
||
457 | |||
458 | 1 | $path = $directory . $fileName; |
|
459 | 1 | $pathChunks = explode(DIRECTORY_SEPARATOR, $path); |
|
460 | 1 | array_pop($pathChunks); |
|
461 | 1 | $path = implode(DIRECTORY_SEPARATOR, $pathChunks); |
|
462 | 1 | if (!file_exists($path)) { |
|
463 | 1 | mkdir($path, 0777, true); |
|
464 | } |
||
465 | |||
466 | 1 | if (!file_exists($path)) { |
|
467 | throw new Exception('Unable to create: ' . $path); |
||
468 | } |
||
469 | |||
470 | 1 | $this->stdio->out(', writing'); |
|
471 | 1 | file_put_contents($directory . $fileName, $fileContents); |
|
472 | |||
473 | do { |
||
474 | 1 | usleep(500); |
|
475 | 1 | } while (!file_exists($directory . $fileName)); |
|
476 | |||
477 | 1 | $this->stdio->out(', applying PSR-2'); |
|
478 | 1 | $this->applyPsr2($directory . $fileName); |
|
479 | 1 | $this->stdio->outln(', done!'); |
|
480 | 1 | } |
|
481 | |||
482 | /** |
||
483 | * @param string $fileName |
||
484 | */ |
||
485 | 1 | protected function applyPsr2($fileName) |
|
486 | { |
||
487 | 1 | $file = new \SplFileInfo($fileName); |
|
488 | 1 | $this->fixer->fixFile( |
|
489 | $file, |
||
490 | 1 | $this->fixers, |
|
491 | 1 | false, |
|
492 | 1 | false, |
|
493 | 1 | new FileCacheManager( |
|
494 | 1 | false, |
|
495 | 1 | '', |
|
496 | 1 | $this->fixers |
|
497 | ) |
||
498 | ); |
||
499 | |||
500 | 1 | file_put_contents( |
|
501 | $fileName, |
||
502 | str_replace( |
||
503 | 1 | '<?php', |
|
504 | 1 | '<?php declare(strict_types=1);', |
|
505 | file_get_contents( |
||
506 | $fileName |
||
507 | ) |
||
508 | ) |
||
509 | ); |
||
510 | 1 | } |
|
511 | |||
512 | |||
513 | /** |
||
514 | * @param ConfigInterface $config |
||
515 | * |
||
516 | * @return FixerInterface[] |
||
517 | */ |
||
518 | 2 | private function prepareFixers(ConfigInterface $config): array |
|
519 | { |
||
520 | 2 | $fixers = $config->getFixers(); |
|
521 | |||
522 | 2 | foreach ($fixers as $fixer) { |
|
523 | 2 | if ($fixer instanceof ConfigAwareInterface) { |
|
524 | 2 | $fixer->setConfig($config); |
|
525 | } |
||
526 | } |
||
527 | |||
528 | 2 | return $fixers; |
|
529 | } |
||
530 | } |
||
531 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: