Command   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 298
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 6
dl 0
loc 298
rs 10
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
B run() 0 23 4
A getGitDiffFilter() 0 16 2
A getOutput() 0 18 2
B getArguments() 0 42 1
A getIgnoredPaths() 0 6 1
A setAnalysedPathsFromString() 0 6 1
A setAnalysedPaths() 0 10 3
A getAnalysedPaths() 0 4 1
A getWorkingDirectory() 0 4 1
A getOutputFormat() 0 4 1
A getDescription() 0 4 1
A getAnalyser() 0 12 2
A getOutputFormatClasses() 0 10 1
A getArgumentValue() 0 4 1
A hasArgumentValue() 0 4 1
1
<?php
2
namespace phphound;
3
4
use League\CLImate\CLImate;
5
use phphound\output\AbstractOutput;
6
use phphound\output\filter\DiffOutputFilter;
7
use ReflectionMethod;
8
use SebastianBergmann\Diff\Parser;
9
use SebastianBergmann\Git\Git;
10
use UnexpectedValueException;
11
12
/**
13
 * Command line tool that run all script analyzers.
14
 */
15
class Command
16
{
17
    /**
18
     * CLI tool.
19
     * @var CLImate CLImate instance.
20
     */
21
    protected $cli;
22
23
    /**
24
     * Analyser.
25
     * @var Analyser analyser instance.
26
     */
27
    protected $analyser;
28
29
    /**
30
     * Command line arguments.
31
     * @var array list of arguments.
32
     */
33
    protected $arguments;
34
35
    /**
36
     * Analysis targets paths.
37
     * @var array list of files and directories paths.
38
     */
39
    protected $analysedPaths;
40
41
    /**
42
     * Composer binaries directory path.
43
     * @var string directory path.
44
     */
45
    protected $binariesPath;
46
47
    /**
48
     * Set dependencies and initialize CLI.
49
     * @param CLImate $climate CLImate instance.
50
     * @param string $binariesPath Composer binaries path.
51
     * @param array $arguments command line arguments.
52
     */
53
    public function __construct(CLImate $climate, $binariesPath, array $arguments)
54
    {
55
        $this->cli = $climate;
56
        $this->cli->description($this->getDescription());
57
        $this->cli->arguments->add($this->getArguments());
58
        $this->cli->arguments->parse($arguments);
59
60
        $this->arguments = $arguments;
61
        $this->binariesPath = $binariesPath;
62
        $this->setAnalysedPathsFromString($this->getArgumentValue('path'));
63
    }
64
65
    /**
66
     * Run PHP-Hound command.
67
     * @return boolean true if it didn't find code issues or ran successfully.
68
     */
69
    public function run()
70
    {
71
        if ($this->hasArgumentValue('help')) {
72
            $this->cli->usage();
73
            return true;
74
        }
75
76
        if ($this->hasArgumentValue('version')) {
77
            $this->cli->out($this->getDescription());
78
            return true;
79
        }
80
81
        if ($this->hasArgumentValue('git-diff')) {
82
            $gitDiff = $this->getArgumentValue('git-diff');
83
            $filter = $this->getGitDiffFilter($gitDiff);
84
            $analysedFiles = $filter->getFilesWithAddedCode();
85
            $this->setAnalysedPaths($analysedFiles);
86
            $this->getAnalyser()->setResultsFilter($filter);
87
            $this->getAnalyser()->setAnalysedPaths($analysedFiles);
88
        }
89
90
        return $this->getAnalyser()->run();
91
    }
92
93
    /**
94
     * Create a DiffOutputFilter based on a git-diff param.
95
     * @param string $gitDiff git diff arguments.
96
     * @return DiffOutputFilter filter instance.
97
     */
98
    protected function getGitDiffFilter($gitDiff)
99
    {
100
        $analysedPaths = $this->getAnalysedPaths();
101
        $gitPath = array_shift($analysedPaths);
102
        if (!is_dir($gitPath)) {
103
            $gitPath = dirname($gitPath);
104
        }
105
        $git = new Git($gitPath);
106
        $executeMethod = new ReflectionMethod($git, 'execute');
107
        $executeMethod->setAccessible(true);
108
        $gitRoot = trim(implode("\n", $executeMethod->invoke($git, 'git rev-parse --show-toplevel')));
109
        list($base, $changed) = explode('..', $gitDiff);
110
        $diff = $git->getDiff($base, $changed);
111
        $diffParser = new Parser;
112
        return new DiffOutputFilter($gitRoot, $diffParser->parse($diff));
0 ignored issues
show
Documentation introduced by
$diffParser->parse($diff) is of type array<integer,object<Seb...ianBergmann\Diff\Diff>>, but the function expects a array<integer,object<php...ianBergmann\Diff\Diff>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
113
    }
114
115
    /**
116
     * Initialize output.
117
     * @throws UnexpectedValueException on invalid format value.
118
     * @return AbstractOutput
119
     */
120
    protected function getOutput()
121
    {
122
        $format = $this->getOutputFormat();
123
        $formatClasses = $this->getOutputFormatClasses();
124
125
        if (!isset($formatClasses[$format])) {
126
            throw new UnexpectedValueException(
127
                'Invalid format: "' . $format . '"'
128
            );
129
        }
130
131
        $outputClassName = $formatClasses[$format];
132
133
        return new $outputClassName(
134
            $this->cli,
135
            $this->getWorkingDirectory()
136
        );
137
    }
138
139
    /**
140
     * Command line arguments list for CLImate.
141
     * @return array CLI list of arguments.
142
     */
143
    protected function getArguments()
144
    {
145
        return [
146
            'help' => [
147
                'prefix' => 'h',
148
                'longPrefix' => 'help',
149
                'description' => 'Prints a usage statement',
150
                'noValue' => true,
151
            ],
152
            'version' => [
153
                'prefix' => 'v',
154
                'longPrefix' => 'version',
155
                'description' => 'Prints installed version',
156
                'noValue' => true,
157
            ],
158
            'ignore' => [
159
                'prefix' => 'i',
160
                'longPrefix' => 'ignore',
161
                'description' => 'Ignore a comma-separated list of directories',
162
                'castTo' => 'string',
163
                'defaultValue' => 'vendor,tests,features,spec',
164
            ],
165
            'format' => [
166
                'prefix' => 'f',
167
                'longPrefix' => 'format',
168
                'description' => 'Output format',
169
                'castTo' => 'string',
170
                'defaultValue' => 'text',
171
            ],
172
            'git-diff' => [
173
                'prefix' => 'g',
174
                'longPrefix' => 'git-diff',
175
                'description' => 'Limit to files and lines changed between two '
176
                                . 'commits or branches, e.g., "master..other".',
177
                'castTo' => 'string',
178
            ],
179
            'path' => [
180
                'description' => 'File or directory path to analyze',
181
                'defaultValue' => '.',
182
            ],
183
        ];
184
    }
185
186
    /**
187
     * Get a list of paths to be ignored by the analysis.
188
     * @return string[] a list of file and/or directory paths.
189
     */
190
    public function getIgnoredPaths()
191
    {
192
        $ignoredArgument = $this->getArgumentValue('ignore');
193
        $ignoredPaths = explode(',', $ignoredArgument);
194
        return array_filter($ignoredPaths);
195
    }
196
197
    /**
198
     * Parse a string of comma separated files and/or directories to be analysed.
199
     * @param string $pathsString the path argument value.
200
     * @return void
201
     */
202
    protected function setAnalysedPathsFromString($pathsString)
203
    {
204
        $rawAnalysedPaths = explode(',', $pathsString);
205
        $analysedPaths = array_filter($rawAnalysedPaths);
0 ignored issues
show
Unused Code introduced by
$analysedPaths is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
206
        $this->setAnalysedPaths(array_filter($rawAnalysedPaths));
207
    }
208
209
    /**
210
     * Set target files and/or directories to be analysed. Fix relative paths.
211
     * @param string[] $paths target paths.
212
     * @return void
213
     */
214
    protected function setAnalysedPaths(array $paths)
215
    {
216
        foreach ($paths as &$path) {
217
            if (0 === strpos($path, DIRECTORY_SEPARATOR)) {
218
                continue;
219
            }
220
            $path = $this->getWorkingDirectory() . DIRECTORY_SEPARATOR . $path;
221
        }
222
        $this->analysedPaths = $paths;
223
    }
224
225
    /**
226
     * Analysis target paths.
227
     * @return string[] a list of analysed paths (usually just one).
228
     */
229
    public function getAnalysedPaths()
230
    {
231
        return $this->analysedPaths;
232
    }
233
234
    /**
235
     * Running script path.
236
     * @return string current script directory.
237
     */
238
    public function getWorkingDirectory()
239
    {
240
        return getcwd();
241
    }
242
243
    /**
244
     * Output format.
245
     * @return string format type.
246
     */
247
    public function getOutputFormat()
248
    {
249
        return $this->getArgumentValue('format');
250
    }
251
252
    /**
253
     * CLI output description.
254
     * @return string description.
255
     */
256
    public function getDescription()
257
    {
258
        return 'PHP Hound ' . Analyser::VERSION;
259
    }
260
261
    /**
262
     * Analyser instance.
263
     * @return Analyser instance.
264
     */
265
    public function getAnalyser()
266
    {
267
        if (null === $this->analyser) {
268
            $this->analyser = new Analyser(
269
                $this->getOutput(),
270
                $this->binariesPath,
271
                $this->getAnalysedPaths(),
272
                $this->getIgnoredPaths()
273
            );
274
        }
275
        return $this->analyser;
276
    }
277
278
    /**
279
     * List of output format classes.
280
     * @return array array where the key is a format and its value the class.
281
     */
282
    protected function getOutputFormatClasses()
283
    {
284
        return [
285
            'text' => 'phphound\output\TextOutput',
286
            'json' => 'phphound\output\JsonOutput',
287
            'xml' => 'phphound\output\XmlOutput',
288
            'csv' => 'phphound\output\CsvOutput',
289
            'html' => 'phphound\output\HtmlOutput',
290
        ];
291
    }
292
293
    /**
294
     * Get argument value from user informed arguments.
295
     * @param string $name argument name.
296
     * @return Mixed argument value.
297
     */
298
    protected function getArgumentValue($name)
299
    {
300
        return $this->cli->arguments->get($name);
301
    }
302
303
    /**
304
     * Check if the user supplied an argument.
305
     * @param string $name argument name.
306
     * @return boolean if the argument has informed or not.
307
     */
308
    protected function hasArgumentValue($name)
309
    {
310
        return $this->cli->arguments->defined($name, $this->arguments);
311
    }
312
}
313