Passed
Pull Request — master (#281)
by Fabien
02:28 queued 13s
created

RunCommand::getConfiguration()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 11
rs 10
cc 2
nc 2
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
     * Configure the command
68
     */
69
    protected function configure(): void
70
    {
71
        $this->setName('run')
72
            ->addArgument('paths', InputArgument::IS_ARRAY, 'Path to source to check.')
73
            ->addOption('configuration', 'c', InputOption::VALUE_OPTIONAL, 'Path to the configuration file', 'churn.yml')  // @codingStandardsIgnoreLine
74
            ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format to use', 'text')
75
            ->addOption('output', 'o', InputOption::VALUE_REQUIRED, 'The path where to write the result')
76
            ->addOption('progress', 'p', InputOption::VALUE_NONE, 'Show progress bar')
77
            ->setDescription('Check files')
78
            ->setHelp('Checks the churn on the provided path argument(s).');
79
    }
80
81
    /**
82
     * Execute the command
83
     *
84
     * @param InputInterface $input Input.
85
     * @param OutputInterface $output Output.
86
     */
87
    protected function execute(InputInterface $input, OutputInterface $output): int
88
    {
89
        $this->displayLogo($input, $output);
90
        $config = $this->getConfiguration($input);
91
        $filesFinder = (new FileFinder($config->getFileExtensions(), $config->getFilesToIgnore()))
92
            ->getPhpFiles($this->getDirectoriesToScan($input, $config->getDirectoriesToScan()));
93
        $accumulator = new ResultAccumulator($config->getFilesToShow(), $config->getMinScoreToShow());
94
        $this->processHandlerFactory->getProcessHandler($config)->process(
95
            $filesFinder,
96
            new ProcessFactory($config->getVCS(), $config->getCommitsSince()),
97
            $this->getOnSuccessObserver($input, $output, $accumulator)
98
        );
99
        $this->writeResult($input, $output, $accumulator);
100
101
        return 0;
102
    }
103
104
    /**
105
     * @param InputInterface $input Input.
106
     * @throws InvalidArgumentException If the configuration file cannot be read.
107
     */
108
    private function getConfiguration(InputInterface $input): Config
109
    {
110
        $confPath = (string) $input->getOption('configuration');
111
112
        if (!\is_readable($confPath)) {
113
            throw new InvalidArgumentException('The configuration file can not be read at ' . $confPath);
114
        }
115
116
        $content = (string) \file_get_contents($confPath);
117
118
        return Config::create(Yaml::parse($content) ?? []);
119
    }
120
121
    /**
122
     * Get the directories to scan.
123
     *
124
     * @param InputInterface $input Input Interface.
125
     * @param array<string> $dirsConfigured The directories configured to scan.
126
     * @throws InvalidArgumentException If paths argument invalid.
127
     * @return array<string> When no directories to scan found.
128
     */
129
    private function getDirectoriesToScan(InputInterface $input, array $dirsConfigured): array
130
    {
131
        $dirsProvidedAsArgs = (array) $input->getArgument('paths');
132
133
        if ([] !== $dirsProvidedAsArgs) {
134
            return $dirsProvidedAsArgs;
135
        }
136
137
        if ([] !== $dirsConfigured) {
138
            return $dirsConfigured;
139
        }
140
141
        throw new InvalidArgumentException(
142
            'Provide the directories you want to scan as arguments, ' .
143
            'or configure them under "directoriesToScan" in your churn.yml file.'
144
        );
145
    }
146
147
    /**
148
     * @param InputInterface $input Input.
149
     * @param OutputInterface $output Output.
150
     * @param ResultAccumulator $accumulator The object accumulating the results.
151
     */
152
    private function getOnSuccessObserver(
153
        InputInterface $input,
154
        OutputInterface $output,
155
        ResultAccumulator $accumulator
156
    ): OnSuccess {
157
        $observer = new OnSuccessAccumulate($accumulator);
158
159
        if ((bool) $input->getOption('progress')) {
160
            $progressBar = new ProgressBar($output);
161
            $progressBar->start();
162
            $observer = new OnSuccessCollection($observer, new OnSuccessProgress($progressBar));
163
        }
164
165
        return $observer;
166
    }
167
168
    /**
169
     * @param InputInterface $input Input.
170
     * @param OutputInterface $output Output.
171
     */
172
    private function displayLogo(InputInterface $input, OutputInterface $output): void
173
    {
174
        if ('text' !== $input->getOption('format') && empty($input->getOption('output'))) {
175
            return;
176
        }
177
178
        $output->writeln(self::LOGO);
179
    }
180
181
    /**
182
     * @param InputInterface $input Input.
183
     * @param OutputInterface $output Output.
184
     * @param ResultAccumulator $accumulator The results to write.
185
     */
186
    private function writeResult(InputInterface $input, OutputInterface $output, ResultAccumulator $accumulator): void
187
    {
188
        if ((bool) $input->getOption('progress')) {
189
            $output->writeln("\n");
190
        }
191
192
        if (!empty($input->getOption('output'))) {
193
            $output = new StreamOutput(
194
                \fopen((string) $input->getOption('output'), 'w+'),
0 ignored issues
show
Bug introduced by
It seems like fopen((string)$input->getOption('output'), 'w+') can also be of type false; however, parameter $stream of Symfony\Component\Consol...amOutput::__construct() does only seem to accept resource, 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

194
                /** @scrutinizer ignore-type */ \fopen((string) $input->getOption('output'), 'w+'),
Loading history...
195
                OutputInterface::VERBOSITY_NORMAL,
196
                false
197
            );
198
        }
199
200
        $renderer = $this->renderFactory->getRenderer($input->getOption('format'));
201
        $renderer->render($output, $accumulator->toArray());
202
    }
203
}
204