Passed
Push — master ( d95c99...2b25c4 )
by Fabien
01:51
created

RunCommand::getConfiguration()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 15
rs 10
cc 3
nc 4
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Churn\Command;
6
7
use Churn\Configuration\Config;
8
use Churn\File\FileFinder;
9
use Churn\Process\Observer\OnSuccess;
10
use Churn\Process\Observer\OnSuccessAccumulate;
11
use Churn\Process\Observer\OnSuccessCollection;
12
use Churn\Process\Observer\OnSuccessProgress;
13
use Churn\Process\ProcessFactory;
14
use Churn\Process\ProcessHandlerFactory;
15
use Churn\Result\ResultAccumulator;
16
use Churn\Result\ResultsRendererFactory;
17
use InvalidArgumentException;
18
use Symfony\Component\Console\Command\Command;
19
use Symfony\Component\Console\Helper\ProgressBar;
20
use Symfony\Component\Console\Input\InputArgument;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Input\InputOption;
23
use Symfony\Component\Console\Output\OutputInterface;
24
use Symfony\Component\Console\Output\StreamOutput;
25
use Symfony\Component\Yaml\Yaml;
26
27
/** @SuppressWarnings(PHPMD.CouplingBetweenObjects) */
28
class RunCommand extends Command
29
{
30
31
    public const LOGO = "
32
    ___  _   _  __  __  ____  _  _     ____  _   _  ____
33
   / __)( )_( )(  )(  )(  _ \( \( )___(  _ \( )_( )(  _ \
34
  ( (__  ) _ (  )(__)(  )   / )  ((___))___/ ) _ (  )___/
35
   \___)(_) (_)(______)(_)\_)(_)\_)   (__)  (_) (_)(__)
36
";
37
38
    /**
39
     * The process handler factory.
40
     *
41
     * @var ProcessHandlerFactory
42
     */
43
    private $processHandlerFactory;
44
45
    /**
46
     * The renderer factory.
47
     *
48
     * @var ResultsRendererFactory
49
     */
50
    private $renderFactory;
51
52
    /**
53
     * ChurnCommand constructor.
54
     *
55
     * @param ProcessHandlerFactory $processHandlerFactory The process handler factory.
56
     * @param ResultsRendererFactory $renderFactory The Results Renderer Factory.
57
     */
58
    public function __construct(ProcessHandlerFactory $processHandlerFactory, ResultsRendererFactory $renderFactory)
59
    {
60
        parent::__construct();
61
62
        $this->processHandlerFactory = $processHandlerFactory;
63
        $this->renderFactory = $renderFactory;
64
    }
65
66
    /**
67
     * Returns a new instance of the command.
68
     */
69
    public static function newInstance(): self
70
    {
71
        return new self(new ProcessHandlerFactory(), new ResultsRendererFactory());
72
    }
73
74
    /**
75
     * Configure the command
76
     */
77
    protected function configure(): void
78
    {
79
        $this->setName('run')
80
            ->addArgument('paths', InputArgument::IS_ARRAY, 'Path to source to check.')
81
            ->addOption('configuration', 'c', InputOption::VALUE_OPTIONAL, 'Path to the configuration file', 'churn.yml')  // @codingStandardsIgnoreLine
82
            ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format to use', 'text')
83
            ->addOption('output', 'o', InputOption::VALUE_REQUIRED, 'The path where to write the result')
84
            ->addOption('progress', 'p', InputOption::VALUE_NONE, 'Show progress bar')
85
            ->setDescription('Check files')
86
            ->setHelp('Checks the churn on the provided path argument(s).');
87
    }
88
89
    /**
90
     * Execute the command
91
     *
92
     * @param InputInterface $input Input.
93
     * @param OutputInterface $output Output.
94
     */
95
    protected function execute(InputInterface $input, OutputInterface $output): int
96
    {
97
        $this->displayLogo($input, $output);
98
        $config = $this->getConfiguration($input);
99
        $filesFinder = (new FileFinder($config->getFileExtensions(), $config->getFilesToIgnore()))
100
            ->getPhpFiles($this->getDirectoriesToScan($input, $config->getDirectoriesToScan()));
101
        $accumulator = new ResultAccumulator($config->getFilesToShow(), $config->getMinScoreToShow());
102
        $this->processHandlerFactory->getProcessHandler($config)->process(
103
            $filesFinder,
104
            new ProcessFactory($config->getVCS(), $config->getCommitsSince()),
105
            $this->getOnSuccessObserver($input, $output, $accumulator)
106
        );
107
        $this->writeResult($input, $output, $accumulator);
108
109
        return 0;
110
    }
111
112
    /**
113
     * @param InputInterface $input Input.
114
     * @throws InvalidArgumentException If the configuration file cannot be read.
115
     */
116
    private function getConfiguration(InputInterface $input): Config
117
    {
118
        $confPath = $originalConfPath = (string) $input->getOption('configuration');
119
120
        if (\is_dir($confPath)) {
121
            $confPath = \rtrim($confPath, '/\\') . '/churn.yml';
122
        }
123
124
        if (!\is_readable($confPath)) {
125
            throw new InvalidArgumentException('The configuration file can not be read at ' . $originalConfPath);
126
        }
127
128
        $content = (string) \file_get_contents($confPath);
129
130
        return Config::create(Yaml::parse($content) ?? []);
131
    }
132
133
    /**
134
     * Get the directories to scan.
135
     *
136
     * @param InputInterface $input Input Interface.
137
     * @param array<string> $dirsConfigured The directories configured to scan.
138
     * @throws InvalidArgumentException If paths argument invalid.
139
     * @return array<string> When no directories to scan found.
140
     */
141
    private function getDirectoriesToScan(InputInterface $input, array $dirsConfigured): array
142
    {
143
        $dirsProvidedAsArgs = (array) $input->getArgument('paths');
144
145
        if ([] !== $dirsProvidedAsArgs) {
146
            return $dirsProvidedAsArgs;
147
        }
148
149
        if ([] !== $dirsConfigured) {
150
            return $dirsConfigured;
151
        }
152
153
        throw new InvalidArgumentException(
154
            'Provide the directories you want to scan as arguments, ' .
155
            'or configure them under "directoriesToScan" in your churn.yml file.'
156
        );
157
    }
158
159
    /**
160
     * @param InputInterface $input Input.
161
     * @param OutputInterface $output Output.
162
     * @param ResultAccumulator $accumulator The object accumulating the results.
163
     */
164
    private function getOnSuccessObserver(
165
        InputInterface $input,
166
        OutputInterface $output,
167
        ResultAccumulator $accumulator
168
    ): OnSuccess {
169
        $observer = new OnSuccessAccumulate($accumulator);
170
171
        if ((bool) $input->getOption('progress')) {
172
            $progressBar = new ProgressBar($output);
173
            $progressBar->start();
174
            $observer = new OnSuccessCollection($observer, new OnSuccessProgress($progressBar));
175
        }
176
177
        return $observer;
178
    }
179
180
    /**
181
     * @param InputInterface $input Input.
182
     * @param OutputInterface $output Output.
183
     */
184
    private function displayLogo(InputInterface $input, OutputInterface $output): void
185
    {
186
        if ('text' !== $input->getOption('format') && empty($input->getOption('output'))) {
187
            return;
188
        }
189
190
        $output->writeln(self::LOGO);
191
    }
192
193
    /**
194
     * @param InputInterface $input Input.
195
     * @param OutputInterface $output Output.
196
     * @param ResultAccumulator $accumulator The results to write.
197
     */
198
    private function writeResult(InputInterface $input, OutputInterface $output, ResultAccumulator $accumulator): void
199
    {
200
        if ((bool) $input->getOption('progress')) {
201
            $output->writeln("\n");
202
        }
203
204
        if (!empty($input->getOption('output'))) {
205
            $output = new StreamOutput(
206
                \fopen((string) $input->getOption('output'), 'w+'),
207
                OutputInterface::VERBOSITY_NORMAL,
208
                false
209
            );
210
        }
211
212
        $renderer = $this->renderFactory->getRenderer($input->getOption('format'));
213
        $renderer->render($output, $accumulator->toArray());
214
    }
215
}
216