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 (#10)
by Cees-Jan
09:36
created

ResourceGenerator::createExtendingClass()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 40
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 40
ccs 24
cts 24
cp 1
rs 8.8571
cc 1
eloc 24
nc 1
nop 3
crap 1
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 56 and the first side effect is on line 24.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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
0 ignored issues
show
Bug introduced by
Possible parse error: class missing opening or closing brace
Loading history...
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(string $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): string
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): string
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
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): string
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
                        [
366 1
                            new Node\Expr\MethodCall(
367 1
                                new Node\Expr\Variable('this'),
368 1
                                'callAsync',
369
                                [
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
        file_put_contents(
442
            $fileName,
443
            str_replace(
444
                '<?php',
445
                '<?php' . PHP_EOL . 'declare(strict_types=1);'',
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_ENCAPSED_AND_WHITESPACE, expecting ',' or ')'
Loading history...
446
                file_get_contents(
447
                    $fileName
448 2
                )
449
            )
450 2
        );
451
    }
452 2
453 2
454 2
    /**
455
     * @param ConfigInterface $config
456
     *
457
     * @return FixerInterface[]
458 2
     */
459
    private function prepareFixers(ConfigInterface $config): array
460
    {
461
        $fixers = $config->getFixers();
462
463
        foreach ($fixers as $fixer) {
464
            if ($fixer instanceof ConfigAwareInterface) {
465
                $fixer->setConfig($config);
466
            }
467
        }
468
469
        return $fixers;
470
    }
471
}
472