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 ( 5dd154...7e4502 )
by Cees-Jan
04:15 queued 01:05
created

ResourceGenerator   D

Complexity

Total Complexity 35

Size/Duplication

Total Lines 437
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 49

Test Coverage

Coverage 97.21%

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 49
dl 0
loc 437
ccs 244
cts 251
cp 0.9721
rs 4.5
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A setUpArguments() 0 14 3
B setUpFixers() 0 35 1
A prepareFixers() 0 12 3
A run() 0 12 2
C checkValidity() 0 24 7
B generateFromDefinition() 0 96 1
A readYaml() 0 4 1
B createBaseClass() 0 34 3
B createInterface() 0 26 3
A createProperty() 0 13 2
A createMethod() 0 17 1
B createExtendingClass() 0 40 1
B save() 0 31 5
A applyPsr2() 0 15 1
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\Resource\ResourceInterface;
23
24
class ResourceGenerator
25
{
26
    /**
27
     * @var Context
28
     */
29
    protected $context;
30
31
    /**
32
     * @var Stdio
33
     */
34
    protected $stdio;
35
36
    /**
37
     * @var array
38
     */
39
    protected $definitions = [];
40
41
    /**
42
     * @var string
43
     */
44
    protected $path;
45
46
    /**
47
     * @var Fixer
48
     */
49
    protected $fixer;
50
51
    /**
52
     * @var array
53
     */
54
    protected $fixers;
55
56 2
    public function __construct(Context $context, Stdio $stdio)
57
    {
58 2
        $this->context = $context;
59 2
        $this->stdio = $stdio;
60
61 2
        $this->setUpArguments();
62 2
        $this->setUpFixers();
63 2
    }
64
65 2
    protected function setUpArguments()
66
    {
67 2
        $getOpt = $this->context->getopt([]);
68 2
        $i = 0;
69
        do {
70 2
            $i++;
71 2
            $opt = $getOpt->get($i);
72 2
            if ($opt === null) {
73 2
                break;
74
            }
75 2
            $this->definitions[] = $opt;
76 2
        } while (true);
77 2
        $this->path = array_pop($this->definitions);
78 2
    }
79
80 2
    protected function setUpFixers()
81
    {
82 2
        $this->fixer = new Fixer();
83 2
        $this->fixer->registerCustomFixers([
84 2
            new Fixer\Symfony\ExtraEmptyLinesFixer(),
85 2
            new Fixer\Symfony\SingleBlankLineBeforeNamespaceFixer(),
86 2
            new Fixer\PSR0\Psr0Fixer(),
87 2
            new Fixer\PSR1\EncodingFixer(),
88 2
            new Fixer\PSR1\ShortTagFixer(),
89 2
            new Fixer\PSR2\BracesFixer(),
90 2
            new Fixer\PSR2\ElseifFixer(),
91 2
            new Fixer\PSR2\EofEndingFixer(),
92 2
            new Fixer\PSR2\FunctionCallSpaceFixer(),
93 2
            new Fixer\PSR2\FunctionDeclarationFixer(),
94 2
            new Fixer\PSR2\IndentationFixer(),
95 2
            new Fixer\PSR2\LineAfterNamespaceFixer(),
96 2
            new Fixer\PSR2\LinefeedFixer(),
97 2
            new Fixer\PSR2\LowercaseConstantsFixer(),
98 2
            new Fixer\PSR2\LowercaseKeywordsFixer(),
99 2
            new Fixer\PSR2\MethodArgumentSpaceFixer(),
100 2
            new Fixer\PSR2\MultipleUseFixer(),
101 2
            new Fixer\PSR2\ParenthesisFixer(),
102 2
            new Fixer\PSR2\PhpClosingTagFixer(),
103 2
            new Fixer\PSR2\SingleLineAfterImportsFixer(),
104 2
            new Fixer\PSR2\TrailingSpacesFixer(),
105 2
            new Fixer\PSR2\VisibilityFixer(),
106 2
            new Fixer\Contrib\NewlineAfterOpenTagFixer(),
107 2
            new EmptyLineAboveDocblocksFixer(),
108
        ]);
109 2
        $config = Config::create()->
110 2
        fixers($this->fixer->getFixers())
111
        ;
112 2
        $this->fixer->addConfig($config);
113 2
        $this->fixers = $this->prepareFixers($config);
114 2
    }
115
116 1
    public function run()
117
    {
118 1
        $this->checkValidity();
119
120 1
        foreach ($this->definitions as $definition) {
121 1
            $this->stdio->outln('-----');
122 1
            $this->stdio->outln('- Definition: ' . $definition);
123 1
            $this->stdio->outln('-----');
124 1
            $this->generateFromDefinition($definition);
125 1
            $this->stdio->outln('-----');
126
        }
127 1
    }
128
129 1
    public function checkValidity()
130
    {
131 1
        if (count($this->definitions) < 1) {
132
            throw new \InvalidArgumentException('Not enough arguments');
133
        }
134
135 1
        if ($this->path === null) {
136
            throw new \InvalidArgumentException('No path set');
137
        }
138
139 1
        if (!file_exists($this->path)) {
140
            throw new \InvalidArgumentException('Path "' . $this->path . '" doesn\'t exist');
141
        }
142
143 1
        if (!is_dir($this->path)) {
144
            throw new \InvalidArgumentException('Path "' . $this->path . '" isn\'t a directory');
145
        }
146
147 1
        foreach ($this->definitions as $definition) {
148 1
            if (!file_exists($definition)) {
149 1
                throw new \InvalidArgumentException('Definition "' . $definition . '" doesn\'t exist');
150
            }
151
        }
152 1
    }
153
154 1
    public function generateFromDefinition($definition)
155
    {
156 1
        $yaml = $this->readYaml($definition);
157
158 1
        $namespacePadding = explode('\\', $yaml['class']);
159 1
        $namespace = explode('\\', $yaml['namespace']);
160
161 1
        $yaml['class'] = array_pop($namespacePadding);
162 1
        $yaml['namespace'] = implode('\\', array_merge($namespace, $namespacePadding));
163
164 1
        $namespacePathPadding = implode(DIRECTORY_SEPARATOR, $namespacePadding);
165 1
        $baseClass = implode(
166 1
            '\\',
167
            array_merge(
168
                $namespace,
169
                $namespacePadding,
170
                [
171 1
                    $yaml['class']
172
                ]
173
            )
174
        );
175
176 1
        $this->stdio->out('Interface: generating');
177 1
        $this->save(
178 1
            $this->path .
179 1
                DIRECTORY_SEPARATOR .
180 1
                $namespacePathPadding .
181 1
                DIRECTORY_SEPARATOR,
182 1
            $yaml['class'] .
183 1
                'Interface.php',
184 1
            $this->createInterface($yaml)
185
        );
186
187 1
        $this->stdio->out('Base class: generating');
188 1
        $this->save(
189 1
            $this->path .
190 1
                DIRECTORY_SEPARATOR .
191 1
                $namespacePathPadding .
192 1
                DIRECTORY_SEPARATOR,
193 1
            $yaml['class'] .
194 1
                '.php',
195 1
            $this->createBaseClass($yaml)
196
        );
197
198 1
        $this->stdio->out('Async class: generating');
199 1
        $this->save(
200 1
            $this->path .
201 1
                DIRECTORY_SEPARATOR .
202 1
                'Async' .
203 1
                DIRECTORY_SEPARATOR .
204 1
                $namespacePathPadding .
205 1
                DIRECTORY_SEPARATOR,
206 1
            $yaml['class'] .
207 1
                '.php',
208 1
            $this->createExtendingClass(
209
                implode(
210 1
                    '\\',
211
                    array_merge(
212
                        $namespace,
213
                        [
214 1
                            'Async',
215
                        ],
216
                        $namespacePadding
217
                    )
218
                ),
219 1
                $yaml['class'],
220
                $baseClass
221
            )
222
        );
223
224 1
        $this->stdio->out('Sync class: generating');
225 1
        $this->save(
226 1
            $this->path .
227 1
                DIRECTORY_SEPARATOR .
228 1
                'Sync' .
229 1
                DIRECTORY_SEPARATOR .
230 1
                $namespacePathPadding .
231 1
                DIRECTORY_SEPARATOR,
232 1
            $yaml['class'] .
233 1
                '.php',
234 1
            $this->createExtendingClass(
235
                implode(
236 1
                    '\\',
237
                    array_merge(
238
                        $namespace,
239
                        [
240 1
                            'Sync',
241
                        ],
242
                        $namespacePadding
243
                    )
244
                ),
245 1
                $yaml['class'],
246
                $baseClass
247
            )
248
        );
249 1
    }
250
251 1
    protected function readYaml(string $filename): array
252
    {
253 1
        return Yaml::parse(file_get_contents($filename));
254
    }
255
256 1
    protected function createBaseClass(array $yaml)
257
    {
258 1
        $factory = new BuilderFactory;
259
260 1
        $class = $factory->class($yaml['class'])
261 1
            ->implement($yaml['class'] . 'Interface')
262 1
            ->makeAbstract();
263 1
        $class->addStmt(
264 1
            new Node\Stmt\TraitUse([
265 1
                new Node\Name('TransportAwareTrait')
266
            ])
267
        );
268
269 1
        foreach ($yaml['properties'] as $name => $details) {
270 1
            $type = $details;
271 1
            if (is_array($details)) {
272 1
                $type = $details['type'];
273
            }
274 1
            $class->addStmt($this->createProperty($factory, $type, $name, $details));
275 1
            $class->addStmt($this->createMethod($factory, $type, $name, $details));
276
        }
277
278 1
        $node = $factory->namespace($yaml['namespace'])
279 1
            ->addStmt($factory->use('WyriHaximus\ApiClient\Resource\TransportAwareTrait'))
280 1
            ->addStmt($class)
281
282 1
            ->getNode()
283
        ;
284
285 1
        $prettyPrinter = new PrettyPrinter\Standard();
286 1
        return $prettyPrinter->prettyPrintFile([
287 1
            $node
288 1
        ]) . PHP_EOL;
289
    }
290
291 1
    protected function createInterface(array $yaml)
292
    {
293 1
        $factory = new BuilderFactory;
294
295 1
        $class = $factory->interface($yaml['class'] . 'Interface')
296 1
            ->extend('ResourceInterface');
297
298 1
        foreach ($yaml['properties'] as $name => $details) {
299 1
            $type = $details;
300 1
            if (is_array($details)) {
301 1
                $type = $details['type'];
302
            }
303 1
            $class->addStmt($this->createMethod($factory, $type, $name, $details));
304
        }
305
306 1
        $node = $factory->namespace($yaml['namespace'])
307 1
            ->addStmt($factory->use(ResourceInterface::class))
308 1
            ->addStmt($class)
309 1
            ->getNode()
310
        ;
311
312 1
        $prettyPrinter = new PrettyPrinter\Standard();
313 1
        return $prettyPrinter->prettyPrintFile([
314 1
            $node
315 1
        ]) . PHP_EOL;
316
    }
317
318 1
    protected function createProperty(BuilderFactory $factory, string $type, string $name, $details): Property
319
    {
320 1
        $property = $factory->property($name)
321 1
            ->makeProtected()
322 1
            ->setDocComment('/**
323 1
                              * @var ' . $type . '
324 1
                              */');
325 1
        if (isset($details['default'])) {
326 1
            $property->setDefault($details['default']);
327
        }
328
329 1
        return $property;
330
    }
331
332 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...
333
    {
334 1
        return $factory->method(Inflector::camelize($name))
335 1
            ->makePublic()
336 1
            ->setReturnType($type)
337 1
            ->setDocComment('/**
338 1
                              * @return ' . $type . '
339 1
                              */')
340 1
            ->addStmt(
341 1
                new Node\Stmt\Return_(
342 1
                    new Node\Expr\PropertyFetch(
343 1
                        new Node\Expr\Variable('this'),
344
                        $name
345
                    )
346
                )
347
            );
348
    }
349
350 1
    protected function createExtendingClass(string $namespace, string $className, string $baseClass)
351
    {
352 1
        $factory = new BuilderFactory;
353
354 1
        $class = $factory->class($className)
355 1
            ->extend('Base' . $className);
356
357 1
        $class->addStmt($factory->method('refresh')
358 1
            ->makePublic()
359 1
            ->setReturnType($className)
360 1
            ->addStmt(
361 1
                new Node\Stmt\Return_(
362 1
                    new Node\Expr\MethodCall(
363 1
                        new Node\Expr\Variable('this'),
364 1
                        'wait',
365
                        [
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...
366 1
                            new Node\Expr\MethodCall(
367 1
                                new Node\Expr\Variable('this'),
368 1
                                'callAsync',
369
                                [
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...
370 1
                                    new Node\Scalar\String_('refresh'),
371
                                ]
372
                            ),
373
                        ]
374
                    )
375
                )
376
            ));
377
378 1
        $node = $factory->namespace($namespace)
379 1
            ->addStmt($factory->use($baseClass)->as('Base' . $className))
380 1
            ->addStmt($class)
381
382 1
            ->getNode()
383
        ;
384
385 1
        $prettyPrinter = new PrettyPrinter\Standard();
386 1
        return $prettyPrinter->prettyPrintFile([
387 1
            $node
388 1
        ]) . PHP_EOL;
389
    }
390
391 1
    protected function save(string $directory, string $fileName, string $fileContents)
392
    {
393 1
        $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $fileName);
394 1
        if (file_exists($directory . $fileName)) {
395
            $this->stdio->outln(', exists!');
396
            return;
397
        }
398
399 1
        $path = $directory . $fileName;
400 1
        $pathChunks = explode(DIRECTORY_SEPARATOR, $path);
401 1
        array_pop($pathChunks);
402 1
        $path = implode(DIRECTORY_SEPARATOR, $pathChunks);
403 1
        if (!file_exists($path)) {
404 1
            mkdir($path, 0777, true);
405
        }
406
407 1
        if (!file_exists($path)) {
408
            throw new Exception('Unable to create: ' . $path);
409
        }
410
411 1
        $this->stdio->out(', writing');
412 1
        file_put_contents($directory . $fileName, $fileContents);
413
414
        do {
415 1
            usleep(500);
416 1
        } while (!file_exists($directory . $fileName));
417
418 1
        $this->stdio->out(', applying PSR-2');
419 1
        $this->applyPsr2($directory . $fileName);
420 1
        $this->stdio->outln(', done!');
421 1
    }
422
423
    /**
424
     * @param string $fileName
425
     */
426 1
    protected function applyPsr2($fileName)
427
    {
428 1
        $file = new \SplFileInfo($fileName);
429 1
        $this->fixer->fixFile(
430
            $file,
431 1
            $this->fixers,
432 1
            false,
433 1
            false,
434 1
            new FileCacheManager(
435 1
                false,
436 1
                '',
437 1
                $this->fixers
438
            )
439
        );
440 1
    }
441
442
443
    /**
444
     * @param ConfigInterface $config
445
     *
446
     * @return FixerInterface[]
447
     */
448 2
    private function prepareFixers(ConfigInterface $config)
449
    {
450 2
        $fixers = $config->getFixers();
451
452 2
        foreach ($fixers as $fixer) {
453 2
            if ($fixer instanceof ConfigAwareInterface) {
454 2
                $fixer->setConfig($config);
455
            }
456
        }
457
458 2
        return $fixers;
459
    }
460
}
461