PhpCsCommand   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 293
Duplicated Lines 0 %

Test Coverage

Coverage 85.94%

Importance

Changes 0
Metric Value
wmc 24
eloc 128
dl 0
loc 293
ccs 110
cts 128
cp 0.8594
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A handle() 0 48 5
A getConfig() 0 9 1
A getRunner() 0 13 2
A createReport() 0 9 1
A __construct() 0 8 1
A manageProgress() 0 18 4
A validateOptions() 0 13 4
A getFinder() 0 11 1
A getResolver() 0 44 5
1
<?php
2
3
namespace Noitran\CsFixer\Console;
4
5
use Illuminate\Console\Command;
6
7
use PhpCsFixer\Config;
8
use PhpCsFixer\Console\Command\FixCommandExitStatusCalculator;
9
use PhpCsFixer\Console\ConfigurationResolver;
10
use PhpCsFixer\Console\Output\ErrorOutput;
11
use PhpCsFixer\Console\Output\NullOutput;
12
use PhpCsFixer\Console\Output\ProcessOutput;
13
use PhpCsFixer\Error\ErrorsManager;
14
use PhpCsFixer\Finder;
15
use PhpCsFixer\Report\ReportSummary;
16
use PhpCsFixer\Runner\Runner;
17
use PhpCsFixer\ToolInfo;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Console\Terminal;
20
use Symfony\Component\EventDispatcher\EventDispatcher;
21
use Symfony\Component\Stopwatch\Stopwatch;
22
use RuntimeException;
23
use ArrayIterator;
24
25
/**
26
 * Class PhpCsCommand
27
 */
28
class PhpCsCommand extends Command
29
{
30
    /**
31
     * The default verbosity of output commands.
32
     *
33
     * @var int
34
     */
35
    protected $verbosity = OutputInterface::VERBOSITY_VERBOSE;
36
37
    /**
38
     * The console command name.
39
     *
40
     * @var string
41
     */
42
    protected $name = 'phpcs:fix';
43
44
    /**
45
     * The name and signature of the console command.
46
     *
47
     * @var string
48
     */
49
    protected $signature = 'phpcs:fix 
50
            {--path=* : The path.}
51
            {--path-mode=override : Specify path mode (can be override or intersection).}
52
            {--allow-risky= : Are risky fixers allowed (can be yes or no).}
53
            {--config= : The path to a .php_cs file.}
54
            {--dry-run : Only shows which files would have been modified.}
55
            {--rules= : The Rules}
56
            {--using-cache=yes : Does cache should be used (can be yes or no).}
57
            {--cache-file= : The path to the cache file.}
58
            {--diff : Also produce diff for each file.}
59
            {--diff-format= : Specify diff format.}
60
            {--format= : To output results in other formats.}
61
            {--stop-on-violation : Stop execution on first violation.}
62
            {--show-progress= : Type of progress indicator (none, run-in, estimating or estimating-max).}';
63
64
    /**
65
     * The console command description.
66
     *
67
     * @var string
68
     */
69
    protected $description = 'Runs PHPCS-Fixer tool to fix code to follow coding standards.';
70
71
    /**
72
     * @var ErrorsManager
73
     */
74
    private $errorsManager;
75
76
    /**
77
     * @var EventDispatcher
78
     */
79
    private $eventDispatcher;
80
81
    /**
82
     * @var Stopwatch
83
     */
84
    private $stopwatch;
85
86
    /**
87
     * @var ToolInfo
88
     */
89
    private $toolInfo;
90
91
    /**
92
     * PhpCsCommand constructor.
93
     */
94 1
    public function __construct()
95
    {
96 1
        parent::__construct();
97
98 1
        $this->errorsManager = new ErrorsManager();
99 1
        $this->eventDispatcher = new EventDispatcher();
100 1
        $this->stopwatch = new Stopwatch();
101 1
        $this->toolInfo = new ToolInfo();
102 1
    }
103
104
    /**
105
     * Handles the Command
106
     */
107 1
    public function handle(): int
108
    {
109 1
        $this->validateOptions();
110 1
        $resolver = $this->getResolver();
111 1
        [$finder, $progressOutput] = $this->manageProgress($resolver);
112 1
        $runner = $this->getRunner($finder, $resolver);
113
114 1
        $this->stopwatch->start('fixFiles');
115 1
        $changed = $runner->fix();
116 1
        $this->stopwatch->stop('fixFiles');
117 1
        $progressOutput->printLegend();
118 1
        $fixEvent = $this->stopwatch->getEvent('fixFiles');
119
120 1
        $reportSummary = $this->createReport($changed, $fixEvent, $resolver);
121
122 1
        $this->getOutput()->isDecorated() ?
123
            $this->getOutput()->write($resolver->getReporter()->generate($reportSummary)) :
124 1
            $this->getOutput()->write(
125 1
                $resolver->getReporter()->generate($reportSummary),
126 1
                false,
127 1
                OutputInterface::OUTPUT_RAW
128
            );
129
130 1
        $invalidErrors = $this->errorsManager->getInvalidErrors();
131 1
        $exceptionErrors = $this->errorsManager->getExceptionErrors();
132 1
        $lintErrors = $this->errorsManager->getLintErrors();
133
134 1
        $errorOutput = new ErrorOutput($this->getOutput());
135
136 1
        if (\count($invalidErrors) > 0) {
137
            $errorOutput->listErrors('linting before fixing', $invalidErrors);
138
        }
139
140 1
        if (\count($exceptionErrors) > 0) {
141
            $errorOutput->listErrors('fixing', $exceptionErrors);
142
        }
143
144 1
        if (\count($lintErrors) > 0) {
145
            $errorOutput->listErrors('linting after fixing', $lintErrors);
146
        }
147
148 1
        $exitStatusCalculator = new FixCommandExitStatusCalculator();
149
150 1
        return $exitStatusCalculator->calculate(
151 1
            $resolver->isDryRun(),
152 1
            \count($changed) > 0,
153 1
            \count($invalidErrors) > 0,
154 1
            \count($exceptionErrors) > 0
155
        );
156
    }
157
158
    /**
159
     * @param $changed
160
     * @param $fixEvent
161
     * @param ConfigurationResolver $resolver
162
     *
163
     * @return ReportSummary
164
     */
165 1
    protected function createReport($changed, $fixEvent, ConfigurationResolver $resolver): ReportSummary
166
    {
167 1
        return new ReportSummary(
168 1
            $changed,
169 1
            $fixEvent->getDuration(),
170 1
            $fixEvent->getMemory(),
171 1
            OutputInterface::VERBOSITY_VERBOSE <= $this->verbosity,
172 1
            $resolver->isDryRun(),
173 1
            $this->getOutput()->isDecorated()
174
        );
175
    }
176
177
    /**
178
     * @param $finder
179
     * @param ConfigurationResolver $resolver
180
     *
181
     * @return Runner
182
     */
183 1
    protected function getRunner($finder, ConfigurationResolver $resolver): Runner
184
    {
185 1
        return new Runner(
186 1
            $finder,
187 1
            $resolver->getFixers(),
188 1
            $resolver->getDiffer(),
189 1
            'none' !== $resolver->getProgress() ? $this->eventDispatcher : null,
190 1
            $this->errorsManager,
191 1
            $resolver->getLinter(),
192 1
            $resolver->isDryRun(),
193 1
            $resolver->getCacheManager(),
194 1
            $resolver->getDirectory(),
195 1
            $resolver->shouldStopOnViolation()
196
        );
197
    }
198
199
    /**
200
     * @return Config
201
     */
202 1
    protected function getConfig(): Config
203
    {
204 1
        $config = new Config('artisan');
205
206 1
        $config->setRules(config('phpcs.rules'))
207 1
            ->setFinder($this->getFinder())
208 1
            ->setCacheFile(storage_path('framework/cache/phpcs.json'));
209
210 1
        return $config;
211
    }
212
213
    /**
214
     * @return Finder
215
     */
216 1
    protected function getFinder(): Finder
217
    {
218 1
        return Finder::create()
219 1
            ->in(base_path())
220 1
            ->exclude(config('phpcs.excludes'))
221 1
            ->notPath('_ide_helper_models.php')
222 1
            ->notPath('_ide_helper.php')
223 1
            ->notPath('.phpstorm.meta.php')
224 1
            ->notName('*.blade.php')
225 1
            ->ignoreDotFiles(true)
226 1
            ->ignoreVCS(true);
227
    }
228
229
    /**
230
     * @return ConfigurationResolver
231
     */
232 1
    protected function getResolver(): ConfigurationResolver
233
    {
234 1
        $resolver = new ConfigurationResolver(
235 1
            $this->getConfig(),
236
            [
237 1
                'allow-risky' => $this->option('allow-risky'),
238 1
                'config' => $this->option('config'),
239 1
                'dry-run' => $this->option('dry-run'),
240 1
                'rules' => $this->option('rules'),
241 1
                'path' => $this->option('path'),
242 1
                'path-mode' => $this->option('path-mode'),
243 1
                'using-cache' => $this->option('using-cache'),
244 1
                'cache-file' => $this->option('cache-file'),
245 1
                'format' => $this->option('format'),
246 1
                'diff' => $this->option('diff'),
247 1
                'diff-format' => $this->option('diff-format'),
248 1
                'stop-on-violation' => $this->option('stop-on-violation'),
249 1
                'verbosity' => $this->verbosity,
250 1
                'show-progress' => $this->option('show-progress'),
251
            ],
252 1
            getcwd(),
253 1
            $this->toolInfo
254
        );
255
256 1
        $this->info(sprintf(
257 1
            'Loaded config <comment>%s</comment>%s.',
258 1
            $resolver->getConfig()->getName(),
259 1
            null === $resolver->getConfigFile() ? '' : ' from "' . $resolver->getConfigFile() . '"'
260
        ));
261
262 1
        if ($resolver->getUsingCache()) {
263 1
            $cacheFile = $resolver->getCacheFile();
264 1
            if (is_file($cacheFile)) {
265 1
                $this->info(sprintf('Using cache file <comment>%s</comment>.', $cacheFile));
266
            }
267
        }
268
269 1
        if ($resolver->configFinderIsOverridden()) {
270
            $this->info(
271
                'Paths from configuration file have been overridden by paths provided as command arguments.'
272
            );
273
        }
274
275 1
        return $resolver;
276
    }
277
278
    /**
279
     *
280
     */
281 1
    protected function validateOptions(): void
282
    {
283 1
        if (null !== $this->option('config') && null !== $this->option('rules')) {
0 ignored issues
show
introduced by
The condition null !== $this->option('rules') is always true.
Loading history...
284
            if (getenv('PHP_CS_FIXER_FUTURE_MODE')) {
285
                throw new RuntimeException(
286
                    'Passing both `config` and `rules` options is not possible. 
287
                    This check was performed as `PHP_CS_FIXER_FUTURE_MODE` env var is set.'
288
                );
289
            }
290
291
            $this->warn('When passing both "--config" and "--rules" 
292
                the rules within the configuration file are not used.');
293
            $this->warn('Passing both options is deprecated; version 
294
                v3.0 PHP-CS-Fixer will exit with a configuration error code.');
295
        }
296 1
    }
297
298
    /**
299
     * @param ConfigurationResolver $resolver
300
     *
301
     * @return array
302
     */
303 1
    protected function manageProgress(ConfigurationResolver $resolver): array
304
    {
305 1
        $finder = $resolver->getFinder();
306 1
        if ('none' === $resolver->getProgress()) {
307
            $progressOutput = new NullOutput();
308 1
        } elseif ('run-in' === $resolver->getProgress()) {
309 1
            $progressOutput = new ProcessOutput($this->getOutput(), $this->eventDispatcher, null, null);
310
        } else {
311
            $finder = new ArrayIterator(iterator_to_array($finder));
312
            $progressOutput = new ProcessOutput(
313
                $this->getOutput(),
314
                $this->eventDispatcher,
315
                'estimating-max' === $resolver->getProgress() ? (new Terminal())->getWidth() : null,
316
                \count($finder)
317
            );
318
        }
319
320 1
        return [$finder, $progressOutput];
321
    }
322
}
323