Passed
Push — master ( 6ae08c...b00ef4 )
by Fabien
02:00
created

RunCommand::execute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 15
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 19
rs 9.7666
1
<?php declare(strict_types = 1);
2
3
namespace Churn\Command;
4
5
use Churn\Configuration\Config;
6
use Churn\Factories\ResultsRendererFactory;
7
use Churn\Logic\ResultsLogic;
8
use Churn\Managers\FileManager;
9
use Churn\Process\Observer\OnSuccess;
10
use Churn\Process\Observer\OnSuccessNull;
11
use Churn\Process\Observer\OnSuccessProgress;
12
use Churn\Process\ProcessFactory;
13
use Churn\Process\ProcessHandlerFactory;
14
use Churn\Results\ResultCollection;
15
use function count;
16
use function file_get_contents;
17
use function fopen;
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 Symfony\Component\Yaml\Yaml;
27
28
/**
29
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
30
 */
31
class RunCommand extends Command
32
{
33
    public const LOGO ="
34
    ___  _   _  __  __  ____  _  _     ____  _   _  ____
35
   / __)( )_( )(  )(  )(  _ \( \( )___(  _ \( )_( )(  _ \
36
  ( (__  ) _ (  )(__)(  )   / )  ((___))___/ ) _ (  )___/
37
   \___)(_) (_)(______)(_)\_)(_)\_)   (__)  (_) (_)(__)
38
";
39
40
    /**
41
     * The results logic.
42
     * @var ResultsLogic
43
     */
44
    private $resultsLogic;
45
46
    /**
47
     * The process handler factory.
48
     * @var ProcessHandlerFactory
49
     */
50
    private $processHandlerFactory;
51
52
    /**
53
     * The renderer factory.
54
     * @var ResultsRendererFactory
55
     */
56
    private $renderFactory;
57
58
    /**
59
     * ChurnCommand constructor.
60
     * @param ResultsLogic           $resultsLogic          The results logic.
61
     * @param ProcessHandlerFactory  $processHandlerFactory The process handler factory.
62
     * @param ResultsRendererFactory $renderFactory         The Results Renderer Factory.
63
     */
64
    public function __construct(
65
        ResultsLogic $resultsLogic,
66
        ProcessHandlerFactory $processHandlerFactory,
67
        ResultsRendererFactory $renderFactory
68
    ) {
69
        parent::__construct();
70
        $this->resultsLogic = $resultsLogic;
71
        $this->processHandlerFactory = $processHandlerFactory;
72
        $this->renderFactory = $renderFactory;
73
    }
74
75
    /**
76
     * Configure the command
77
     * @return void
78
     */
79
    protected function configure(): void
80
    {
81
        $this->setName('run')
82
            ->addArgument('paths', InputArgument::IS_ARRAY, 'Path to source to check.')
83
            ->addOption('configuration', 'c', InputOption::VALUE_OPTIONAL, 'Path to the configuration file', 'churn.yml')  // @codingStandardsIgnoreLine
84
            ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format to use', 'text')
85
            ->addOption('output', 'o', InputOption::VALUE_REQUIRED, 'The path where to write the result')
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
     * @param InputInterface  $input  Input.
94
     * @param OutputInterface $output Output.
95
     * @return integer
96
     */
97
    protected function execute(InputInterface $input, OutputInterface $output): int
98
    {
99
        $this->displayLogo($input, $output);
100
        $content = (string) @file_get_contents($input->getOption('configuration'));
0 ignored issues
show
Bug introduced by
It seems like $input->getOption('configuration') can also be of type string[]; however, parameter $filename of file_get_contents() does only seem to accept string, 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

100
        $content = (string) @file_get_contents(/** @scrutinizer ignore-type */ $input->getOption('configuration'));
Loading history...
101
        $config = Config::create(Yaml::parse($content) ?? []);
102
        $filesCollection = (new FileManager($config->getFileExtensions(), $config->getFilesToIgnore()))
103
            ->getPhpFiles($this->getDirectoriesToScan($input, $config->getDirectoriesToScan()));
104
        $completedProcesses = $this->processHandlerFactory->getProcessHandler($config)->process(
105
            $filesCollection,
106
            new ProcessFactory($config->getCommitsSince()),
107
            $this->getOnSuccessObserver($input, $output, $filesCollection->count())
108
        );
109
        $resultCollection = $this->resultsLogic->process(
110
            $completedProcesses,
111
            $config->getMinScoreToShow(),
112
            $config->getFilesToShow()
113
        );
114
        $this->writeResult($input, $output, $resultCollection);
115
        return 0;
116
    }
117
118
    /**
119
     * Get the directories to scan.
120
     * @param InputInterface $input          Input Interface.
121
     * @param array          $dirsConfigured The directories configured to scan.
122
     * @throws InvalidArgumentException If paths argument invalid.
123
     * @return array When no directories to scan found.
124
     */
125
    private function getDirectoriesToScan(InputInterface $input, array $dirsConfigured): array
126
    {
127
        $dirsProvidedAsArgs = $input->getArgument('paths');
128
        if (count($dirsProvidedAsArgs) > 0) {
0 ignored issues
show
Bug introduced by
It seems like $dirsProvidedAsArgs can also be of type string; however, parameter $var of count() does only seem to accept Countable|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

128
        if (count(/** @scrutinizer ignore-type */ $dirsProvidedAsArgs) > 0) {
Loading history...
129
            return $dirsProvidedAsArgs;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $dirsProvidedAsArgs could return the type null|string which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
130
        }
131
132
        if (count($dirsConfigured) > 0) {
133
            return $dirsConfigured;
134
        }
135
136
        throw new InvalidArgumentException(
137
            'Provide the directories you want to scan as arguments, ' .
138
            'or configure them under "directoriesToScan" in your churn.yml file.'
139
        );
140
    }
141
142
    /**
143
     * @param InputInterface  $input      Input.
144
     * @param OutputInterface $output     Output.
145
     * @param integer         $totalFiles Total number of files to process.
146
     * @return OnSuccess
147
     */
148
    private function getOnSuccessObserver(InputInterface $input, OutputInterface $output, int $totalFiles): OnSuccess
149
    {
150
        if ((bool)$input->getOption('progress')) {
151
            $progressBar = new ProgressBar($output, $totalFiles);
152
            $progressBar->start();
153
            return new OnSuccessProgress($progressBar);
154
        }
155
156
        return new OnSuccessNull();
157
    }
158
159
    /**
160
     * @param InputInterface  $input  Input.
161
     * @param OutputInterface $output Output.
162
     * @return void
163
     */
164
    private function displayLogo(InputInterface $input, OutputInterface $output): void
165
    {
166
        if ($input->getOption('format') !== 'text' && empty($input->getOption('output'))) {
167
            return;
168
        }
169
170
        $output->writeln(self::LOGO);
171
    }
172
173
    /**
174
     * @param InputInterface   $input            Input.
175
     * @param OutputInterface  $output           Output.
176
     * @param ResultCollection $resultCollection The result to write.
177
     * @return void
178
     */
179
    private function writeResult(InputInterface $input, OutputInterface $output, ResultCollection $resultCollection): void
180
    {
181
        if ((bool)$input->getOption('progress')) {
182
            $output->writeln("\n");
183
        }
184
        if (!empty($input->getOption('output'))) {
185
            $output = new StreamOutput(fopen($input->getOption('output'), 'w+'), OutputInterface::VERBOSITY_NORMAL, false);
0 ignored issues
show
Bug introduced by
It seems like fopen($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

185
            $output = new StreamOutput(/** @scrutinizer ignore-type */ fopen($input->getOption('output'), 'w+'), OutputInterface::VERBOSITY_NORMAL, false);
Loading history...
Bug introduced by
It seems like $input->getOption('output') can also be of type string[]; however, parameter $filename of fopen() does only seem to accept string, 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

185
            $output = new StreamOutput(fopen(/** @scrutinizer ignore-type */ $input->getOption('output'), 'w+'), OutputInterface::VERBOSITY_NORMAL, false);
Loading history...
186
        }
187
188
        $renderer = $this->renderFactory->getRenderer($input->getOption('format'));
189
        $renderer->render($output, $resultCollection);
190
    }
191
}
192