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
Pull Request — master (#7)
by Cees-Jan
01:55
created

ResourceGenerator::generateFromDefinition()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 96
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 96
rs 8.3859
ccs 0
cts 57
cp 0
cc 1
eloc 69
nc 1
nop 1
crap 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
        foreach ($this->definitions as $definition) {
121
            $this->stdio->outln('-----');
122
            $this->stdio->outln('- Definition: ' . $definition);
123
            $this->stdio->outln('-----');
124
            $this->generateFromDefinition($definition);
125
            $this->stdio->outln('-----');
126
        }
127
    }
128
129 1
    public function checkValidity()
130
    {
131 1
        if ($this->path === null) {
132
            throw new \InvalidArgumentException('No path set');
133
        }
134
135 1
        if (!file_exists($this->path)) {
136 1
            throw new \InvalidArgumentException('Path doesn\'t exist');
137
        }
138
139
        if (is_dir($this->path)) {
140
            throw new \InvalidArgumentException('Path isn\'t a directory');
141
        }
142
143
        if (count($this->definitions) < 1) {
144
            throw new \InvalidArgumentException('Not enough definitions');
145
        }
146
147
        foreach ($this->definitions as $definition) {
148
            if (!file_exists($definition)) {
149
                throw new \InvalidArgumentException('Definition "' . $definition . '" doesn\'t exist');
150
            }
151
        }
152
    }
153
154
    public function generateFromDefinition($definition)
155
    {
156
        $yaml = $this->readYaml($definition);
157
158
        $namespacePadding = explode('\\', $yaml['class']);
159
        $namespace = explode('\\', $yaml['namespace']);
160
161
        $yaml['class'] = array_pop($namespacePadding);
162
        $yaml['namespace'] = implode('\\', array_merge($namespace, $namespacePadding));
163
164
        $namespacePathPadding = implode(DIRECTORY_SEPARATOR, $namespacePadding);
165
        $baseClass = implode(
166
            '\\',
167
            array_merge(
168
                $namespace,
169
                $namespacePadding,
170
                [
171
                    $yaml['class']
172
                ]
173
            )
174
        );
175
176
        $this->stdio->out('Interface: generating');
177
        $this->save(
178
            $this->path .
179
                DIRECTORY_SEPARATOR .
180
                $namespacePathPadding .
181
                DIRECTORY_SEPARATOR,
182
            $yaml['class'] .
183
                'Interface.php',
184
            $this->createInterface($yaml)
185
        );
186
187
        $this->stdio->out('Base class: generating');
188
        $this->save(
189
            $this->path .
190
                DIRECTORY_SEPARATOR .
191
                $namespacePathPadding .
192
                DIRECTORY_SEPARATOR,
193
            $yaml['class'] .
194
                '.php',
195
            $this->createBaseClass($yaml)
196
        );
197
198
        $this->stdio->out('Async class: generating');
199
        $this->save(
200
            $this->path .
201
                DIRECTORY_SEPARATOR .
202
                'Async' .
203
                DIRECTORY_SEPARATOR .
204
                $namespacePathPadding .
205
                DIRECTORY_SEPARATOR,
206
            $yaml['class'] .
207
                '.php',
208
            $this->createExtendingClass(
209
                implode(
210
                    '\\',
211
                    array_merge(
212
                        $namespace,
213
                        [
214
                            'Async',
215
                        ],
216
                        $namespacePadding
217
                    )
218
                ),
219
                $yaml['class'],
220
                $baseClass
221
            )
222
        );
223
224
        $this->stdio->out('Sync class: generating');
225
        $this->save(
226
            $this->path .
227
                DIRECTORY_SEPARATOR .
228
                'Sync' .
229
                DIRECTORY_SEPARATOR .
230
                $namespacePathPadding .
231
                DIRECTORY_SEPARATOR,
232
            $yaml['class'] .
233
                '.php',
234
            $this->createExtendingClass(
235
                implode(
236
                    '\\',
237
                    array_merge(
238
                        $namespace,
239
                        [
240
                            'Sync',
241
                        ],
242
                        $namespacePadding
243
                    )
244
                ),
245
                $yaml['class'],
246
                $baseClass
247
            )
248
        );
249
    }
250
251
    protected function readYaml(string $filename): array
252
    {
253
        return Yaml::parse(file_get_contents($filename));
254
    }
255
256
    protected function createBaseClass(array $yaml)
257
    {
258
        $factory = new BuilderFactory;
259
260
        $class = $factory->class($yaml['class'])
261
            ->implement($yaml['class'] . 'Interface')
262
            ->makeAbstract();
263
        $class->addStmt(
264
            new Node\Stmt\TraitUse([
265
                new Node\Name('TransportAwareTrait')
266
            ])
267
        );
268
269
        foreach ($yaml['properties'] as $name => $details) {
270
            $type = $details;
271
            if (is_array($details)) {
272
                $type = $details['type'];
273
            }
274
            $class->addStmt($this->createProperty($factory, $type, $name, $details));
275
            $class->addStmt($this->createMethod($factory, $type, $name, $details));
276
        }
277
278
        $node = $factory->namespace($yaml['namespace'])
279
            ->addStmt($factory->use('WyriHaximus\ApiClient\Resource\TransportAwareTrait'))
280
            ->addStmt($class)
281
282
            ->getNode()
283
        ;
284
285
        $prettyPrinter = new PrettyPrinter\Standard();
286
        return $prettyPrinter->prettyPrintFile([
287
            $node
288
        ]) . PHP_EOL;
289
    }
290
291
    protected function createInterface(array $yaml)
292
    {
293
        $factory = new BuilderFactory;
294
295
        $class = $factory->interface($yaml['class'] . 'Interface')
296
            ->extend('ResourceInterface');
297
298
        foreach ($yaml['properties'] as $name => $details) {
299
            $type = $details;
300
            if (is_array($details)) {
301
                $type = $details['type'];
302
            }
303
            $class->addStmt($this->createMethod($factory, $type, $name, $details));
304
        }
305
306
        $node = $factory->namespace($yaml['namespace'])
307
            ->addStmt($factory->use(ResourceInterface::class))
308
            ->addStmt($class)
309
            ->getNode()
310
        ;
311
312
        $prettyPrinter = new PrettyPrinter\Standard();
313
        return $prettyPrinter->prettyPrintFile([
314
            $node
315
        ]) . PHP_EOL;
316
    }
317
318
    protected function createProperty(BuilderFactory $factory, string $type, string $name, $details): Property
319
    {
320
        $property = $factory->property($name)
321
            ->makeProtected()
322
            ->setDocComment('/**
323
                              * @var ' . $type . '
324
                              */');
325
        if (isset($details['default'])) {
326
            $property->setDefault($details['default']);
327
        }
328
329
        return $property;
330
    }
331
332
    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
        return $factory->method(Inflector::camelize($name))
335
            ->makePublic()
336
            ->setReturnType($type)
337
            ->setDocComment('/**
338
                              * @return ' . $type . '
339
                              */')
340
            ->addStmt(
341
                new Node\Stmt\Return_(
342
                    new Node\Expr\PropertyFetch(
343
                        new Node\Expr\Variable('this'),
344
                        $name
345
                    )
346
                )
347
            );
348
    }
349
350
    protected function createExtendingClass(string $namespace, string $className, string $baseClass)
351
    {
352
        $factory = new BuilderFactory;
353
354
        $class = $factory->class($className)
355
            ->extend('Base' . $className);
356
357
        $class->addStmt($factory->method('refresh')
358
            ->makePublic()
359
            ->setReturnType($className)
360
            ->addStmt(
361
                new Node\Stmt\Return_(
362
                    new Node\Expr\MethodCall(
363
                        new Node\Expr\Variable('this'),
364
                        '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
                            new Node\Expr\MethodCall(
367
                                new Node\Expr\Variable('this'),
368
                                '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
                                    new Node\Scalar\String_('refresh'),
371
                                ]
372
                            ),
373
                        ]
374
                    )
375
                )
376
            ));
377
378
        $node = $factory->namespace($namespace)
379
            ->addStmt($factory->use($baseClass)->as('Base' . $className))
380
            ->addStmt($class)
381
382
            ->getNode()
383
        ;
384
385
        $prettyPrinter = new PrettyPrinter\Standard();
386
        return $prettyPrinter->prettyPrintFile([
387
            $node
388
        ]) . PHP_EOL;
389
    }
390
391
    protected function save(string $directory, string $fileName, string $fileContents)
392
    {
393
        $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $fileName);
394
        if (file_exists($directory . $fileName)) {
395
            $this->stdio->outln(', exists!');
396
            return;
397
        }
398
399
        $path = $directory . $fileName;
400
        $pathChunks = explode(DIRECTORY_SEPARATOR, $path);
401
        array_pop($pathChunks);
402
        $path = implode(DIRECTORY_SEPARATOR, $pathChunks);
403
        if (!file_exists($path)) {
404
            mkdir($path, 0777, true);
405
        }
406
407
        if (!file_exists($path)) {
408
            throw new Exception('Unable to create: ' . $path);
409
        }
410
411
        $this->stdio->out(', writing');
412
        file_put_contents($directory . $fileName, $fileContents);
413
414
        do {
415
            usleep(500);
416
        } while (!file_exists($directory . $fileName));
417
418
        $this->stdio->out(', applying PSR-2');
419
        $this->applyPsr2($directory . $fileName);
420
        $this->stdio->outln(', done!');
421
    }
422
423
    /**
424
     * @param string $fileName
425
     */
426
    protected function applyPsr2($fileName)
427
    {
428
        $file = new \SplFileInfo($fileName);
429
        $this->fixer->fixFile(
430
            $file,
431
            $this->fixers,
432
            false,
433
            false,
434
            new FileCacheManager(
435
                false,
436
                '',
437
                $this->fixers
438
            )
439
        );
440
    }
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