Completed
Pull Request — master (#51)
by Povilas
02:04
created

Command::execute()   D

Complexity

Conditions 11
Paths 177

Size

Total Lines 49
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 49
rs 4.9629
cc 11
eloc 30
nc 177
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Povils\PHPMND\Console;
4
5
use Povils\PHPMND\Detector;
6
use Povils\PHPMND\ExtensionResolver;
7
use Povils\PHPMND\FileReportList;
8
use Povils\PHPMND\HintList;
9
use Povils\PHPMND\PHPFinder;
10
use Povils\PHPMND\Printer;
11
use Symfony\Component\Console\Command\Command as BaseCommand;
12
use Symfony\Component\Console\Helper\ProgressBar;
13
use Symfony\Component\Console\Input\InputArgument;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Console\Output\OutputInterface;
17
18
/**
19
 * Class Command
20
 *
21
 * @package Povils\PHPMND\Console
22
 */
23
class Command extends BaseCommand
24
{
25
    /**
26
     * @inheritdoc
27
     */
28
    protected function configure()
29
    {
30
        $this
31
            ->setName('phpmnd')
32
            ->setDefinition(
33
                [
34
                    new InputArgument(
35
                        'directory',
36
                        InputArgument::REQUIRED,
37
                        'Directory to analyze'
38
                    )
39
                ]
40
            )
41
            ->addOption(
42
                'extensions',
43
                null,
44
                InputOption::VALUE_REQUIRED,
45
                'A comma-separated list of extensions',
46
                []
47
            )
48
            ->addOption(
49
                'ignore-numbers',
50
                null,
51
                InputOption::VALUE_REQUIRED,
52
                'A comma-separated list of numbers to ignore',
53
                [0, 1]
54
            )
55
            ->addOption(
56
                'ignore-funcs',
57
                null,
58
                InputOption::VALUE_REQUIRED,
59
                'A comma-separated list of functions to ignore when using "argument" extension',
60
                []
61
            )
62
            ->addOption(
63
                'exclude',
64
                null,
65
                InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
66
                'Exclude a directory from code analysis (must be relative to source)'
67
            )
68
            ->addOption(
69
                'exclude-path',
70
                null,
71
                InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
72
                'Exclude a path from code analysis (must be relative to source)'
73
            )
74
            ->addOption(
75
                'exclude-file',
76
                null,
77
                InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
78
                'Exclude a file from code analysis (must be relative to source)'
79
            )
80
            ->addOption(
81
                'suffixes',
82
                null,
83
                InputOption::VALUE_REQUIRED,
84
                'Comma-separated string of valid source code filename extensions',
85
                'php'
86
            )
87
            ->addOption(
88
                'progress',
89
                null,
90
                InputOption::VALUE_NONE,
91
                'Show progress bar'
92
            )
93
            ->addOption(
94
                'hint',
95
                null,
96
                InputOption::VALUE_NONE,
97
                'Suggest replacements for magic numbers'
98
            )
99
            ->addOption(
100
                'non-zero-exit-on-violation',
101
                null,
102
                InputOption::VALUE_NONE,
103
                'Return a non zero exit code when there are magic numbers'
104
            )
105
            ->addOption(
106
                'strings',
107
                null,
108
                InputOption::VALUE_NONE,
109
                'Include strings literal search in code analysis'
110
            )
111
            ->addOption(
112
                'ignore-strings',
113
                null,
114
                InputOption::VALUE_REQUIRED,
115
                'A comma-separated list of strings to ignore when using "strings" option',
116
                []
117
            );
118
    }
119
120
    /**
121
     * @inheritdoc
122
     */
123
    protected function execute(InputInterface $input, OutputInterface $output)
124
    {
125
        $finder = $this->createFinder($input);
126
127
        if (0 === $finder->count()) {
128
            $output->writeln('No files found to scan');
129
            exit(0);
130
        }
131
132
        $progressBar = null;
133
        if ($input->getOption('progress')) {
134
            $progressBar = new ProgressBar($output, $finder->count());
135
            $progressBar->start();
136
        }
137
138
        $hintList = new HintList;
139
        $detector = new Detector($this->createOption($input), $hintList);
140
141
        $fileReportList = new FileReportList();
142
        $printer = new Printer();
143
        foreach ($finder as $file) {
144
            try {
145
                $fileReport = $detector->detect($file);
146
                if ($fileReport->hasMagicNumbers()) {
147
                    $fileReportList->addFileReport($fileReport);
148
                }
149
            } catch (\Exception $e) {
150
                $output->writeln($e->getMessage());
151
            }
152
153
            if ($input->getOption('progress')) {
154
                $progressBar->advance();
155
            }
156
        }
157
158
        if ($input->getOption('progress')) {
159
            $progressBar->finish();
160
        }
161
162
        if ($output->getVerbosity() !== OutputInterface::VERBOSITY_QUIET) {
163
            $output->writeln('');
164
            $printer->printData($output, $fileReportList, $hintList);
165
            $output->writeln('<info>' . \PHP_Timer::resourceUsage() . '</info>');
166
        }
167
168
        if ($input->getOption('non-zero-exit-on-violation') && $fileReportList->hasMagicNumbers()) {
169
            exit(1);
170
        }
171
    }
172
173
    /**
174
     * @param InputInterface $input
175
     * @return Option
176
     * @throws \Exception
177
     */
178
    private function createOption(InputInterface $input)
179
    {
180
        $option = new Option;
181
        $option->setIgnoreNumbers(array_map([$this, 'castToNumber'], $this->getCSVOption($input, 'ignore-numbers')));
182
        $option->setIgnoreFuncs($this->getCSVOption($input, 'ignore-funcs'));
183
        $option->setIncludeStrings($input->getOption('strings'));
184
        $option->setIgnoreStrings($input->getOption('ignore-strings'));
185
        $option->setGiveHint($input->getOption('hint'));
186
        $option->setExtensions(
187
            (new ExtensionResolver())->resolve($this->getCSVOption($input, 'extensions'))
188
        );
189
190
        return $option;
191
    }
192
193
    /**
194
     * @param InputInterface $input
195
     * @param string $option
196
     *
197
     * @return array
198
     */
199
    private function getCSVOption(InputInterface $input, $option)
200
    {
201
        $result = $input->getOption($option);
202
        if (false === is_array($result)) {
203
            return explode(',', $result);
204
        }
205
206
        return $result;
207
    }
208
209
    /**
210
     * @param InputInterface $input
211
     *
212
     * @return PHPFinder
213
     */
214
    protected function createFinder(InputInterface $input)
215
    {
216
        return new PHPFinder(
217
            $input->getArgument('directory'),
218
            $input->getOption('exclude'),
219
            $input->getOption('exclude-path'),
220
            $input->getOption('exclude-file'),
221
            $this->getCSVOption($input, 'suffixes')
222
        );
223
    }
224
225
    /**
226
     * @param string $value
227
     *
228
     * @return int|float|string
229
     */
230
    private function castToNumber($value)
231
    {
232
        if (is_numeric($value)) {
233
            $value += 0; // '2' -> (int) 2, '2.' -> (float) 2.0
234
        }
235
236
        return $value;
237
    }
238
}
239