• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

keradus / PHP-CS-Fixer / 16790327565

25 Jul 2025 09:45AM UTC coverage: 94.467%. First build
16790327565

push

github

keradus
DX: init command

0 of 88 new or added lines in 1 file covered. (0.0%)

28188 of 29839 relevant lines covered (94.47%)

45.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Console/Command/InitCommand.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of PHP CS Fixer.
7
 *
8
 * (c) Fabien Potencier <fabien@symfony.com>
9
 *     Dariusz RumiƄski <dariusz.ruminski@gmail.com>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14

15
namespace PhpCsFixer\Console\Command;
16

17
use PhpCsFixer\Console\Application;
18
use Symfony\Component\Console\Attribute\AsCommand;
19
use Symfony\Component\Console\Command\Command;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Output\ConsoleOutputInterface;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Style\SymfonyStyle;
24

25
/**
26
 * @author Dariusz RumiƄski <dariusz.ruminski@gmail.com>
27
 *
28
 * @internal
29
 */
30
#[AsCommand(name: 'init', description: 'Create config file.')]
31
final class InitCommand extends Command
32
{
33
    private const FIXER_FILENAME = '.xxx_php-cs-fixer.dist.php';
34
    private const COMPOSER_FILENAME = 'composer.json';
35

36
    /** @TODO PHP 8.0 - remove the property */
37
    protected static $defaultName = 'init';
38

39
    /** @TODO PHP 8.0 - remove the property */
40
    protected static $defaultDescription = 'Create config file.';
41

42
    protected function execute(InputInterface $input, OutputInterface $output): int
43
    {
44
        $stdErr = $output;
×
45

46
        if ($output instanceof ConsoleOutputInterface) {
×
47
            $stdErr = $output->getErrorOutput();
×
48
            $stdErr->writeln(Application::getAboutWithRuntime(true));
×
49
        }
50

NEW
51
        $io = new SymfonyStyle($input, $stdErr);
×
52

NEW
53
        $io->warning('This command is experimental');
×
54

NEW
55
        if (file_exists(self::FIXER_FILENAME)) {
×
NEW
56
            $io->error(\sprintf('Configuration file `%s` already exists.', self::FIXER_FILENAME));
×
57

NEW
58
            return Command::FAILURE;
×
59
        }
60

NEW
61
        $isComposerFilePresent = false;
×
62

NEW
63
        $canAutoPHP = false;
×
NEW
64
        $canAutoPHPUnit = false;
×
65

NEW
66
        if (file_exists(self::COMPOSER_FILENAME)) {
×
NEW
67
            $composerJson = json_decode(file_get_contents(self::COMPOSER_FILENAME), true, 512, \JSON_THROW_ON_ERROR);
×
NEW
68
            $isComposerFilePresent = true;
×
69

NEW
70
            if (null !== $this->detectPhpFromComposer($composerJson)) {
×
NEW
71
                $canAutoPHP = true;
×
72
                // $phpOptions = ['@autoPHPMigration'];
73
            }
NEW
74
            if (null !== $this->detectPackageInComposer($composerJson, 'phpunit/phpunit')) {
×
75
                // $phpUnitOptions = ['@autoPHPUnitMigration'];
NEW
76
                $canAutoPHPUnit = true;
×
77
            }
78
        }
79

NEW
80
        $io->note('We recommend usage of `@auto` ruleset. It will take insights from your existing `composer.json` to confiugre project the best:');
×
81

NEW
82
        $io->listing(array_filter([
×
NEW
83
            '<fg=blue>`@PER-CS`</> - rules that follow PER Coding Style 2.0 (https://www.php-fig.org/per/coding-style/)',
×
NEW
84
            $canAutoPHP ? '<fg=blue>`@autoPHPMigration`</> - migration rules towards the minimum supported PHP by your project' : '',
×
NEW
85
            $canAutoPHPUnit ? '<fg=blue>`@autoPHPUnitMigration`</> - migration rules towards the minimum supported PHPUnit by your project' : '',
×
NEW
86
        ]));
×
87

NEW
88
        $rules = [];
×
89

NEW
90
        $useAutoSet = 'yes' === $io->choice(
×
NEW
91
            'Do you want to use <fg=blue>`@default`</> ruleset?',
×
NEW
92
            ['yes', 'no'],
×
NEW
93
            'yes',
×
NEW
94
        );
×
95

NEW
96
        if ($useAutoSet) {
×
NEW
97
            $rules['@auto'] = true;
×
98
        }
99

NEW
100
        $sets = $io->choice(
×
NEW
101
            'Do you want to use any of other recommended ruleset? (multi-choice)',
×
NEW
102
            (!$useAutoSet && $canAutoPHP ? ['@autoPHPMigration' => 'migration rules towards the minimum supported PHP by your project'] : [])
×
NEW
103
                     + (!$useAutoSet && $canAutoPHPUnit ? ['@autoPHPUnitMigration' => 'migration rules towards the minimum supported PHPUnit by your project'] : [])
×
NEW
104
                     + [
×
NEW
105
                         '@PhpCsFixer' => 'set of rules recommended by us - opinioated , extends <fg=blue>`@PER-CS`</> and <fg=blue>`@Symfony`</> (https://cs.symfony.com/doc/ruleSets/PhpCsFixer.html)',
×
NEW
106
                         '@Symfony' => 'set of rules recommended by Symfony team, extends <fg=blue>`@PER-CS`</> (https://cs.symfony.com/doc/ruleSets/Symfony.html)',
×
NEW
107
                     ]
×
NEW
108
                     + [
×
NEW
109
                         'no' => 'no',
×
NEW
110
                     ],
×
NEW
111
            'no',
×
NEW
112
            true
×
NEW
113
        );
×
114

NEW
115
        $sets = array_unique(array_filter($sets, static fn ($item) => 'no' !== $item));
×
NEW
116
        foreach ($sets as $set) {
×
NEW
117
            $rules[$set] = true;
×
118
        }
119

NEW
120
        $io->note('Risky rules exist. A rule is risky if it could change code behaviour, e.g. transforming `==` into `===`, and you shall always review proposed changes');
×
NEW
121
        $isRiskyAllowed = 'yes' === $io->choice(
×
NEW
122
            'Do you want to enable risky rules?',
×
NEW
123
            ['yes', 'no'],
×
NEW
124
            'no',
×
NEW
125
        );
×
126

NEW
127
        if ($isRiskyAllowed) {
×
NEW
128
            foreach ($rules as $set => $val) {
×
129
                // reset order of keys in set, so set and set:risky are always one after another
NEW
130
                unset($rules[$set]);
×
NEW
131
                $rules[$set] = true;
×
132

NEW
133
                $rules[$set.':risky'] = true;
×
134
            }
135
        }
136

NEW
137
        $content = str_replace(
×
NEW
138
            [
×
NEW
139
                '/*{{ IS_RISKY_ALLOWED }}*/',
×
NEW
140
                '/*{{ RULES }}*/',
×
NEW
141
            ],
×
NEW
142
            [
×
NEW
143
                $isRiskyAllowed ? 'true' : 'false',
×
NEW
144
                "[\n".implode(
×
NEW
145
                    ",\n",
×
NEW
146
                    array_map(
×
NEW
147
                        static fn ($item) => "        '{$item}' => true",
×
NEW
148
                        array_keys($rules)
×
NEW
149
                    )
×
NEW
150
                )."\n    ]",
×
NEW
151
            ],
×
NEW
152
            file_get_contents(__DIR__.'/../../../doc/.php-cs-fixer.dist.php.template')
×
NEW
153
        );
×
154

NEW
155
        $writeResult = @file_put_contents(self::FIXER_FILENAME, $content);
×
NEW
156
        if (false === $writeResult) {
×
NEW
157
            throw new IOException(\sprintf('Failed to write file "%s".', self::FIXER_FILENAME));
×
158
        }
159

NEW
160
        return Command::SUCCESS;
×
161
    }
162

163
    private static function detectPhpFromComposer(array $composerJson): ?string
164
    {
NEW
165
        if (isset($composerJson['config']['platform']['php'])) {
×
NEW
166
            return $composerJson['config']['platform']['php'];
×
167
        }
168

NEW
169
        if (isset($composerJson['require']['php'])) {
×
NEW
170
            return $composerJson['require']['php'];
×
171
        }
172

NEW
173
        return null;
×
174
    }
175

176
    private static function detectPackageInComposer(array $composerJson, string $package): ?string
177
    {
NEW
178
        if (isset($composerJson['require'][$package])) {
×
NEW
179
            return $composerJson['require'][$package];
×
180
        }
181

NEW
182
        if (isset($composerJson['require-dev'][$package])) {
×
NEW
183
            return $composerJson['require-dev'][$package];
×
184
        }
185

NEW
186
        return null;
×
187
    }
188
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc