GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 6cd26f...e0a211 )
by Cees-Jan
8s
created

ResourceGenerator::createBaseClass()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 50
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 50
ccs 31
cts 31
cp 1
rs 8.6315
c 0
b 0
f 0
cc 6
eloc 32
nc 12
nop 1
crap 6
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\Nested;
23
use WyriHaximus\ApiClient\Resource\ResourceInterface;
24
25
class ResourceGenerator
26
{
27
    /**
28
     * @var Context
29
     */
30
    protected $context;
31
32
    /**
33
     * @var Stdio
34
     */
35
    protected $stdio;
36
37
    /**
38
     * @var array
39
     */
40
    protected $definitions = [];
41
42
    /**
43
     * @var string
44
     */
45
    protected $path;
46
47
    /**
48
     * @var Fixer
49
     */
50
    protected $fixer;
51
52
    /**
53
     * @var array
54
     */
55
    protected $fixers;
56
57 2
    public function __construct(Context $context, Stdio $stdio)
58
    {
59 2
        $this->context = $context;
60 2
        $this->stdio = $stdio;
61
62 2
        $this->setUpArguments();
63 2
        $this->setUpFixers();
64 2
    }
65
66 2
    protected function setUpArguments()
67
    {
68 2
        $getOpt = $this->context->getopt([]);
69 2
        $i = 0;
70
        do {
71 2
            $i++;
72 2
            $opt = $getOpt->get($i);
73 2
            if ($opt === null) {
74 2
                break;
75
            }
76 2
            $this->definitions[] = $opt;
77 2
        } while (true);
78 2
        $this->path = array_pop($this->definitions);
79 2
    }
80
81 2
    protected function setUpFixers()
82
    {
83 2
        $this->fixer = new Fixer();
84 2
        $this->fixer->registerCustomFixers([
85 2
            new Fixer\Symfony\ExtraEmptyLinesFixer(),
86 2
            new Fixer\Symfony\SingleBlankLineBeforeNamespaceFixer(),
87 2
            new Fixer\PSR0\Psr0Fixer(),
88 2
            new Fixer\PSR1\EncodingFixer(),
89 2
            new Fixer\PSR1\ShortTagFixer(),
90 2
            new Fixer\PSR2\BracesFixer(),
91 2
            new Fixer\PSR2\ElseifFixer(),
92 2
            new Fixer\PSR2\EofEndingFixer(),
93 2
            new Fixer\PSR2\FunctionCallSpaceFixer(),
94 2
            new Fixer\PSR2\FunctionDeclarationFixer(),
95 2
            new Fixer\PSR2\IndentationFixer(),
96 2
            new Fixer\PSR2\LineAfterNamespaceFixer(),
97 2
            new Fixer\PSR2\LinefeedFixer(),
98 2
            new Fixer\PSR2\LowercaseConstantsFixer(),
99 2
            new Fixer\PSR2\LowercaseKeywordsFixer(),
100 2
            new Fixer\PSR2\MethodArgumentSpaceFixer(),
101 2
            new Fixer\PSR2\MultipleUseFixer(),
102 2
            new Fixer\PSR2\ParenthesisFixer(),
103 2
            new Fixer\PSR2\PhpClosingTagFixer(),
104 2
            new Fixer\PSR2\SingleLineAfterImportsFixer(),
105 2
            new Fixer\PSR2\TrailingSpacesFixer(),
106 2
            new Fixer\PSR2\VisibilityFixer(),
107 2
            new Fixer\Contrib\NewlineAfterOpenTagFixer(),
108 2
            new EmptyLineAboveDocblocksFixer(),
109
        ]);
110 2
        $config = Config::create()->
111 2
        fixers($this->fixer->getFixers())
112
        ;
113 2
        $this->fixer->addConfig($config);
114 2
        $this->fixers = $this->prepareFixers($config);
115 2
    }
116
117 1
    public function run()
118
    {
119 1
        $this->checkValidity();
120
121 1
        foreach ($this->definitions as $definition) {
122 1
            $this->stdio->outln('-----');
123 1
            $this->stdio->outln('- Definition: ' . $definition);
124 1
            $this->stdio->outln('-----');
125 1
            $this->generateFromDefinition($definition);
126 1
            $this->stdio->outln('-----');
127
        }
128 1
    }
129
130 1
    public function checkValidity()
131
    {
132 1
        if (count($this->definitions) < 1) {
133
            throw new \InvalidArgumentException('Not enough arguments');
134
        }
135
136 1
        if ($this->path === null) {
137
            throw new \InvalidArgumentException('No path set');
138
        }
139
140 1
        if (!file_exists($this->path)) {
141
            throw new \InvalidArgumentException('Path "' . $this->path . '" doesn\'t exist');
142
        }
143
144 1
        if (!is_dir($this->path)) {
145
            throw new \InvalidArgumentException('Path "' . $this->path . '" isn\'t a directory');
146
        }
147
148 1
        foreach ($this->definitions as $definition) {
149 1
            if (!file_exists($definition)) {
150 1
                throw new \InvalidArgumentException('Definition "' . $definition . '" doesn\'t exist');
151
            }
152
        }
153 1
    }
154
155 1
    public function generateFromDefinition(string $definition)
156
    {
157 1
        $yaml = $this->readYaml($definition);
158
159 1
        $namespacePadding = explode('\\', $yaml['class']);
160 1
        $namespace = explode('\\', $yaml['namespace']);
161
162 1
        $yaml['class'] = array_pop($namespacePadding);
163 1
        $yaml['namespace'] = implode('\\', array_merge($namespace, $namespacePadding));
164
165 1
        $namespacePathPadding = implode(DIRECTORY_SEPARATOR, $namespacePadding);
166 1
        $baseClass = implode(
167 1
            '\\',
168
            array_merge(
169
                $namespace,
170
                $namespacePadding,
171
                [
172 1
                    $yaml['class']
173
                ]
174
            )
175
        );
176
177 1
        $this->stdio->out('Interface: generating');
178 1
        $this->save(
179 1
            $this->path .
180 1
                DIRECTORY_SEPARATOR .
181 1
                $namespacePathPadding .
182 1
                DIRECTORY_SEPARATOR,
183 1
            $yaml['class'] .
184 1
                'Interface.php',
185 1
            $this->createInterface($yaml)
186
        );
187
188 1
        $this->stdio->out('Base class: 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
                '.php',
196 1
            $this->createBaseClass($yaml)
197
        );
198
199 1
        $this->stdio->out('Async class: generating');
200 1
        $this->save(
201 1
            $this->path .
202 1
                DIRECTORY_SEPARATOR .
203 1
                'Async' .
204 1
                DIRECTORY_SEPARATOR .
205 1
                $namespacePathPadding .
206 1
                DIRECTORY_SEPARATOR,
207 1
            $yaml['class'] .
208 1
                '.php',
209 1
            $this->createExtendingClass(
210
                implode(
211 1
                    '\\',
212
                    array_merge(
213
                        $namespace,
214
                        [
215 1
                            'Async',
216
                        ],
217
                        $namespacePadding
218
                    )
219
                ),
220 1
                $yaml['class'],
221
                $baseClass
222
            )
223
        );
224
225 1
        $this->stdio->out('Sync class: generating');
226 1
        $this->save(
227 1
            $this->path .
228 1
                DIRECTORY_SEPARATOR .
229 1
                'Sync' .
230 1
                DIRECTORY_SEPARATOR .
231 1
                $namespacePathPadding .
232 1
                DIRECTORY_SEPARATOR,
233 1
            $yaml['class'] .
234 1
                '.php',
235 1
            $this->createExtendingClass(
236
                implode(
237 1
                    '\\',
238
                    array_merge(
239
                        $namespace,
240
                        [
241 1
                            'Sync',
242
                        ],
243
                        $namespacePadding
244
                    )
245
                ),
246 1
                $yaml['class'],
247
                $baseClass
248
            )
249
        );
250 1
    }
251
252 1
    protected function readYaml(string $filename): array
253
    {
254 1
        return Yaml::parse(file_get_contents($filename));
255
    }
256
257 1
    protected function createBaseClass(array $yaml): string
258
    {
259 1
        $factory = new BuilderFactory;
260
261 1
        $class = $factory->class($yaml['class'])
262 1
            ->implement($yaml['class'] . 'Interface')
263 1
            ->makeAbstract();
264
265 1
        if (isset($yaml['nested'])) {
266 1
            $nestedResources = [];
267 1
            foreach ($yaml['nested'] as $key => $resource) {
268 1
                $nestedResources[] = $key . '="' . $resource . '"';
269
            }
270 1
            $docBlock = "/**\r\n * @Nested(" . implode(', ', $nestedResources) . ")\r\n */";
271 1
            $class->setDocComment($docBlock);
272
        }
273
274 1
        $class->addStmt(
275 1
            new Node\Stmt\TraitUse([
276 1
                new Node\Name('TransportAwareTrait')
277
            ])
278
        );
279
280 1
        foreach ($yaml['properties'] as $name => $details) {
281 1
            $type = $details;
282 1
            if (is_array($details)) {
283 1
                $type = $details['type'];
284
            }
285 1
            $class->addStmt($this->createProperty($factory, $type, $name, $details));
286 1
            $class->addStmt($this->createMethod($factory, $type, $name, $details));
287
        }
288
289 1
        $stmt = $factory->namespace($yaml['namespace']);
290 1
        if (isset($yaml['nested'])) {
291 1
            $stmt = $stmt->addStmt(
292 1
                $factory->use(Nested::class)
293
            );
294
        }
295
        $stmt
296 1
            ->addStmt($factory->use('WyriHaximus\ApiClient\Resource\TransportAwareTrait'))
297 1
            ->addStmt($class)
298
        ;
299
300 1
        $node = $stmt->getNode();
301
302 1
        $prettyPrinter = new PrettyPrinter\Standard();
303 1
        return $prettyPrinter->prettyPrintFile([
304 1
            $node
305 1
        ]) . PHP_EOL;
306
    }
307
308 1
    protected function createInterface(array $yaml): string
309
    {
310 1
        $factory = new BuilderFactory;
311
312 1
        $class = $factory->interface($yaml['class'] . 'Interface')
313 1
            ->extend('ResourceInterface');
314
315 1
        foreach ($yaml['properties'] as $name => $details) {
316 1
            $type = $details;
317 1
            if (is_array($details)) {
318 1
                $type = $details['type'];
319
            }
320 1
            $class->addStmt($this->createMethod($factory, $type, $name, $details));
321
        }
322
323 1
        $node = $factory->namespace($yaml['namespace'])
324 1
            ->addStmt($factory->use(ResourceInterface::class))
325 1
            ->addStmt($class)
326 1
            ->getNode()
327
        ;
328
329 1
        $prettyPrinter = new PrettyPrinter\Standard();
330 1
        return $prettyPrinter->prettyPrintFile([
331 1
            $node
332 1
        ]) . PHP_EOL;
333
    }
334
335 1
    protected function createProperty(BuilderFactory $factory, string $type, string $name, $details): Property
336
    {
337 1
        $property = $factory->property($name)
338 1
            ->makeProtected()
339 1
            ->setDocComment('/**
340 1
                              * @var ' . $type . '
341 1
                              */');
342 1
        if (isset($details['default'])) {
343 1
            $property->setDefault($details['default']);
344
        }
345
346 1
        return $property;
347
    }
348
349 1
    protected function createMethod(BuilderFactory $factory, string $type, string $name, $details): Method
0 ignored issues
show
Unused Code introduced by
The parameter $details is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
350
    {
351 1
        return $factory->method(Inflector::camelize($name))
352 1
            ->makePublic()
353 1
            ->setReturnType($type)
354 1
            ->setDocComment('/**
355 1
                              * @return ' . $type . '
356 1
                              */')
357 1
            ->addStmt(
358 1
                new Node\Stmt\Return_(
359 1
                    new Node\Expr\PropertyFetch(
360 1
                        new Node\Expr\Variable('this'),
361
                        $name
362
                    )
363
                )
364
            );
365
    }
366
367 1
    protected function createExtendingClass(string $namespace, string $className, string $baseClass): string
368
    {
369 1
        $factory = new BuilderFactory;
370
371 1
        $class = $factory->class($className)
372 1
            ->extend('Base' . $className);
373
374 1
        $class->addStmt($factory->method('refresh')
375 1
            ->makePublic()
376 1
            ->setReturnType($className)
377 1
            ->addStmt(
378 1
                new Node\Stmt\Return_(
379 1
                    new Node\Expr\MethodCall(
380 1
                        new Node\Expr\Variable('this'),
381 1
                        'wait',
382
                        [
0 ignored issues
show
Documentation introduced by
array(new \PhpParser\Nod...r\String_('refresh')))) is of type array<integer,object<Php...e\\Expr\\MethodCall>"}>, 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...
383 1
                            new Node\Expr\MethodCall(
384 1
                                new Node\Expr\Variable('this'),
385 1
                                'callAsync',
386
                                [
0 ignored issues
show
Documentation introduced by
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...
387 1
                                    new Node\Scalar\String_('refresh'),
388
                                ]
389
                            ),
390
                        ]
391
                    )
392
                )
393
            ));
394
395 1
        $node = $factory->namespace($namespace)
396 1
            ->addStmt($factory->use($baseClass)->as('Base' . $className))
397 1
            ->addStmt($class)
398
399 1
            ->getNode()
400
        ;
401
402 1
        $prettyPrinter = new PrettyPrinter\Standard();
403 1
        return $prettyPrinter->prettyPrintFile([
404 1
            $node
405 1
        ]) . PHP_EOL;
406
    }
407
408 1
    protected function save(string $directory, string $fileName, string $fileContents)
409
    {
410 1
        $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $fileName);
411 1
        if (file_exists($directory . $fileName)) {
412
            $this->stdio->outln(', exists!');
413
            return;
414
        }
415
416 1
        $path = $directory . $fileName;
417 1
        $pathChunks = explode(DIRECTORY_SEPARATOR, $path);
418 1
        array_pop($pathChunks);
419 1
        $path = implode(DIRECTORY_SEPARATOR, $pathChunks);
420 1
        if (!file_exists($path)) {
421 1
            mkdir($path, 0777, true);
422
        }
423
424 1
        if (!file_exists($path)) {
425
            throw new Exception('Unable to create: ' . $path);
426
        }
427
428 1
        $this->stdio->out(', writing');
429 1
        file_put_contents($directory . $fileName, $fileContents);
430
431
        do {
432 1
            usleep(500);
433 1
        } while (!file_exists($directory . $fileName));
434
435 1
        $this->stdio->out(', applying PSR-2');
436 1
        $this->applyPsr2($directory . $fileName);
437 1
        $this->stdio->outln(', done!');
438 1
    }
439
440
    /**
441
     * @param string $fileName
442
     */
443 1
    protected function applyPsr2($fileName)
444
    {
445 1
        $file = new \SplFileInfo($fileName);
446 1
        $this->fixer->fixFile(
447
            $file,
448 1
            $this->fixers,
449 1
            false,
450 1
            false,
451 1
            new FileCacheManager(
452 1
                false,
453 1
                '',
454 1
                $this->fixers
455
            )
456
        );
457
458 1
        file_put_contents(
459
            $fileName,
460
            str_replace(
461 1
                '<?php',
462 1
                '<?php declare(strict_types=1);',
463
                file_get_contents(
464
                    $fileName
465
                )
466
            )
467
        );
468 1
    }
469
470
471
    /**
472
     * @param ConfigInterface $config
473
     *
474
     * @return FixerInterface[]
475
     */
476 2
    private function prepareFixers(ConfigInterface $config): array
477
    {
478 2
        $fixers = $config->getFixers();
479
480 2
        foreach ($fixers as $fixer) {
481 2
            if ($fixer instanceof ConfigAwareInterface) {
482 2
                $fixer->setConfig($config);
483
            }
484
        }
485
486 2
        return $fixers;
487
    }
488
}
489