Passed
Push — master ( 1a0ddb...73af58 )
by Kyle
47s queued 10s
created

CommandLineOptions::createCheckStyleRenderer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 1
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\Baseline\BaselineMode;
21
use PHPMD\Renderer\AnsiRenderer;
22
use PHPMD\Renderer\GitHubRenderer;
23
use PHPMD\Renderer\HTMLRenderer;
24
use PHPMD\Renderer\JSONRenderer;
25
use PHPMD\Renderer\SARIFRenderer;
26
use PHPMD\Renderer\TextRenderer;
27
use PHPMD\Renderer\CheckStyleRenderer;
28
use PHPMD\Renderer\XMLRenderer;
29
use PHPMD\Rule;
30
31
/**
32
 * This is a helper class that collects the specified cli arguments and puts them
33
 * into accessible properties.
34
 *
35
 * @SuppressWarnings(PHPMD.LongVariable)
36
 */
37
class CommandLineOptions
38
{
39
    /**
40
     * Error code for invalid input
41
     */
42
    const INPUT_ERROR = 23;
43
44
    /**
45
     * The minimum rule priority.
46
     *
47
     * @var integer
48
     */
49
    protected $minimumPriority = Rule::LOWEST_PRIORITY;
50
51
    /**
52
     * The maximum rule priority.
53
     *
54
     * @var integer
55
     */
56
    protected $maximumPriority = Rule::HIGHEST_PRIORITY;
57
58
    /**
59
     * A php source code filename or directory.
60
     *
61
     * @var string
62
     */
63
    protected $inputPath;
64
65
    /**
66
     * The specified report format.
67
     *
68
     * @var string
69
     */
70
    protected $reportFormat;
71
72
    /**
73
     * An optional filename for the generated report.
74
     *
75
     * @var string
76
     */
77
    protected $reportFile;
78
79
    /**
80
     * Additional report files.
81
     *
82
     * @var array
83
     */
84
    protected $reportFiles = array();
85
86
    /**
87
     * A ruleset filename or a comma-separated string of ruleset filenames.
88
     *
89
     * @var string
90
     */
91
    protected $ruleSets;
92
93
    /**
94
     * File name of a PHPUnit code coverage report.
95
     *
96
     * @var string
97
     */
98
    protected $coverageReport;
99
100
    /**
101
     * A string of comma-separated extensions for valid php source code filenames.
102
     *
103
     * @var string
104
     */
105
    protected $extensions;
106
107
    /**
108
     * A string of comma-separated pattern that is used to exclude directories.
109
     *
110
     * Use asterisks to exclude by pattern. For example *src/foo/*.php or *src/foo/*
111
     *
112
     * @var string
113
     */
114
    protected $ignore;
115
116
    /**
117
     * Should the shell show the current phpmd version?
118
     *
119
     * @var boolean
120
     */
121
    protected $version = false;
122
123
    /**
124
     * Should PHPMD run in strict mode?
125
     *
126
     * @var boolean
127
     * @since 1.2.0
128
     */
129
    protected $strict = false;
130
131
    /**
132
     * Should PHPMD exit without error code even if error is found?
133
     *
134
     * @var boolean
135
     * @since 2.10.0
136
     */
137
    protected $ignoreErrorsOnExit = false;
138
139
    /**
140
     * Should PHPMD exit without error code even if violation is found?
141
     *
142
     * @var boolean
143
     */
144
    protected $ignoreViolationsOnExit = false;
145
146 41
    /**
147
     * List of available rule-sets.
148
     *
149 41
     * @var array(string)
150
     */
151 41
    protected $availableRuleSets = array();
152
153 41
    /**
154 41
     * Should PHPMD baseline the existing violations and write them to the $baselineFile
155 41
     * @var string allowed modes: NONE, GENERATE or UPDATE
156 41
     */
157 41
    protected $generateBaseline = BaselineMode::NONE;
158 41
159 1
    /**
160 1
     * The baseline source file to read the baseline violations from.
161 41
     * Defaults to the path of the (first) ruleset file as phpmd.baseline.xml
162 41
     * @var string|null
163 41
     */
164 1
    protected $baselineFile;
165 1
166 41
    /**
167 41
     * Constructs a new command line options instance.
168 4
     *
169 4
     * @param string[] $args
170 41
     * @param string[] $availableRuleSets
171 41
     * @throws \InvalidArgumentException
172 6
     */
173 5
    public function __construct(array $args, array $availableRuleSets = array())
174 41
    {
175 1
        // Remove current file name
176 1
        array_shift($args);
177 41
178 1
        $this->availableRuleSets = $availableRuleSets;
179
180 1
        $arguments = array();
181 1
        while (($arg = array_shift($args)) !== null) {
182 41
            switch ($arg) {
183
                case '--min-priority':
184
                case '--minimum-priority':
185 41
                case '--minimumpriority':
186 1
                    $this->minimumPriority = (int)array_shift($args);
187
                    break;
188 1
                case '--max-priority':
189 1
                case '--maximum-priority':
190 41
                case '--maximumpriority':
191
                    $this->maximumPriority = (int)array_shift($args);
192
                    break;
193 41
                case '--report-file':
194 1
                case '--reportfile':
195
                    $this->reportFile = array_shift($args);
196 1
                    break;
197 40
                case '--input-file':
198 1
                case '--inputfile':
199 1
                    array_unshift($arguments, $this->readInputFile(array_shift($args)));
200 40
                    break;
201
                case '--coverage':
202
                    $this->coverageReport = array_shift($args);
203 40
                    break;
204 1
                case '--extensions':
205 1
                    $this->logDeprecated('extensions', 'suffixes');
206 40
                    /* Deprecated: We use the suffixes option now */
207 40
                    $this->extensions = array_shift($args);
208 40
                    break;
209 40
                case '--suffixes':
210 4
                    $this->extensions = array_shift($args);
211 4
                    break;
212 4
                case '--ignore':
213
                    $this->logDeprecated('ignore', 'exclude');
214 40
                    /* Deprecated: We use the exclude option now */
215 40
                    $this->ignore = array_shift($args);
216
                    break;
217
                case '--exclude':
218
                    $this->ignore = array_shift($args);
219 39
                    break;
220 1
                case '--version':
221
                    $this->version = true;
222
223 38
                    return;
224 38
                case '--strict':
225 38
                    $this->strict = true;
226 38
                    break;
227
                case '--not-strict':
228
                    $this->strict = false;
229
                    break;
230
                case '--generate-baseline':
231
                    $this->generateBaseline = BaselineMode::GENERATE;
232
                    break;
233 6
                case '--update-baseline':
234
                    $this->generateBaseline = BaselineMode::UPDATE;
235 6
                    break;
236
                case '--baseline-file':
237
                    $this->baselineFile = array_shift($args);
238
                    break;
239
                case '--ignore-errors-on-exit':
240
                    $this->ignoreErrorsOnExit = true;
241
                    break;
242
                case '--ignore-violations-on-exit':
243 2
                    $this->ignoreViolationsOnExit = true;
244
                    break;
245 2
                case '--reportfile-checkstyle':
246
                case '--reportfile-html':
247
                case '--reportfile-json':
248
                case '--reportfile-sarif':
249
                case '--reportfile-text':
250
                case '--reportfile-xml':
251
                    preg_match('(^\-\-reportfile\-(checkstyle|html|json|sarif|text|xml)$)', $arg, $match);
252
                    $this->reportFiles[$match[1]] = array_shift($args);
253
                    break;
254 4
                default:
255
                    $arguments[] = $arg;
256 4
                    break;
257
            }
258
        }
259
260
        if (count($arguments) < 3) {
261
            throw new \InvalidArgumentException($this->usage(), self::INPUT_ERROR);
262
        }
263
264
        $this->inputPath    = (string)array_shift($arguments);
265 8
        $this->reportFormat = (string)array_shift($arguments);
266
        $this->ruleSets     = (string)array_shift($arguments);
267 8
    }
268
269
    /**
270
     * Returns a php source code filename or directory.
271
     *
272
     * @return string
273
     */
274
    public function getInputPath()
275 6
    {
276
        return $this->inputPath;
277 6
    }
278
279
    /**
280
     * Returns the specified report format.
281
     *
282
     * @return string
283
     */
284
    public function getReportFormat()
285 6
    {
286
        return $this->reportFormat;
287 6
    }
288
289
    /**
290
     * Returns the output filename for a generated report or <b>null</b> when
291
     * the report should be displayed in STDOUT.
292
     *
293
     * @return string
294
     */
295 5
    public function getReportFile()
296
    {
297 5
        return $this->reportFile;
298
    }
299
300
    /**
301
     * Returns a hash with report files specified for different renderers. The
302
     * key represents the report format and the value the report file location.
303
     *
304
     * @return array
305
     */
306 6
    public function getReportFiles()
307
    {
308 6
        return $this->reportFiles;
309
    }
310
311
    /**
312
     * Returns a ruleset filename or a comma-separated string of ruleset
313
     *
314
     * @return string
315
     */
316
    public function getRuleSets()
317 4
    {
318
        return $this->ruleSets;
319 4
    }
320
321
    /**
322
     * Returns the minimum rule priority.
323
     *
324
     * @return integer
325
     */
326
    public function getMinimumPriority()
327
    {
328 4
        return $this->minimumPriority;
329
    }
330 4
331
    /**
332
     * Returns the maximum rule priority.
333
     *
334
     * @return integer
335
     */
336
    public function getMaximumPriority()
337
    {
338 6
        return $this->maximumPriority;
339
    }
340 6
341
    /**
342
     * Returns the file name of a supplied code coverage report or <b>NULL</b>
343
     * if the user has not supplied the --coverage option.
344
     *
345
     * @return string
346
     */
347
    public function getCoverageReport()
348
    {
349 6
        return $this->coverageReport;
350
    }
351 6
352
    /**
353
     * Returns a string of comma-separated extensions for valid php source code
354
     * filenames or <b>null</b> when this argument was not set.
355
     *
356
     * @return string
357
     */
358
    public function getExtensions()
359 6
    {
360
        return $this->extensions;
361 6
    }
362
363
    /**
364
     * Returns string of comma-separated pattern that is used to exclude
365
     * directories or <b>null</b> when this argument was not set.
366
     *
367
     * @return string
368
     */
369
    public function getIgnore()
370
    {
371
        return $this->ignore;
372
    }
373
374
    /**
375
     * Was the <b>--version</b> passed to PHPMD's command line interface?
376
     *
377
     * @return boolean
378
     */
379
    public function hasVersion()
380 13
    {
381
        return $this->version;
382 13
    }
383
384 13
    /**
385 13
     * Was the <b>--strict</b> option passed to PHPMD's command line interface?
386 1
     *
387 12
     * @return boolean
388 1
     * @since 1.2.0
389 11
     */
390 5
    public function hasStrict()
391 6
    {
392
        return $this->strict;
393 6
    }
394 1
395
    /**
396 5
     * Should the current violations be baselined
397
     *
398
     * @return string
399
     */
400
    public function generateBaseline()
401
    {
402
        return $this->generateBaseline;
403 1
    }
404
405 1
    /**
406
     * The filepath of the baseline violations xml
407
     *
408
     * @return string|null
409
     */
410
    public function baselineFile()
411 5
    {
412
        return $this->baselineFile;
413 5
    }
414
415
    /**
416
     * Was the <b>--ignore-errors-on-exit</b> passed to PHPMD's command line interface?
417
     *
418
     * @return boolean
419 1
     * @since 2.10.0
420
     */
421 1
    public function ignoreErrorsOnExit()
422
    {
423
        return $this->ignoreErrorsOnExit;
424
    }
425
426
    /**
427 1
     * Was the <b>--ignore-violations-on-exit</b> passed to PHPMD's command line interface?
428
     *
429 1
     * @return boolean
430
     */
431
    public function ignoreViolationsOnExit()
432
    {
433
        return $this->ignoreViolationsOnExit;
434
    }
435
436
    /**
437
     * Creates a report renderer instance based on the user's command line
438
     * argument.
439
     *
440
     * Valid renderers are:
441
     * <ul>
442
     *   <li>xml</li>
443
     *   <li>html</li>
444 5
     *   <li>text</li>
445
     *   <li>json</li>
446 5
     * </ul>
447 1
     *
448 1
     * @param string $reportFormat
449 1
     * @return \PHPMD\AbstractRenderer
450
     * @throws \InvalidArgumentException When the specified renderer does not exist.
451
     */
452
    public function createRenderer($reportFormat = null)
453 4
    {
454 1
        $reportFormat = $reportFormat ?: $this->reportFormat;
455
456
        switch ($reportFormat) {
457
            case 'ansi':
458 3
                return $this->createAnsiRenderer();
459
            case 'checkstyle':
460 3
                return $this->createCheckStyleRenderer();
461 3
            case 'github':
462 1
                return $this->createGitHubRenderer();
463 1
            case 'html':
464 1
                return $this->createHtmlRenderer();
465 1
            case 'json':
466
                return $this->createJsonRenderer();
467 1
            case 'sarif':
468
                return $this->createSarifRenderer();
469
            case 'text':
470 2
                return $this->createTextRenderer();
471
            case 'xml':
472 2
                return $this->createXmlRenderer();
473
            default:
474 2
                return $this->createCustomRenderer();
475
        }
476
    }
477
478
    /**
479
     * @return \PHPMD\Renderer\XMLRenderer
480
     */
481
    protected function createXmlRenderer()
482 4
    {
483
        return new XMLRenderer();
484 4
    }
485
486
    /**
487
     * @return \PHPMD\Renderer\TextRenderer
488
     */
489
    protected function createTextRenderer()
490
    {
491
        return new TextRenderer();
492
    }
493 4
494 4
    /**
495 4
     * @return \PHPMD\Renderer\AnsiRenderer
496 4
     */
497 4
    protected function createAnsiRenderer()
498 4
    {
499 4
        return new AnsiRenderer();
500 4
    }
501 4
502 4
    /**
503 4
     * @return \PHPMD\Renderer\GitHubRenderer
504 4
     */
505 4
    protected function createGitHubRenderer()
506 4
    {
507 4
        return new GitHubRenderer();
508 4
    }
509
510
    /**
511
     * @return \PHPMD\Renderer\HTMLRenderer
512
     */
513
    protected function createHtmlRenderer()
514
    {
515
        return new HTMLRenderer();
516 4
    }
517
518 4
    /**
519 4
     * @return \PHPMD\Renderer\JSONRenderer
520
     */
521 4
    protected function createJsonRenderer()
522 4
    {
523 4
        return new JSONRenderer();
524
    }
525
526
    /**
527 4
     * @return \PHPMD\Renderer\JSONRenderer
528
     */
529 4
    protected function createCheckStyleRenderer()
530 4
    {
531
        return new CheckStyleRenderer();
532
    }
533
534
    /**
535
     * @return \PHPMD\Renderer\SARIFRenderer
536
     */
537
    protected function createSarifRenderer()
538
    {
539
        return new SARIFRenderer();
540
    }
541
542
    /**
543 2
     * @return \PHPMD\AbstractRenderer
544
     * @throws \InvalidArgumentException
545 2
     */
546 2
    protected function createCustomRenderer()
547
    {
548
        if ('' === $this->reportFormat) {
549
            throw new \InvalidArgumentException(
550
                'Can\'t create report with empty format.',
551 2
                self::INPUT_ERROR
552 2
            );
553
        }
554
555
        if (class_exists($this->reportFormat)) {
556
            return new $this->reportFormat();
557
        }
558
559
        // Try to load a custom renderer
560
        $fileName = strtr($this->reportFormat, '_\\', '//') . '.php';
561
562
        $fileHandle = @fopen($fileName, 'r', true);
563
        if (is_resource($fileHandle) === false) {
564
            throw new \InvalidArgumentException(
565 6
                sprintf(
566
                    'Can\'t find the custom report class: %s',
567 6
                    $this->reportFormat
568 5
                ),
569
                self::INPUT_ERROR
570 1
            );
571
        }
572
        @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...
573
574
        include_once $fileName;
575
576
        return new $this->reportFormat();
577
    }
578
579
    /**
580
     * Returns usage information for the PHPMD command line interface.
581
     *
582
     * @return string
583
     */
584
    public function usage()
585
    {
586
        $availableRenderers = $this->getListOfAvailableRenderers();
587
588
        return 'Mandatory arguments:' . \PHP_EOL .
589
            '1) A php source code filename or directory. Can be a comma-' .
590
            'separated string' . \PHP_EOL .
591
            '2) A report format' . \PHP_EOL .
592
            '3) A ruleset filename or a comma-separated string of ruleset' .
593
            'filenames' . \PHP_EOL . \PHP_EOL .
594
            'Example: phpmd /path/to/source format ruleset' . \PHP_EOL . \PHP_EOL .
595
            'Available formats: ' . $availableRenderers . '.' . \PHP_EOL .
596
            'Available rulesets: ' . implode(', ', $this->availableRuleSets) . '.' . \PHP_EOL . \PHP_EOL .
597
            'Optional arguments that may be put after the mandatory arguments:' .
598
            \PHP_EOL .
599
            '--minimumpriority: rule priority threshold; rules with lower ' .
600
            'priority than this will not be used' . \PHP_EOL .
601
            '--reportfile: send report output to a file; default to STDOUT' .
602
            \PHP_EOL .
603
            '--suffixes: comma-separated string of valid source code ' .
604
            'filename extensions, e.g. php,phtml' . \PHP_EOL .
605
            '--exclude: comma-separated string of patterns that are used to ' .
606
            'ignore directories. Use asterisks to exclude by pattern. ' .
607
            'For example *src/foo/*.php or *src/foo/*' . \PHP_EOL .
608
            '--strict: also report those nodes with a @SuppressWarnings ' .
609
            'annotation' . \PHP_EOL .
610
            '--ignore-errors-on-exit: will exit with a zero code, ' .
611
            'even on error' . \PHP_EOL .
612
            '--ignore-violations-on-exit: will exit with a zero code, ' .
613
            'even if any violations are found' . \PHP_EOL .
614
            '--generate-baseline: will generate a phpmd.baseline.xml next ' .
615
            'to the first ruleset file location' . \PHP_EOL .
616
            '--update-baseline: will remove any non-existing violations from the phpmd.baseline.xml' . \PHP_EOL .
617
            '--baseline-file: a custom location of the baseline file' . \PHP_EOL;
618
    }
619
620
    /**
621
     * Get a list of available renderers
622
     *
623
     * @return string The list of renderers found.
624
     */
625
    protected function getListOfAvailableRenderers()
626
    {
627
        $renderersDirPathName = __DIR__ . '/../Renderer';
628
        $renderers            = array();
629
630
        foreach (scandir($renderersDirPathName) as $rendererFileName) {
631
            if (preg_match('/^(\w+)Renderer.php$/i', $rendererFileName, $rendererName)) {
632
                $renderers[] = strtolower($rendererName[1]);
633
            }
634
        }
635
636
        sort($renderers);
637
638
        if (count($renderers) > 1) {
639
            return implode(', ', $renderers);
640
        }
641
642
        return array_pop($renderers);
643
    }
644
645
    /**
646
     * Logs a deprecated option to the current user interface.
647
     *
648
     * @param string $deprecatedName
649
     * @param string $newName
650
     * @return void
651
     */
652
    protected function logDeprecated($deprecatedName, $newName)
653
    {
654
        $message = sprintf(
655
            'The --%s option is deprecated, please use --%s instead.',
656
            $deprecatedName,
657
            $newName
658
        );
659
660
        fwrite(STDERR, $message . PHP_EOL . PHP_EOL);
661
    }
662
663
    /**
664
     * This method takes the given input file, reads the newline separated paths
665
     * from that file and creates a comma separated string of the file paths. If
666
     * the given <b>$inputFile</b> not exists, this method will throw an
667
     * exception.
668
     *
669
     * @param string $inputFile Specified input file name.
670
     * @return string
671
     * @throws \InvalidArgumentException If the specified input file does not exist.
672
     * @since 1.1.0
673
     */
674
    protected function readInputFile($inputFile)
675
    {
676
        if (file_exists($inputFile)) {
677
            return implode(',', array_map('trim', file($inputFile)));
678
        }
679
        throw new \InvalidArgumentException("Input file '{$inputFile}' not exists.");
680
    }
681
}
682