Passed
Pull Request — master (#297)
by Fabien
02:21
created

RunCommand::getConfiguration()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 21
rs 9.9
cc 4
nc 6
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Churn\Command;
6
7
use Churn\Configuration\Config;
8
use Churn\Configuration\Loader;
9
use Churn\File\FileFinder;
10
use Churn\Process\Observer\OnSuccess;
11
use Churn\Process\Observer\OnSuccessAccumulate;
12
use Churn\Process\Observer\OnSuccessCollection;
13
use Churn\Process\Observer\OnSuccessProgress;
14
use Churn\Process\ProcessFactory;
15
use Churn\Process\ProcessHandlerFactory;
16
use Churn\Result\ResultAccumulator;
17
use Churn\Result\ResultsRendererFactory;
18
use InvalidArgumentException;
19
use Symfony\Component\Console\Command\Command;
20
use Symfony\Component\Console\Helper\ProgressBar;
21
use Symfony\Component\Console\Input\InputArgument;
22
use Symfony\Component\Console\Input\InputInterface;
23
use Symfony\Component\Console\Input\InputOption;
24
use Symfony\Component\Console\Output\OutputInterface;
25
use Symfony\Component\Console\Output\StreamOutput;
26
use Webmozart\Assert\Assert;
27
28
/** @SuppressWarnings(PHPMD.CouplingBetweenObjects) */
29
class RunCommand extends Command
30
{
31
32
    public const LOGO = "
33
    ___  _   _  __  __  ____  _  _     ____  _   _  ____
34
   / __)( )_( )(  )(  )(  _ \( \( )___(  _ \( )_( )(  _ \
35
  ( (__  ) _ (  )(__)(  )   / )  ((___))___/ ) _ (  )___/
36
   \___)(_) (_)(______)(_)\_)(_)\_)   (__)  (_) (_)(__)
37
";
38
39
    /**
40
     * The process handler factory.
41
     *
42
     * @var ProcessHandlerFactory
43
     */
44
    private $processHandlerFactory;
45
46
    /**
47
     * The renderer factory.
48
     *
49
     * @var ResultsRendererFactory
50
     */
51
    private $renderFactory;
52
53
    /**
54
     * Class constructor.
55
     *
56
     * @param ProcessHandlerFactory $processHandlerFactory The process handler factory.
57
     * @param ResultsRendererFactory $renderFactory The Results Renderer Factory.
58
     */
59
    public function __construct(ProcessHandlerFactory $processHandlerFactory, ResultsRendererFactory $renderFactory)
60
    {
61
        parent::__construct();
62
63
        $this->processHandlerFactory = $processHandlerFactory;
64
        $this->renderFactory = $renderFactory;
65
    }
66
67
    /**
68
     * Returns a new instance of the command.
69
     */
70
    public static function newInstance(): self
71
    {
72
        return new self(new ProcessHandlerFactory(), new ResultsRendererFactory());
73
    }
74
75
    /**
76
     * Configure the command
77
     */
78
    protected function configure(): void
79
    {
80
        $this->setName('run')
81
            ->addArgument('paths', InputArgument::IS_ARRAY, 'Path to source to check.')
82
            ->addOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Path to the configuration file', 'churn.yml') // @codingStandardsIgnoreLine
83
            ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format to use', 'text')
84
            ->addOption('output', 'o', InputOption::VALUE_REQUIRED, 'The path where to write the result')
85
            ->addOption('parallel', null, InputOption::VALUE_REQUIRED, 'Number of parallel jobs')
86
            ->addOption('progress', 'p', InputOption::VALUE_NONE, 'Show progress bar')
87
            ->setDescription('Check files')
88
            ->setHelp('Checks the churn on the provided path argument(s).');
89
    }
90
91
    /**
92
     * Execute the command.
93
     *
94
     * @param InputInterface $input Input.
95
     * @param OutputInterface $output Output.
96
     */
97
    protected function execute(InputInterface $input, OutputInterface $output): int
98
    {
99
        $this->displayLogo($input, $output);
100
        $accumulator = $this->analyze($input, $output, $this->getConfiguration($input));
101
        $this->writeResult($input, $output, $accumulator);
102
103
        return 0;
104
    }
105
106
    /**
107
     * @param InputInterface $input Input.
108
     * @throws InvalidArgumentException If paths argument invalid.
109
     */
110
    private function getConfiguration(InputInterface $input): Config
111
    {
112
        $config = Loader::fromPath((string) $input->getOption('configuration'));
113
114
        if ([] !== $input->getArgument('paths')) {
115
            $config->setDirectoriesToScan($input->getArgument('paths'));
0 ignored issues
show
Bug introduced by
It seems like $input->getArgument('paths') can also be of type null and string; however, parameter $directories of Churn\Configuration\Config::setDirectoriesToScan() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

115
            $config->setDirectoriesToScan(/** @scrutinizer ignore-type */ $input->getArgument('paths'));
Loading history...
116
        }
117
118
        if ([] === $config->getDirectoriesToScan()) {
119
            throw new InvalidArgumentException(
120
                'Provide the directories you want to scan as arguments, ' .
121
                'or configure them under "directoriesToScan" in your churn.yml file.'
122
            );
123
        }
124
125
        if (null !== $input->getOption('parallel')) {
126
            Assert::integerish($input->getOption('parallel'), 'Amount of parallel jobs should be an integer');
127
            $config->setParallelJobs((int) $input->getOption('parallel'));
128
        }
129
130
        return $config;
131
    }
132
133
    /**
134
     * Run the actual analysis.
135
     *
136
     * @param InputInterface $input Input.
137
     * @param OutputInterface $output Output.
138
     * @param Config $config The configuration object.
139
     */
140
    private function analyze(InputInterface $input, OutputInterface $output, Config $config): ResultAccumulator
141
    {
142
        $filesFinder = (new FileFinder($config->getFileExtensions(), $config->getFilesToIgnore()))
143
            ->getPhpFiles($config->getDirectoriesToScan());
144
        $accumulator = new ResultAccumulator($config->getFilesToShow(), $config->getMinScoreToShow());
145
        $this->processHandlerFactory->getProcessHandler($config)->process(
146
            $filesFinder,
147
            new ProcessFactory($config->getVCS(), $config->getCommitsSince()),
148
            $this->getOnSuccessObserver($input, $output, $accumulator)
149
        );
150
151
        return $accumulator;
152
    }
153
154
    /**
155
     * @param InputInterface $input Input.
156
     * @param OutputInterface $output Output.
157
     * @param ResultAccumulator $accumulator The object accumulating the results.
158
     */
159
    private function getOnSuccessObserver(
160
        InputInterface $input,
161
        OutputInterface $output,
162
        ResultAccumulator $accumulator
163
    ): OnSuccess {
164
        $observer = new OnSuccessAccumulate($accumulator);
165
166
        if ((bool) $input->getOption('progress')) {
167
            $progressBar = new ProgressBar($output);
168
            $progressBar->start();
169
            $observer = new OnSuccessCollection($observer, new OnSuccessProgress($progressBar));
170
        }
171
172
        return $observer;
173
    }
174
175
    /**
176
     * @param InputInterface $input Input.
177
     * @param OutputInterface $output Output.
178
     */
179
    private function displayLogo(InputInterface $input, OutputInterface $output): void
180
    {
181
        if ('text' !== $input->getOption('format') && empty($input->getOption('output'))) {
182
            return;
183
        }
184
185
        $output->writeln(self::LOGO);
186
    }
187
188
    /**
189
     * @param InputInterface $input Input.
190
     * @param OutputInterface $output Output.
191
     * @param ResultAccumulator $accumulator The results to write.
192
     */
193
    private function writeResult(InputInterface $input, OutputInterface $output, ResultAccumulator $accumulator): void
194
    {
195
        if ((bool) $input->getOption('progress')) {
196
            $output->writeln("\n");
197
        }
198
199
        if (!empty($input->getOption('output'))) {
200
            $output = new StreamOutput(
201
                \fopen((string) $input->getOption('output'), 'w+'),
202
                OutputInterface::VERBOSITY_NORMAL,
203
                false
204
            );
205
        }
206
207
        $renderer = $this->renderFactory->getRenderer($input->getOption('format'));
208
        $renderer->render($output, $accumulator->toArray());
209
    }
210
}
211