Completed
Pull Request — master (#125)
by Matthias
01:41
created

ChurnCommand   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 12

Importance

Changes 0
Metric Value
wmc 16
lcom 2
cbo 12
dl 0
loc 192
rs 10
c 0
b 0
f 0

8 Methods

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