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 ( f3a9c0...75651d )
by Cees-Jan
08:09
created

Install::setUpFixers()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 29
nc 1
nop 0
1
<?php declare(strict_types = 1);
2
3
namespace ApiClients\Tools\Installer;
4
5
use Composer\Factory;
6
use Composer\Json\JsonFile;
7
use PhpParser\Node\Name;
8
use PhpParser\Node\Stmt\Namespace_;
9
use PhpParser\Node\Stmt\Use_;
10
use PhpParser\Parser;
11
use PhpParser\ParserFactory;
12
use PhpParser\PrettyPrinter\Standard;
13
use Symfony\Component\Console\Command\Command;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use Symfony\Component\Console\Style\SymfonyStyle;
17
use Symfony\CS\AbstractFixer;
18
use Symfony\CS\Config\Config;
19
use Symfony\CS\ConfigAwareInterface;
20
use Symfony\CS\ConfigInterface;
21
use Symfony\CS\Fixer;
22
use Symfony\CS\FixerInterface;
23
24
final class Install extends Command
25
{
26
    const COMMAND = 'install';
27
28
    const NS_VENDOR       = '__NS_VENDOR__';
29
    const NS_TESTS_VENDOR = '__NS_TESTS_VENDOR__';
30
    const NS_PROJECT      = '__NS_PROJECT__';
31
    const PACKAGE_NAME    = '__PACKAGE_NAME__';
32
    const AUTHOR          = '__AUTHOR__';
33
    const AUTHOR_NAME     = '__AUTHOR_NAME__';
34
    const AUTHOR_EMAIL    = '__AUTHOR_EMAIL__';
35
36
    /**
37
     * @var AbstractFixer[]
38
     */
39
    private $fixers;
40
41
    protected function execute(InputInterface $input, OutputInterface $output)
42
    {
43
        retry:
44
        $style = new SymfonyStyle( $input, $output );
45
46
        $style->title('Welcome to the IceHawk installer.');
47
        $style->section('Please answer the following questions.');
48
49
        $replacements = [];
50
        $replacements[self::NS_VENDOR]       = $style->ask('What is your vendor namespace?', 'MyVendor');
51
        $replacements[self::NS_TESTS_VENDOR] = $style->ask('What is your vendor test namespace?', 'MyVendor\\Tests');
52
        $replacements[self::NS_PROJECT]      = $style->ask('What is your project namespace?', 'MyProject');
53
        $replacements[self::PACKAGE_NAME]    = $style->ask(
54
            'What is your package name?',
55
            strtolower($replacements[self::NS_VENDOR]) . '/' . strtolower($replacements[self::NS_PROJECT])
56
        );
57
        $replacements[self::AUTHOR_NAME]  = $style->ask('What is your name?');
58
        $replacements[self::AUTHOR_EMAIL] = $style->ask('What is your email address?');
59
60
        while ( false === filter_var( $replacements[self::AUTHOR_EMAIL], FILTER_VALIDATE_EMAIL ) )
61
        {
62
            $replacements[self::AUTHOR_EMAIL] = $style->ask('Invalid email address, try again.');
63
        }
64
65
        $replacements[self::AUTHOR] = "{$replacements[self::AUTHOR_NAME]} <{$replacements[self::AUTHOR_EMAIL]}>";
66
67
        $style->section('Summary:');
68
69
        $style->table(
70
            [],
71
            [
72
                ['Your namespace', $replacements[self::NS_VENDOR] . '\\' . $replacements[self::NS_PROJECT]],
73
                ['Your test namespace', $replacements[self::NS_TESTS_VENDOR] . '\\' . $replacements[self::NS_PROJECT]],
74
                ['Your package', $replacements[self::PACKAGE_NAME]],
75
                ['Author name', $replacements[self::AUTHOR_NAME]],
76
                ['Author email', $replacements[self::AUTHOR_EMAIL]],
77
            ]
78
        );
79
80
        $installNow = $style->choice(
81
            'All settings correct?',
82
            ['Yes', 'Change settings', 'Cancel installation'],
83
            'Yes'
84
        );
85
86
        switch ( $installNow )
87
        {
88
            case 'Yes':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
89
            {
90
                $style->text('Creating your middleware package now.');
91
                $this->updateComposerJson($replacements, $style);
92
                $this->updatePHPFiles($replacements, $style);
93
                $this->removingOurSelfs($style);
94
                $style->section('Your middleware package creation has been successfully.');
95
96
                break;
97
            }
98
            case 'Change settings':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
99
            {
100
                goto retry;
101
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
102
            }
103
            case 'Cancel installation':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
104
            {
105
                $style->error( 'Installation canceled.' );
106
107
                return 9;
108
            }
109
        }
110
111
        return 0;
112
    }
113
114
    private function updateComposerJson(array $replacements, SymfonyStyle $style)
115
    {
116
        $style->section('Updating composer.json');
117
        $json = new JsonFile(Factory::getComposerFile());
118
        $style->text('Reading composer.json');
119
        $composerJson = $json->read();
120
121
        $style->text('Replacing package name');
122
        $composerJson['name'] = $replacements[self::PACKAGE_NAME];
123
124
        $style->text('Adding authors');
125
        $composerJson['authors'] = [
126
            [
127
                'name'  => $replacements[self::AUTHOR_NAME],
128
                'email' => $replacements[self::AUTHOR_EMAIL],
129
            ],
130
        ];
131
132
        $style->text('Updating autoload');
133
        $composerJson['autoload']['psr-4'][$replacements[self::NS_VENDOR] . '\\' . $replacements[self::NS_PROJECT] . '\\'] = 'src/';
134
        $composerJson['autoload-dev']['psr-4'][$replacements[self::NS_TESTS_VENDOR] . '\\' . $replacements[self::NS_PROJECT] . '\\'] = 'tests/';
135
136
        $style->text('Removing package needed for installation and post create script');
137
        unset(
138
            $composerJson['autoload']['psr-4']['ApiClients\\Middleware\\Installer\\'],
139
            $composerJson['autoload']['psr-4']['ApiClients\\Middleware\\Skeleton\\'],
140
            $composerJson['autoload-dev']['psr-4']['ApiClients\\Tests\\Middleware\\Skeleton\\'],
141
            $composerJson['require']['composer/composer'],
142
            $composerJson['require']['friendsofphp/php-cs-fixer'],
143
            $composerJson['require']['nikic/php-parser'],
144
            $composerJson['require']['ocramius/package-versions'],
145
            $composerJson['require']['symfony/console'],
146
            $composerJson['scripts']['post-create-project-cmd']
147
        );
148
149
        $style->text('Writing updated composer.json');
150
        $json->write($composerJson);
151
        $style->success('Updated composer.json');
152
    }
153
154
    private function updatePHPFiles(array $replacements, SymfonyStyle $style)
155
    {
156
        $style->section('Updating namespaces in PHP files');
157
        $this->setUpFixers();
158
        $path = dirname(__DIR__) . DIRECTORY_SEPARATOR;
159
        foreach([
160
            'src' => self::NS_VENDOR,
161
            'tests' => self::NS_TESTS_VENDOR,
162
        ] as $dir => $namespace) {
163
            $this->iterateDirectory(
164
                $path . $dir . DIRECTORY_SEPARATOR,
165
                $style,
166
                $replacements[$namespace] . '\\' . $replacements[self::NS_PROJECT],
167
                $replacements[self::NS_VENDOR] . '\\' . $replacements[self::NS_PROJECT]
168
            );
169
        }
170
        $style->success('Namespaces updated');
171
    }
172
173
    private function iterateDirectory(string $path, SymfonyStyle $style, string $namespace, string $namespaceSrc)
174
    {
175
        $d = dir($path);
176
        while (false !== ($entry = $d->read())) {
177
            $entryPath = $path . $entry;
178
            if (!is_file($entryPath)) {
179
                continue;
180
            }
181
182
            $style->text('Updating ' . $entry . ' namespace');
183
            $this->updateNamespaces($entryPath, $namespace, $namespaceSrc);
184
        }
185
        $d->close();
186
    }
187
188
    private function updateNamespaces(string $fileName, string $namespace, string $namespaceSrc)
189
    {
190
        $md5 = md5_file($fileName);
191
        $stmts = (new ParserFactory())->create(ParserFactory::ONLY_PHP7)->parse(file_get_contents($fileName));
192
        if ($stmts === null) {
193
            return;
194
        }
195
        foreach ($stmts as $index => $node) {
196
            if (!($node instanceof Namespace_)) {
197
                continue;
198
            }
199
200
            $nodeStmts = $node->stmts;
201
202
            foreach ($nodeStmts as $nodeIndex => $nodeNode) {
203
                if (!($nodeNode instanceof Use_)) {
204
                    continue;
205
                }
206
207
                foreach ($nodeNode->uses as $useUse) {
208
                    $nameString = $useUse->name->toString();
209
                    if (strpos($nameString, 'ApiClients\\Middleware\\Skeleton') !== 0) {
210
                        continue;
211
                    }
212
213
                    $useUse->name = new Name(
214
                        $namespaceSrc . substr($nameString, strlen('ApiClients\\Middleware\\Skeleton'))
215
                    );
216
                }
217
            }
218
219
            $stmts[$index] = new Namespace_(
220
                new Name(
221
                    $namespace
222
                ),
223
                $nodeStmts,
224
                $node->getAttributes()
225
            );
226
            break;
227
        }
228
        file_put_contents($fileName, (new Standard())->prettyPrintFile($stmts) . PHP_EOL);
229
        while($md5 === md5_file($fileName)) {
230
            usleep(500);
231
        }
232
        $this->applyPsr2($fileName);
233
    }
234
235
236
    /**
237
     * @param string $fileName
238
     */
239
    protected function applyPsr2(string $fileName)
240
    {
241
        $file = new \SplFileInfo($fileName);
242
        $new = file_get_contents($file->getRealPath());
243
244
        foreach ($this->fixers as $fixer) {
245
            if (!$fixer->supports($file)) {
246
                continue;
247
            }
248
249
            $new = $fixer->fix($file, $new);
250
        }
251
252
        file_put_contents(
253
            $fileName,
254
            str_replace(
255
                '<?php',
256
                '<?php declare(strict_types=1);',
257
                $new
258
            )
259
        );
260
    }
261
262
    protected function setUpFixers()
263
    {
264
        $fixer = new Fixer();
265
        $fixer->registerCustomFixers([
266
            new Fixer\Symfony\ExtraEmptyLinesFixer(),
267
            new Fixer\Symfony\SingleBlankLineBeforeNamespaceFixer(),
268
            new Fixer\PSR0\Psr0Fixer(),
269
            new Fixer\PSR1\EncodingFixer(),
270
            new Fixer\PSR1\ShortTagFixer(),
271
            new Fixer\PSR2\BracesFixer(),
272
            new Fixer\PSR2\ElseifFixer(),
273
            new Fixer\PSR2\EofEndingFixer(),
274
            new Fixer\PSR2\FunctionCallSpaceFixer(),
275
            new Fixer\PSR2\FunctionDeclarationFixer(),
276
            new Fixer\PSR2\IndentationFixer(),
277
            new Fixer\PSR2\LineAfterNamespaceFixer(),
278
            new Fixer\PSR2\LinefeedFixer(),
279
            new Fixer\PSR2\LowercaseConstantsFixer(),
280
            new Fixer\PSR2\LowercaseKeywordsFixer(),
281
            new Fixer\PSR2\MethodArgumentSpaceFixer(),
282
            new Fixer\PSR2\MultipleUseFixer(),
283
            new Fixer\PSR2\ParenthesisFixer(),
284
            new Fixer\PSR2\PhpClosingTagFixer(),
285
            new Fixer\PSR2\SingleLineAfterImportsFixer(),
286
            new Fixer\PSR2\TrailingSpacesFixer(),
287
            new Fixer\PSR2\VisibilityFixer(),
288
            new Fixer\Contrib\NewlineAfterOpenTagFixer(),
289
        ]);
290
        $config = Config::create()->fixers($fixer->getFixers());
291
        $fixer->addConfig($config);
292
        $this->fixers = $this->prepareFixers($config);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->prepareFixers($config) of type array<integer,object<Symfony\CS\FixerInterface>> is incompatible with the declared type array<integer,object<Symfony\CS\AbstractFixer>> of property $fixers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
293
    }
294
295
    /**
296
     * @param ConfigInterface $config
297
     *
298
     * @return FixerInterface[]
299
     */
300
    private function prepareFixers(ConfigInterface $config): array
301
    {
302
        $fixers = $config->getFixers();
303
304
        foreach ($fixers as $fixer) {
305
            if ($fixer instanceof ConfigAwareInterface) {
0 ignored issues
show
Bug introduced by
The class Symfony\CS\ConfigAwareInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
306
                $fixer->setConfig($config);
307
            }
308
        }
309
310
        return $fixers;
311
    }
312
313
    private function removingOurSelfs(SymfonyStyle $style)
314
    {
315
        $style->section('Removing installer');
316
        unlink(__DIR__ . DIRECTORY_SEPARATOR . 'Install.php');
317
        $style->text('Removed Install.php');
318
        unlink(__DIR__ . DIRECTORY_SEPARATOR . 'Installer.php');
319
        $style->text('Removed Installer.php');
320
        rmdir(__DIR__);
321
        $style->text('Removed installer directory');
322
        $style->success('Installer removed');
323
    }
324
}