Completed
Pull Request — master (#122)
by Matthias
01:39
created

ChurnCommand::displayResultsText()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.3142
c 0
b 0
f 0
cc 1
eloc 12
nc 1
nop 2
1
<?php declare(strict_types = 1);
2
3
namespace Churn\Commands;
4
5
use Churn\Collections\FileCollection;
6
use Churn\Results\Result;
7
use Churn\Results\ResultCollection;
8
use Illuminate\Support\Collection;
9
use Churn\Factories\ProcessFactory;
10
use Churn\Managers\FileManager;
11
use Churn\Results\ResultsParser;
12
use Symfony\Component\Console\Command\Command;
13
use Symfony\Component\Console\Helper\Table;
14
use Symfony\Component\Console\Input\InputArgument;
15
use Symfony\Component\Console\Input\InputInterface;
16
use Symfony\Component\Console\Input\InputOption;
17
use Symfony\Component\Console\Output\OutputInterface;
18
use Churn\Configuration\Config;
19
use Symfony\Component\Yaml\Yaml;
20
21
class ChurnCommand extends Command
22
{
23
    const FORMAT_JSON = 'json';
24
    const FORMAT_TEXT = 'text';
25
26
    /**
27
     * The config values.
28
     * @var Config
29
     */
30
    private $config;
31
32
    /**
33
     * The process factory.
34
     * @var ProcessFactory
35
     */
36
    private $processFactory;
37
38
    /**
39
     * Th results parser.
40
     * @var ResultsParser
41
     */
42
    private $resultsParser;
43
44
    /**
45
     * Collection of files to run the processes on.
46
     * @var FileCollection
47
     */
48
    private $filesCollection;
49
50
    /**
51
     * Collection of processes currently running.
52
     * @var Collection
53
     */
54
    private $runningProcesses;
55
56
    /**
57
     * Array of completed processes.
58
     * @var array
59
     */
60
    private $completedProcessesArray;
61
62
    /**
63
     * The start time.
64
     * @var float
65
     */
66
    private $startTime;
67
68
    /**
69
     * Keeps track of how many files were processed.
70
     * @var integer
71
     */
72
    private $filesCount;
73
74
    /**
75
     * ChurnCommand constructor.
76
     */
77
    public function __construct()
78
    {
79
        parent::__construct();
80
81
        $this->resultsParser = new ResultsParser();
82
    }
83
84
    /**
85
     * Configure the command
86
     * @return void
87
     */
88
    protected function configure()
89
    {
90
        $this->setName('run')
91
            ->addArgument('paths', InputArgument::IS_ARRAY, 'Path to source to check.')
92
            ->addOption('configuration', 'c', InputOption::VALUE_OPTIONAL, 'Path to the configuration file', 'churn.yml')  // @codingStandardsIgnoreLine
93
            ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format to use', 'text')
94
            ->setDescription('Check files')
95
            ->setHelp('Checks the churn on the provided path argument(s).');
96
    }
97
98
    /**
99
     * Execute the command
100
     * @param  InputInterface  $input  Input.
101
     * @param  OutputInterface $output Output.
102
     * @return void
103
     */
104
    protected function execute(InputInterface $input, OutputInterface $output)
105
    {
106
        $this->startTime = microtime(true);
107
        $this->setupProcessor($input->getOption('configuration'));
108
109
        $this->filesCollection = $this->getPhpFiles($input->getArgument('paths'));
110
        $this->filesCount = $this->filesCollection->count();
111
        $this->runningProcesses = new Collection;
112
        $this->completedProcessesArray = [];
113
        while ($this->filesCollection->hasFiles() || $this->runningProcesses->count()) {
114
            $this->getProcessResults();
115
        }
116
        $completedProcesses = new Collection($this->completedProcessesArray);
117
118
        $results = $this->resultsParser
119
            ->parse($completedProcesses)
120
            ->normalizeAgainst($this->config);
121
        $this->displayResults($input->getOption('format'), $output, $results);
122
    }
123
124
    /**
125
     * Gets the output from processes and stores them in the completedProcessArray member.
126
     * @return void
127
     */
128
    private function getProcessResults()
129
    {
130
        for ($index = $this->runningProcesses->count(); $this->filesCollection->hasFiles() > 0 && $index < $this->config->getParallelJobs(); $index++) {
131
            $file = $this->filesCollection->getNextFile();
132
133
            $process = $this->processFactory->createGitCommitProcess($file);
134
            $process->start();
135
            $this->runningProcesses->put($process->getKey(), $process);
136
137
            $process = $this->processFactory->createCyclomaticComplexityProcess($file);
138
            $process->start();
139
            $this->runningProcesses->put($process->getKey(), $process);
140
        }
141
142
        foreach ($this->runningProcesses as $file => $process) {
143
            if ($process->isSuccessful()) {
144
                $this->runningProcesses->forget($process->getKey());
145
                $this->completedProcessesArray[$process->getFileName()][$process->getType()] = $process;
146
            }
147
        }
148
    }
149
150
    /**
151
     * Displays the results in a table.
152
     * @param  OutputInterface  $output  Output.
153
     * @param  ResultCollection $results Results Collection.
154
     * @return void
155
     */
156
    protected function displayResults(string $format, OutputInterface $output, ResultCollection $results)
157
    {
158
        if ($format === self::FORMAT_JSON) {
159
            $this->displayResultsJson($output, $results);
160
        } elseif ($format === self::FORMAT_TEXT) {
161
            $this->displayResultsText($output, $results);
162
        } else {
163
            throw new \InvalidArgumentException('Invalid output format provided');
164
        }
165
    }
166
167
    /**
168
     * @param string $configFile Relative path churn.yml configuration file.
169
     * @return void
170
     */
171
    private function setupProcessor(string $configFile)
172
    {
173
        $this->config = Config::create(Yaml::parse(@file_get_contents($configFile)) ?? []);
174
        $this->processFactory = new ProcessFactory($this->config);
175
    }
176
177
    /**
178
     * @param array $directory Directories.
179
     * @return FileCollection
180
     */
181
    private function getPhpFiles(array $directory): FileCollection
182
    {
183
        $fileManager = new FileManager($this->config->getFileExtensions(), $this->config->getFilesToIgnore());
184
185
        return $fileManager->getPhpFiles($directory);
186
    }
187
188
    /**
189
     * @param OutputInterface $output
190
     * @param ResultCollection $results
191
     */
192
    private function displayResultsText(OutputInterface $output, ResultCollection $results)
193
    {
194
        $totalTime = microtime(true) - $this->startTime;
195
        echo "\n
196
    ___  _   _  __  __  ____  _  _     ____  _   _  ____
197
   / __)( )_( )(  )(  )(  _ \( \( )___(  _ \( )_( )(  _ \
198
  ( (__  ) _ (  )(__)(  )   / )  ((___))___/ ) _ (  )___/
199
   \___)(_) (_)(______)(_)\_)(_)\_)   (__)  (_) (_)(__)      https://github.com/bmitch/churn-php\n\n";
200
201
        $table = new Table($output);
202
        $table->setHeaders(['File', 'Times Changed', 'Complexity', 'Score']);
203
        $table->addRows($results->toArray());
204
205
        $table->render();
206
207
        echo "  "
208
            . $this->filesCount
209
            . " files analysed in {$totalTime} seconds using "
210
            . $this->config->getParallelJobs()
211
            . " parallel jobs.\n\n";
212
    }
213
214
    private function displayResultsJson(OutputInterface $output, ResultCollection $results)
215
    {
216
        $data = array_map(function(array $result) {
217
            return [
218
                'file' => $result[0],
219
                'commits' => $result[1],
220
                'complexity' => $result[2],
221
                'score' => $result[3]
222
            ];
223
        }, $results->toArray());
224
225
        $output->write(json_encode($data));
226
    }
227
}
228