Passed
Push — master ( 5ba82c...2c1d46 )
by Kyle
02:49 queued 11s
created

CommandLineOptions::createRenderer()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7.0178

Importance

Changes 0
Metric Value
cc 7
nc 12
nop 1
dl 0
loc 19
ccs 13
cts 14
cp 0.9286
crap 7.0178
rs 8.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of PHP Mess Detector.
4
 *
5
 * Copyright (c) Manuel Pichler <[email protected]>.
6
 * All rights reserved.
7
 *
8
 * Licensed under BSD License
9
 * For full copyright and license information, please see the LICENSE file.
10
 * Redistributions of files must retain the above copyright notice.
11
 *
12
 * @author Manuel Pichler <[email protected]>
13
 * @copyright Manuel Pichler. All rights reserved.
14
 * @license https://opensource.org/licenses/bsd-license.php BSD License
15
 * @link http://phpmd.org/
16
 */
17
18
namespace PHPMD\TextUI;
19
20
use PHPMD\Renderer\AnsiRenderer;
21
use PHPMD\Renderer\HTMLRenderer;
22
use PHPMD\Renderer\JSONRenderer;
23
use PHPMD\Renderer\TextRenderer;
24
use PHPMD\Renderer\XMLRenderer;
25
use PHPMD\Rule;
26
27
/**
28
 * This is a helper class that collects the specified cli arguments and puts them
29
 * into accessible properties.
30
 */
31
class CommandLineOptions
32
{
33
    /**
34
     * Error code for invalid input
35
     */
36
    const INPUT_ERROR = 23;
37
38
    /**
39
     * The minimum rule priority.
40
     *
41
     * @var integer
42
     */
43
    protected $minimumPriority = Rule::LOWEST_PRIORITY;
44
45
    /**
46
     * The maximum rule priority.
47
     *
48
     * @var integer
49
     */
50
    protected $maximumPriority = Rule::HIGHEST_PRIORITY;
51
52
    /**
53
     * A php source code filename or directory.
54
     *
55
     * @var string
56
     */
57
    protected $inputPath;
58
59
    /**
60
     * The specified report format.
61
     *
62
     * @var string
63
     */
64
    protected $reportFormat;
65
66
    /**
67
     * An optional filename for the generated report.
68
     *
69
     * @var string
70
     */
71
    protected $reportFile;
72
73
    /**
74
     * Additional report files.
75
     *
76
     * @var array
77
     */
78
    protected $reportFiles = array();
79
80
    /**
81
     * A ruleset filename or a comma-separated string of ruleset filenames.
82
     *
83
     * @var string
84
     */
85
    protected $ruleSets;
86
87
    /**
88
     * File name of a PHPUnit code coverage report.
89
     *
90
     * @var string
91
     */
92
    protected $coverageReport;
93
94
    /**
95
     * A string of comma-separated extensions for valid php source code filenames.
96
     *
97
     * @var string
98
     */
99
    protected $extensions;
100
101
    /**
102
     * A string of comma-separated pattern that is used to exclude directories.
103
     *
104
     * @var string
105
     */
106
    protected $ignore;
107
108
    /**
109
     * Should the shell show the current phpmd version?
110
     *
111
     * @var boolean
112
     */
113
    protected $version = false;
114
115
    /**
116
     * Should PHPMD run in strict mode?
117
     *
118
     * @var boolean
119
     * @since 1.2.0
120
     */
121
    protected $strict = false;
122
123
    /**
124
     * Should PHPMD exit without error code even if violation is found?
125
     *
126
     * @var boolean
127
     */
128
    protected $ignoreViolationsOnExit = false;
129
130
    /**
131
     * List of available rule-sets.
132
     *
133
     * @var array(string)
134
     */
135
    protected $availableRuleSets = array();
136
137
    /**
138
     * Constructs a new command line options instance.
139
     *
140
     * @param array $args
141
     * @param array $availableRuleSets
142
     * @throws \InvalidArgumentException
143
     */
144 41
    public function __construct(array $args, array $availableRuleSets = array())
145
    {
146
        // Remove current file name
147 41
        array_shift($args);
148
149 41
        $this->availableRuleSets = $availableRuleSets;
150
151 41
        $arguments = array();
152 41
        while (($arg = array_shift($args)) !== null) {
153 41
            switch ($arg) {
154 41
                case '--min-priority':
155 41
                case '--minimum-priority':
156 41
                case '--minimumpriority':
157 1
                    $this->minimumPriority = (int)array_shift($args);
158 1
                    break;
159 41
                case '--max-priority':
160 41
                case '--maximum-priority':
161 41
                case '--maximumpriority':
162 1
                    $this->maximumPriority = (int)array_shift($args);
163 1
                    break;
164 41
                case '--report-file':
165 41
                case '--reportfile':
166 4
                    $this->reportFile = array_shift($args);
167 4
                    break;
168 41
                case '--input-file':
169 41
                case '--inputfile':
170 6
                    array_unshift($arguments, $this->readInputFile(array_shift($args)));
171 5
                    break;
172 41
                case '--coverage':
173 1
                    $this->coverageReport = array_shift($args);
174 1
                    break;
175 41
                case '--extensions':
176 1
                    $this->logDeprecated('extensions', 'suffixes');
177
                    /* Deprecated: We use the suffixes option now */
178 1
                    $this->extensions = array_shift($args);
179 1
                    break;
180 41
                case '--suffixes':
181
                    $this->extensions = array_shift($args);
182
                    break;
183 41
                case '--ignore':
184 1
                    $this->logDeprecated('ignore', 'exclude');
185
                    /* Deprecated: We use the exclude option now */
186 1
                    $this->ignore = array_shift($args);
187 1
                    break;
188 41
                case '--exclude':
189
                    $this->ignore = array_shift($args);
190
                    break;
191 41
                case '--version':
192 1
                    $this->version = true;
193
194 1
                    return;
195 40
                case '--strict':
196 1
                    $this->strict = true;
197 1
                    break;
198 40
                case '--not-strict':
199
                    $this->strict = false;
200
                    break;
201 40
                case '--ignore-violations-on-exit':
202 1
                    $this->ignoreViolationsOnExit = true;
203 1
                    break;
204 40
                case '--reportfile-html':
205 40
                case '--reportfile-text':
206 40
                case '--reportfile-xml':
207 40
                case '--reportfile-json':
208 4
                    preg_match('(^\-\-reportfile\-(xml|html|text|json)$)', $arg, $match);
209 4
                    $this->reportFiles[$match[1]] = array_shift($args);
210 4
                    break;
211
                default:
212 40
                    $arguments[] = $arg;
213 40
                    break;
214
            }
215
        }
216
217 39
        if (count($arguments) < 3) {
218 1
            throw new \InvalidArgumentException($this->usage(), self::INPUT_ERROR);
219
        }
220
221 38
        $this->inputPath = (string)array_shift($arguments);
222 38
        $this->reportFormat = (string)array_shift($arguments);
223 38
        $this->ruleSets = (string)array_shift($arguments);
224 38
    }
225
226
    /**
227
     * Returns a php source code filename or directory.
228
     *
229
     * @return string
230
     */
231 6
    public function getInputPath()
232
    {
233 6
        return $this->inputPath;
234
    }
235
236
    /**
237
     * Returns the specified report format.
238
     *
239
     * @return string
240
     */
241 2
    public function getReportFormat()
242
    {
243 2
        return $this->reportFormat;
244
    }
245
246
    /**
247
     * Returns the output filename for a generated report or <b>null</b> when
248
     * the report should be displayed in STDOUT.
249
     *
250
     * @return string
251
     */
252 4
    public function getReportFile()
253
    {
254 4
        return $this->reportFile;
255
    }
256
257
    /**
258
     * Returns a hash with report files specified for different renderers. The
259
     * key represents the report format and the value the report file location.
260
     *
261
     * @return array
262
     */
263 8
    public function getReportFiles()
264
    {
265 8
        return $this->reportFiles;
266
    }
267
268
    /**
269
     * Returns a ruleset filename or a comma-separated string of ruleset
270
     *
271
     * @return string
272
     */
273 6
    public function getRuleSets()
274
    {
275 6
        return $this->ruleSets;
276
    }
277
278
    /**
279
     * Returns the minimum rule priority.
280
     *
281
     * @return integer
282
     */
283 6
    public function getMinimumPriority()
284
    {
285 6
        return $this->minimumPriority;
286
    }
287
288
    /**
289
     * Returns the maximum rule priority.
290
     *
291
     * @return integer
292
     */
293 5
    public function getMaximumPriority()
294
    {
295 5
        return $this->maximumPriority;
296
    }
297
298
    /**
299
     * Returns the file name of a supplied code coverage report or <b>NULL</b>
300
     * if the user has not supplied the --coverage option.
301
     *
302
     * @return string
303
     */
304 6
    public function getCoverageReport()
305
    {
306 6
        return $this->coverageReport;
307
    }
308
309
    /**
310
     * Returns a string of comma-separated extensions for valid php source code
311
     * filenames or <b>null</b> when this argument was not set.
312
     *
313
     * @return string
314
     */
315 4
    public function getExtensions()
316
    {
317 4
        return $this->extensions;
318
    }
319
320
    /**
321
     * Returns  string of comma-separated pattern that is used to exclude
322
     * directories or <b>null</b> when this argument was not set.
323
     *
324
     * @return string
325
     */
326 4
    public function getIgnore()
327
    {
328 4
        return $this->ignore;
329
    }
330
331
    /**
332
     * Was the <b>--version</b> passed to PHPMD's command line interface?
333
     *
334
     * @return boolean
335
     */
336 6
    public function hasVersion()
337
    {
338 6
        return $this->version;
339
    }
340
341
    /**
342
     * Was the <b>--strict</b> option passed to PHPMD's command line interface?
343
     *
344
     * @return boolean
345
     * @since 1.2.0
346
     */
347 6
    public function hasStrict()
348
    {
349 6
        return $this->strict;
350
    }
351
352
    /**
353
     * Was the <b>--ignore-violations-on-exit</b> passed to PHPMD's command line interface?
354
     *
355
     * @return boolean
356
     */
357 6
    public function ignoreViolationsOnExit()
358
    {
359 6
        return $this->ignoreViolationsOnExit;
360
    }
361
362
    /**
363
     * Creates a report renderer instance based on the user's command line
364
     * argument.
365
     *
366
     * Valid renderers are:
367
     * <ul>
368
     *   <li>xml</li>
369
     *   <li>html</li>
370
     *   <li>text</li>
371
     *   <li>json</li>
372
     * </ul>
373
     *
374
     * @param string $reportFormat
375
     * @return \PHPMD\AbstractRenderer
376
     * @throws \InvalidArgumentException When the specified renderer does not exist.
377
     */
378 13
    public function createRenderer($reportFormat = null)
379
    {
380 13
        $reportFormat = $reportFormat ?: $this->reportFormat;
381
382 13
        switch ($reportFormat) {
383 13
            case 'xml':
384 1
                return $this->createXmlRenderer();
385 12
            case 'html':
386 1
                return $this->createHtmlRenderer();
387 11
            case 'text':
388 5
                return $this->createTextRenderer();
389 6
            case 'json':
390
                return $this->createJsonRenderer();
391 6
            case 'ansi':
392 1
                return $this->createAnsiRenderer();
393
            default:
394 5
                return $this->createCustomRenderer();
395
        }
396
    }
397
398
    /**
399
     * @return \PHPMD\Renderer\XMLRenderer
400
     */
401 1
    protected function createXmlRenderer()
402
    {
403 1
        return new XMLRenderer();
404
    }
405
406
    /**
407
     * @return \PHPMD\Renderer\TextRenderer
408
     */
409 5
    protected function createTextRenderer()
410
    {
411 5
        return new TextRenderer();
412
    }
413
414
    /**
415
     * @return \PHPMD\Renderer\AnsiRenderer
416
     */
417 1
    protected function createAnsiRenderer()
418
    {
419 1
        return new AnsiRenderer();
420
    }
421
422
    /**
423
     * @return \PHPMD\Renderer\HTMLRenderer
424
     */
425 1
    protected function createHtmlRenderer()
426
    {
427 1
        return new HTMLRenderer();
428
    }
429
430
    /**
431
     * @return \PHPMD\Renderer\JSONRenderer
432
     */
433
    protected function createJsonRenderer()
434
    {
435
        return new JSONRenderer();
436
    }
437
438
    /**
439
     * @return \PHPMD\AbstractRenderer
440
     * @throws \InvalidArgumentException
441
     */
442 5
    protected function createCustomRenderer()
443
    {
444 5
        if ('' === $this->reportFormat) {
445 1
            throw new \InvalidArgumentException(
446 1
                'Can\'t create report with empty format.',
447 1
                self::INPUT_ERROR
448
            );
449
        }
450
451 4
        if (class_exists($this->reportFormat)) {
452 1
            return new $this->reportFormat();
453
        }
454
455
        // Try to load a custom renderer
456 3
        $fileName = strtr($this->reportFormat, '_\\', '//') . '.php';
457
458 3
        $fileHandle = @fopen($fileName, 'r', true);
459 3
        if (is_resource($fileHandle) === false) {
460 1
            throw new \InvalidArgumentException(
461 1
                sprintf(
462 1
                    'Can\'t find the custom report class: %s',
463 1
                    $this->reportFormat
464
                ),
465 1
                self::INPUT_ERROR
466
            );
467
        }
468 2
        @fclose($fileHandle);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
469
470 2
        include_once $fileName;
471
472 2
        return new $this->reportFormat();
473
    }
474
475
    /**
476
     * Returns usage information for the PHPMD command line interface.
477
     *
478
     * @return string
479
     */
480 4
    public function usage()
481
    {
482 4
        $availableRenderers = $this->getListOfAvailableRenderers();
483
484
        return 'Mandatory arguments:' . \PHP_EOL .
485
            '1) A php source code filename or directory. Can be a comma-' .
486
            'separated string' . \PHP_EOL .
487
            '2) A report format' . \PHP_EOL .
488
            '3) A ruleset filename or a comma-separated string of ruleset' .
489
            'filenames' . \PHP_EOL . \PHP_EOL .
490
            'Example: phpmd /path/to/source format ruleset' . \PHP_EOL . \PHP_EOL .
491 4
            'Available formats: ' . $availableRenderers . '.' . \PHP_EOL .
492 4
            'Available rulesets: ' . implode(', ', $this->availableRuleSets) . '.' . \PHP_EOL . \PHP_EOL .
493 4
            'Optional arguments that may be put after the mandatory arguments:' .
494 4
            \PHP_EOL .
495 4
            '--minimumpriority: rule priority threshold; rules with lower ' .
496 4
            'priority than this will not be used' . \PHP_EOL .
497 4
            '--reportfile: send report output to a file; default to STDOUT' .
498 4
            \PHP_EOL .
499 4
            '--suffixes: comma-separated string of valid source code ' .
500 4
            'filename extensions, e.g. php,phtml' . \PHP_EOL .
501 4
            '--exclude: comma-separated string of patterns that are used to ' .
502 4
            'ignore directories' . \PHP_EOL .
503 4
            '--strict: also report those nodes with a @SuppressWarnings ' .
504 4
            'annotation' . \PHP_EOL .
505 4
            '--ignore-violations-on-exit: will exit with a zero code, ' .
506 4
            'even if any violations are found' . \PHP_EOL;
507
    }
508
509
    /**
510
     * Get a list of available renderers
511
     *
512
     * @return string The list of renderers found.
513
     */
514 4
    protected function getListOfAvailableRenderers()
515
    {
516 4
        $renderersDirPathName=__DIR__.'/../Renderer';
517 4
        $renderers = array();
518
519 4
        foreach (scandir($renderersDirPathName) as $rendererFileName) {
520 4
            if (preg_match('/^(\w+)Renderer.php$/i', $rendererFileName, $rendererName)) {
521 4
                $renderers[] =  strtolower($rendererName[1]);
522
            }
523
        }
524
525 4
        sort($renderers);
526
527 4
        if (count($renderers) > 1) {
528 4
            return implode(', ', $renderers);
529
        }
530
531
        return array_pop($renderers);
532
    }
533
534
    /**
535
     * Logs a deprecated option to the current user interface.
536
     *
537
     * @param string $deprecatedName
538
     * @param string $newName
539
     * @return void
540
     */
541 2
    protected function logDeprecated($deprecatedName, $newName)
542
    {
543 2
        $message = sprintf(
544 2
            'The --%s option is deprecated, please use --%s instead.',
545
            $deprecatedName,
546
            $newName
547
        );
548
549 2
        fwrite(STDERR, $message . PHP_EOL . PHP_EOL);
550 2
    }
551
552
    /**
553
     * This method takes the given input file, reads the newline separated paths
554
     * from that file and creates a comma separated string of the file paths. If
555
     * the given <b>$inputFile</b> not exists, this method will throw an
556
     * exception.
557
     *
558
     * @param string $inputFile Specified input file name.
559
     * @return string
560
     * @throws \InvalidArgumentException If the specified input file does not exist.
561
     * @since 1.1.0
562
     */
563 6
    protected function readInputFile($inputFile)
564
    {
565 6
        if (file_exists($inputFile)) {
566 5
            return join(',', array_map('trim', file($inputFile)));
567
        }
568 1
        throw new \InvalidArgumentException("Input file '{$inputFile}' not exists.");
569
    }
570
}
571