CommandLineOptions::generateBaseline()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 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 InvalidArgumentException;
21
use PHPMD\Baseline\BaselineMode;
22
use PHPMD\Cache\Model\ResultCacheStrategy;
23
use PHPMD\Console\OutputInterface;
24
use PHPMD\Renderer\AnsiRenderer;
25
use PHPMD\Renderer\CheckStyleRenderer;
26
use PHPMD\Renderer\GitHubRenderer;
27
use PHPMD\Renderer\GitLabRenderer;
28
use PHPMD\Renderer\HTMLRenderer;
29
use PHPMD\Renderer\JSONRenderer;
30
use PHPMD\Renderer\Option\Color;
31
use PHPMD\Renderer\Option\Verbose;
32
use PHPMD\Renderer\SARIFRenderer;
33
use PHPMD\Renderer\TextRenderer;
34
use PHPMD\Renderer\XMLRenderer;
35
use PHPMD\Rule;
36
use PHPMD\Utility\ArgumentsValidator;
37
38
/**
39
 * This is a helper class that collects the specified cli arguments and puts them
40
 * into accessible properties.
41
 *
42
 * @SuppressWarnings(PHPMD.LongVariable)
43
 */
44
class CommandLineOptions
45
{
46
    /**
47
     * Error code for invalid input
48
     */
49
    const INPUT_ERROR = 23;
50
51
    /**
52
     * The minimum rule priority.
53
     *
54
     * @var integer
55
     */
56
    protected $minimumPriority = Rule::LOWEST_PRIORITY;
57
58
    /**
59
     * The maximum rule priority.
60
     *
61
     * @var integer
62
     */
63
    protected $maximumPriority = Rule::HIGHEST_PRIORITY;
64
65
    /**
66
     * A php source code filename or directory.
67
     *
68
     * @var string
69
     */
70
    protected $inputPath;
71
72
    /**
73
     * The specified report format.
74
     *
75
     * @var string
76
     */
77
    protected $reportFormat;
78
79
    /**
80
     * An optional filename for the generated report.
81
     *
82
     * @var string
83
     */
84
    protected $reportFile;
85
86
    /**
87
     * An optional filename to collect errors.
88
     *
89
     * @var string
90
     */
91
    protected $errorFile;
92
93
    /**
94
     * Additional report files.
95
     *
96
     * @var array
97
     */
98
    protected $reportFiles = array();
99
100
    /**
101
     * List of deprecations.
102
     *
103
     * @var array
104
     */
105
    protected $deprecations = array();
106
107
    /**
108
     * A ruleset filename or a comma-separated string of ruleset filenames.
109
     *
110
     * @var string
111
     */
112
    protected $ruleSets;
113
114
    /**
115
     * File name of a PHPUnit code coverage report.
116
     *
117
     * @var string
118
     */
119
    protected $coverageReport;
120
121
    /**
122
     * A string of comma-separated extensions for valid php source code filenames.
123
     *
124
     * @var string
125
     */
126
    protected $extensions;
127
128
    /**
129
     * A string of comma-separated pattern that is used to exclude directories.
130
     *
131
     * Use asterisks to exclude by pattern. For example *src/foo/*.php or *src/foo/*
132
     *
133
     * @var string
134
     */
135
    protected $ignore;
136
137
    /**
138
     * Should the shell show the current phpmd version?
139
     *
140
     * @var boolean
141
     */
142
    protected $version = false;
143
144
    /**
145
     * Should PHPMD run in strict mode?
146
     *
147
     * @var boolean
148
     * @since 1.2.0
149
     */
150
    protected $strict = false;
151
152
    /** @var int */
153
    protected $verbosity = OutputInterface::VERBOSITY_NORMAL;
154
155
    /**
156
     * Should PHPMD exit without error code even if error is found?
157
     *
158
     * @var boolean
159
     * @since 2.10.0
160
     */
161
    protected $ignoreErrorsOnExit = false;
162
163
    /**
164
     * Should PHPMD exit without error code even if violation is found?
165
     *
166
     * @var boolean
167
     */
168
    protected $ignoreViolationsOnExit = false;
169
170
    /**
171
     * List of available rule-sets.
172
     *
173
     * @var array(string)
174
     */
175
    protected $availableRuleSets = array();
176
177
    /**
178
     * Should PHPMD baseline the existing violations and write them to the $baselineFile
179
     * @var string allowed modes: NONE, GENERATE or UPDATE
180
     */
181
    protected $generateBaseline = BaselineMode::NONE;
182
183
    /**
184
     * The baseline source file to read the baseline violations from.
185
     * Defaults to the path of the (first) ruleset file as phpmd.baseline.xml
186
     * @var string|null
187
     */
188
    protected $baselineFile;
189
190
    /**
191
     * Should PHPMD read or write the result cache state from the cache file
192
     * @var bool
193
     */
194
    protected $cacheEnabled = false;
195
196
    /**
197
     * If set the path to read and write the result cache state from and to.
198
     * @var string|null
199
     */
200
    protected $cacheFile;
201
202
    /**
203
     * If set determine the cache strategy. Either `content` or `timestamp`. Defaults to `content`.
204
     * @var string|null
205
     */
206
    protected $cacheStrategy;
207
208
    /**
209
     * Either the output should be colored.
210
     *
211
     * @var bool
212
     */
213
    protected $colored = false;
214
215
    /**
216
     * Specify how many extra lines are added to a code snippet
217
     * @var int|null
218
     */
219
    protected $extraLineInExcerpt;
220
221
    /**
222
     * Constructs a new command line options instance.
223
     *
224
     * @param string[] $args
225
     * @param string[] $availableRuleSets
226
     * @throws InvalidArgumentException
227
     */
228
    public function __construct(array $args, array $availableRuleSets = array())
229
    {
230
        // Remove current file name
231
        array_shift($args);
232
233
        $originalArguments = $args;
234
        $this->availableRuleSets = $availableRuleSets;
235
236
        $arguments = array();
237
        $listenOptions = true;
238
        $hasImplicitArguments = false;
239
240
        while (($arg = array_shift($args)) !== null) {
241
            if (!$listenOptions) {
242
                $arguments[] = $arg;
243
244
                continue;
245
            }
246
247
            $equalChunk = explode('=', $arg, 2);
248
249
            switch ($equalChunk[0]) {
250
                case '--':
251
                    $this->refuseValue($equalChunk);
252
                    $listenOptions = false;
253
                    break;
254
                case '--verbose':
255
                case '-v':
256
                    $this->refuseValue($equalChunk);
257
                    $this->verbosity = OutputInterface::VERBOSITY_VERBOSE;
258
                    break;
259
                case '-vv':
260
                    $this->refuseValue($equalChunk);
261
                    $this->verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE;
262
                    break;
263
                case '-vvv':
264
                    $this->refuseValue($equalChunk);
265
                    $this->verbosity = OutputInterface::VERBOSITY_DEBUG;
266
                    break;
267
                case '--min-priority':
268
                case '--minimum-priority':
269
                case '--minimumpriority':
270
                    $this->minimumPriority = (int)$this->readValue($equalChunk, $args);
271
                    break;
272
                case '--max-priority':
273
                case '--maximum-priority':
274
                case '--maximumpriority':
275
                    $this->maximumPriority = (int)$this->readValue($equalChunk, $args);
276
                    break;
277
                case '--report-file':
278
                case '--reportfile':
279
                    $this->reportFile = $this->readValue($equalChunk, $args);
280
                    break;
281
                case '--error-file':
282
                case '--errorfile':
283
                    $this->errorFile = $this->readValue($equalChunk, $args);
284
                    break;
285
                case '--input-file':
286
                case '--inputfile':
287
                    array_unshift($arguments, $this->readInputFile($this->readValue($equalChunk, $args)));
288
                    break;
289
                case '--coverage':
290
                    $this->coverageReport = $this->readValue($equalChunk, $args);
291
                    break;
292
                case '--extensions':
293
                    $this->logDeprecated('extensions', 'suffixes');
294
                    /* Deprecated: We use the suffixes option now */
295
                    $this->extensions = $this->readValue($equalChunk, $args);
296
                    break;
297
                case '--suffixes':
298
                    $this->extensions = $this->readValue($equalChunk, $args);
299
                    break;
300
                case '--ignore':
301
                    $this->logDeprecated('ignore', 'exclude');
302
                    /* Deprecated: We use the exclude option now */
303
                    $this->ignore = $this->readValue($equalChunk, $args);
304
                    break;
305
                case '--exclude':
306
                    $this->ignore = $this->readValue($equalChunk, $args);
307
                    break;
308
                case '--color':
309
                    $this->refuseValue($equalChunk);
310
                    $this->colored = true;
311
                    break;
312
                case '--version':
313
                    $this->refuseValue($equalChunk);
314
                    $this->version = true;
315
316
                    return;
317
                case '--strict':
318
                    $this->refuseValue($equalChunk);
319
                    $this->strict = true;
320
                    break;
321
                case '--not-strict':
322
                    $this->refuseValue($equalChunk);
323
                    $this->strict = false;
324
                    break;
325
                case '--generate-baseline':
326
                    $this->refuseValue($equalChunk);
327
                    $this->generateBaseline = BaselineMode::GENERATE;
328
                    break;
329
                case '--update-baseline':
330
                    $this->refuseValue($equalChunk);
331
                    $this->generateBaseline = BaselineMode::UPDATE;
332
                    break;
333
                case '--baseline-file':
334
                    $this->baselineFile = $this->readValue($equalChunk, $args);
335
                    break;
336
                case '--cache':
337
                    $this->refuseValue($equalChunk);
338
                    $this->cacheEnabled = true;
339
                    break;
340
                case '--cache-file':
341
                    $this->cacheFile = $this->readValue($equalChunk, $args);
342
                    break;
343
                case '--cache-strategy':
344
                    $this->cacheStrategy = $this->readValue($equalChunk, $args);
345
                    break;
346
                case '--ignore-errors-on-exit':
347
                    $this->refuseValue($equalChunk);
348
                    $this->ignoreErrorsOnExit = true;
349
                    break;
350
                case '--ignore-violations-on-exit':
351
                    $this->refuseValue($equalChunk);
352
                    $this->ignoreViolationsOnExit = true;
353
                    break;
354
                case '--reportfile-checkstyle':
355
                case '--reportfile-github':
356
                case '--reportfile-gitlab':
357
                case '--reportfile-html':
358
                case '--reportfile-json':
359
                case '--reportfile-sarif':
360
                case '--reportfile-text':
361
                case '--reportfile-xml':
362
                    preg_match('(^\-\-reportfile\-(checkstyle|github|gitlab|html|json|sarif|text|xml)$)', $arg, $match);
363
                    $this->reportFiles[$match[1]] = $this->readValue($equalChunk, $args);
364
                    break;
365
                case '--extra-line-in-excerpt':
366
                    $this->extraLineInExcerpt = (int)$this->readValue($equalChunk, $args);
367
                    break;
368
                default:
369
                    $hasImplicitArguments = true;
370
                    $arguments[] = $arg;
371
                    break;
372
            }
373
        }
374
375
        if (count($arguments) < 3) {
376
            throw new InvalidArgumentException($this->usage(), self::INPUT_ERROR);
377
        }
378
379
        $validator = new ArgumentsValidator($hasImplicitArguments, $originalArguments, $arguments);
380
381
        $this->ruleSets = (string)array_pop($arguments);
382
        $validator->validate('ruleset', $this->ruleSets);
383
384
        $this->reportFormat = (string)array_pop($arguments);
385
        $validator->validate('report format', $this->reportFormat);
386
387
        $this->inputPath = implode(',', $arguments);
388
389
        if ($this->inputPath === '-') {
390
            $this->inputPath = 'php://stdin';
391
392
            return;
393
        }
394
395
        foreach ($arguments as $arg) {
396
            $validator->validate('input path', $arg);
397
        }
398
    }
399
400
    /**
401
     * Returns a php source code filename or directory.
402
     *
403
     * @return string
404
     */
405
    public function getInputPath()
406
    {
407
        return $this->inputPath;
408
    }
409
410
    /**
411
     * Returns the specified report format.
412
     *
413
     * @return string
414
     */
415
    public function getReportFormat()
416
    {
417
        return $this->reportFormat;
418
    }
419
420
    /**
421
     * Returns the output filename for a generated report or <b>null</b> when
422
     * the report should be displayed in STDOUT.
423
     *
424
     * @return string
425
     */
426
    public function getReportFile()
427
    {
428
        return $this->reportFile;
429
    }
430
431
    /**
432
     * Returns the output filename for the errors or <b>null</b> when
433
     * the report should be displayed in STDERR.
434
     *
435
     * @return string
436
     */
437
    public function getErrorFile()
438
    {
439
        return $this->errorFile;
440
    }
441
442
    /**
443
     * Return the list of deprecations raised when parsing options.
444
     *
445
     * @return list<string>
0 ignored issues
show
Bug introduced by
The type PHPMD\TextUI\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
446
     */
447
    public function getDeprecations()
448
    {
449
        return $this->deprecations;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->deprecations returns the type array which is incompatible with the documented return type PHPMD\TextUI\list.
Loading history...
450
    }
451
452
    /**
453
     * Returns a hash with report files specified for different renderers. The
454
     * key represents the report format and the value the report file location.
455
     *
456
     * @return array
457
     */
458
    public function getReportFiles()
459
    {
460
        return $this->reportFiles;
461
    }
462
463
    /**
464
     * Returns a ruleset filename or a comma-separated string of ruleset
465
     *
466
     * @return string
467
     */
468
    public function getRuleSets()
469
    {
470
        return $this->ruleSets;
471
    }
472
473
    /**
474
     * Returns the minimum rule priority.
475
     *
476
     * @return integer
477
     */
478
    public function getMinimumPriority()
479
    {
480
        return $this->minimumPriority;
481
    }
482
483
    /**
484
     * Returns the maximum rule priority.
485
     *
486
     * @return integer
487
     */
488
    public function getMaximumPriority()
489
    {
490
        return $this->maximumPriority;
491
    }
492
493
    /**
494
     * Returns the file name of a supplied code coverage report or <b>NULL</b>
495
     * if the user has not supplied the --coverage option.
496
     *
497
     * @return string
498
     */
499
    public function getCoverageReport()
500
    {
501
        return $this->coverageReport;
502
    }
503
504
    /**
505
     * Returns a string of comma-separated extensions for valid php source code
506
     * filenames or <b>null</b> when this argument was not set.
507
     *
508
     * @return string
509
     */
510
    public function getExtensions()
511
    {
512
        return $this->extensions;
513
    }
514
515
    /**
516
     * Returns string of comma-separated pattern that is used to exclude
517
     * directories or <b>null</b> when this argument was not set.
518
     *
519
     * @return string
520
     */
521
    public function getIgnore()
522
    {
523
        return $this->ignore;
524
    }
525
526
    /**
527
     * Was the <b>--version</b> passed to PHPMD's command line interface?
528
     *
529
     * @return boolean
530
     */
531
    public function hasVersion()
532
    {
533
        return $this->version;
534
    }
535
536
    /**
537
     * Was the <b>--strict</b> option passed to PHPMD's command line interface?
538
     *
539
     * @return boolean
540
     * @since 1.2.0
541
     */
542
    public function hasStrict()
543
    {
544
        return $this->strict;
545
    }
546
547
    /**
548
     * @return int
549
     */
550
    public function getVerbosity()
551
    {
552
        return $this->verbosity;
553
    }
554
555
    /**
556
     * Should the current violations be baselined
557
     *
558
     * @return string
559
     */
560
    public function generateBaseline()
561
    {
562
        return $this->generateBaseline;
563
    }
564
565
    /**
566
     * The filepath of the baseline violations xml
567
     *
568
     * @return string|null
569
     */
570
    public function baselineFile()
571
    {
572
        return $this->baselineFile;
573
    }
574
575
    /**
576
     * @return bool
577
     */
578
    public function isCacheEnabled()
579
    {
580
        return $this->cacheEnabled;
581
    }
582
583
    /**
584
     * The filepath to the result cache state file
585
     *
586
     * @return string
587
     */
588
    public function cacheFile()
589
    {
590
        return $this->cacheFile === null ? '.phpmd.result-cache.php' : $this->cacheFile;
591
    }
592
593
    /**
594
     * The caching strategy to determine if a file should be (re)inspected. Either
595
     * `content` or last modified `timestamp` based.
596
     *
597
     * @return string
598
     */
599
    public function cacheStrategy()
600
    {
601
        switch ($this->cacheStrategy) {
602
            case ResultCacheStrategy::CONTENT:
603
            case ResultCacheStrategy::TIMESTAMP:
604
                return $this->cacheStrategy;
605
            default:
606
                return ResultCacheStrategy::CONTENT;
607
        }
608
    }
609
610
    /**
611
     * Was the <b>--ignore-errors-on-exit</b> passed to PHPMD's command line interface?
612
     *
613
     * @return boolean
614
     * @since 2.10.0
615
     */
616
    public function ignoreErrorsOnExit()
617
    {
618
        return $this->ignoreErrorsOnExit;
619
    }
620
621
    /**
622
     * Was the <b>--ignore-violations-on-exit</b> passed to PHPMD's command line interface?
623
     *
624
     * @return boolean
625
     */
626
    public function ignoreViolationsOnExit()
627
    {
628
        return $this->ignoreViolationsOnExit;
629
    }
630
631
    /**
632
     * Specify how many extra lines are added to a code snippet
633
     *
634
     * @return int|null
635
     */
636
    public function extraLineInExcerpt()
637
    {
638
        return $this->extraLineInExcerpt;
639
    }
640
    /**
641
     * Creates a report renderer instance based on the user's command line
642
     * argument.
643
     *
644
     * Valid renderers are:
645
     * <ul>
646
     *   <li>xml</li>
647
     *   <li>html</li>
648
     *   <li>text</li>
649
     *   <li>json</li>
650
     * </ul>
651
     *
652
     * @param string $reportFormat
653
     * @return \PHPMD\AbstractRenderer
654
     * @throws InvalidArgumentException When the specified renderer does not exist.
655
     */
656
    public function createRenderer($reportFormat = null)
657
    {
658
        $renderer = $this->createRendererWithoutOptions($reportFormat);
659
660
        if ($renderer instanceof Verbose) {
661
            $renderer->setVerbosityLevel($this->verbosity);
662
        }
663
664
        if ($renderer instanceof Color) {
665
            $renderer->setColored($this->colored);
666
        }
667
668
        return $renderer;
669
    }
670
671
    /**
672
     * @param string $reportFormat
673
     * @return \PHPMD\AbstractRenderer
674
     * @throws InvalidArgumentException When the specified renderer does not exist.
675
     */
676
    protected function createRendererWithoutOptions($reportFormat = null)
677
    {
678
        $reportFormat = $reportFormat ?: $this->reportFormat;
679
680
        switch ($reportFormat) {
681
            case 'ansi':
682
                return $this->createAnsiRenderer();
683
            case 'checkstyle':
684
                return $this->createCheckStyleRenderer();
685
            case 'gitlab':
686
                return $this->createGitLabRenderer();
687
            case 'github':
688
                return $this->createGitHubRenderer();
689
            case 'html':
690
                return $this->createHtmlRenderer();
691
            case 'json':
692
                return $this->createJsonRenderer();
693
            case 'sarif':
694
                return $this->createSarifRenderer();
695
            case 'text':
696
                return $this->createTextRenderer();
697
            case 'xml':
698
                return $this->createXmlRenderer();
699
            default:
700
                return $this->createCustomRenderer();
701
        }
702
    }
703
704
    /**
705
     * @return \PHPMD\Renderer\XMLRenderer
706
     */
707
    protected function createXmlRenderer()
708
    {
709
        return new XMLRenderer();
710
    }
711
712
    /**
713
     * @return \PHPMD\Renderer\TextRenderer
714
     */
715
    protected function createTextRenderer()
716
    {
717
        return new TextRenderer();
718
    }
719
720
    /**
721
     * @return \PHPMD\Renderer\AnsiRenderer
722
     */
723
    protected function createAnsiRenderer()
724
    {
725
        return new AnsiRenderer();
726
    }
727
728
    /**
729
     * @return \PHPMD\Renderer\GitLabRenderer
730
     */
731
    protected function createGitLabRenderer()
732
    {
733
        return new GitLabRenderer();
734
    }
735
736
    /**
737
     * @return \PHPMD\Renderer\GitHubRenderer
738
     */
739
    protected function createGitHubRenderer()
740
    {
741
        return new GitHubRenderer();
742
    }
743
744
    /**
745
     * @return \PHPMD\Renderer\HTMLRenderer
746
     */
747
    protected function createHtmlRenderer()
748
    {
749
        return new HTMLRenderer($this->extraLineInExcerpt);
750
    }
751
752
    /**
753
     * @return \PHPMD\Renderer\JSONRenderer
754
     */
755
    protected function createJsonRenderer()
756
    {
757
        return new JSONRenderer();
758
    }
759
760
    /**
761
     * @return \PHPMD\Renderer\CheckStyleRenderer
762
     */
763
    protected function createCheckStyleRenderer()
764
    {
765
        return new CheckStyleRenderer();
766
    }
767
768
    /**
769
     * @return \PHPMD\Renderer\SARIFRenderer
770
     */
771
    protected function createSarifRenderer()
772
    {
773
        return new SARIFRenderer();
774
    }
775
776
    /**
777
     * @return \PHPMD\AbstractRenderer
778
     * @throws InvalidArgumentException
779
     */
780
    protected function createCustomRenderer()
781
    {
782
        if ('' === $this->reportFormat) {
783
            throw new InvalidArgumentException(
784
                'Can\'t create report with empty format.',
785
                self::INPUT_ERROR
786
            );
787
        }
788
789
        if (class_exists($this->reportFormat)) {
790
            return new $this->reportFormat();
791
        }
792
793
        // Try to load a custom renderer
794
        $fileName = strtr($this->reportFormat, '_\\', '//') . '.php';
795
796
        $fileHandle = @fopen($fileName, 'r', true);
797
        if (is_resource($fileHandle) === false) {
798
            throw new InvalidArgumentException(
799
                sprintf(
800
                    'Can\'t find the custom report class: %s',
801
                    $this->reportFormat
802
                ),
803
                self::INPUT_ERROR
804
            );
805
        }
806
        @fclose($fileHandle);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fclose(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

806
        /** @scrutinizer ignore-unhandled */ @fclose($fileHandle);

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...
Bug introduced by
$fileHandle of type false is incompatible with the type resource expected by parameter $stream of fclose(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

806
        @fclose(/** @scrutinizer ignore-type */ $fileHandle);
Loading history...
807
808
        include_once $fileName;
809
810
        return new $this->reportFormat();
811
    }
812
813
    /**
814
     * Returns usage information for the PHPMD command line interface.
815
     *
816
     * @return string
817
     */
818
    public function usage()
819
    {
820
        $availableRenderers = $this->getListOfAvailableRenderers();
821
        $noRenderers = ($availableRenderers === null);
822
823
        return 'Mandatory arguments:' . \PHP_EOL .
824
            '1) A php source code filename or directory. Can be a comma-' .
825
            'separated string, glob pattern, or "-" to scan stdin' . \PHP_EOL .
826
            '2) A report format' . \PHP_EOL .
827
            '3) A ruleset filename or a comma-separated string of ruleset' .
828
            'filenames' . \PHP_EOL . \PHP_EOL .
829
            'Example: phpmd /path/to/source format ruleset' . \PHP_EOL . \PHP_EOL .
830
            ($noRenderers ? 'No available formats' : 'Available formats: ' . $availableRenderers) . '.' . \PHP_EOL .
831
            'Available rulesets: ' . implode(', ', $this->availableRuleSets) . '.' . \PHP_EOL . \PHP_EOL .
832
            'Optional arguments that may be put after the mandatory arguments:' .
833
            \PHP_EOL .
834
            '--verbose, -v, -vv, -vvv: Show debug information.' . \PHP_EOL .
835
            '--minimum-priority: rule priority threshold; rules with lower ' .
836
            'priority than this will not be used' . \PHP_EOL .
837
            '--report-file: send report output to a file; default to STDOUT' .
838
            \PHP_EOL .
839
            '--error-file: send errors (other than reported violations) ' .
840
            'output to a file; default to STDERR' .
841
            \PHP_EOL .
842
            '--suffixes: comma-separated string of valid source code ' .
843
            'filename extensions, e.g. php,phtml' . \PHP_EOL .
844
            '--exclude: comma-separated string of patterns that are used to ' .
845
            'ignore directories. Use asterisks to exclude by pattern. ' .
846
            'For example *src/foo/*.php or *src/foo/*' . \PHP_EOL .
847
            '--strict: also report those nodes with a @SuppressWarnings ' .
848
            'annotation' . \PHP_EOL .
849
            '--ignore-errors-on-exit: will exit with a zero code, ' .
850
            'even on error' . \PHP_EOL .
851
            '--ignore-violations-on-exit: will exit with a zero code, ' .
852
            'even if any violations are found' . \PHP_EOL .
853
            '--cache: will enable the result cache.' . \PHP_EOL .
854
            '--cache-file: instead of the default .phpmd.result-cache.php' .
855
            ' will use this file as result cache file path.' . \PHP_EOL .
856
            '--cache-strategy: sets the caching strategy to determine if' .
857
            ' a file is still fresh. Either `content` to base it on the ' .
858
            'file contents, or `timestamp` to base it on the file modified ' .
859
            'timestamp' . \PHP_EOL .
860
            '--generate-baseline: will generate a phpmd.baseline.xml next ' .
861
            'to the first ruleset file location' . \PHP_EOL .
862
            '--update-baseline: will remove any non-existing violations from the phpmd.baseline.xml' . \PHP_EOL .
863
            '--baseline-file: a custom location of the baseline file' . \PHP_EOL .
864
            '--color: enable color in output' . \PHP_EOL .
865
            '--extra-line-in-excerpt: Specify how many extra lines are added ' .
866
            'to a code snippet in html format' . \PHP_EOL .
867
            '--: Explicit argument separator: Anything after "--" will be read as an argument even if' .
868
            'it starts with "-" or matches the name of an option' . \PHP_EOL;
869
    }
870
871
    /**
872
     * Get a list of available renderers
873
     *
874
     * @return string|null The list of renderers found separated by comma, or null if none.
875
     */
876
    protected function getListOfAvailableRenderers()
877
    {
878
        $renderersDirPathName = __DIR__ . '/../Renderer';
879
        $renderers            = array();
880
881
        foreach (scandir($renderersDirPathName) as $rendererFileName) {
882
            $rendererName = array();
883
            if (preg_match('/^(\w+)Renderer.php$/i', $rendererFileName, $rendererName)) {
884
                $renderers[] = strtolower($rendererName[1]);
885
            }
886
        }
887
888
        sort($renderers);
889
890
        return implode(', ', $renderers) ?: null;
891
    }
892
893
    /**
894
     * Logs a deprecated option to the current user interface.
895
     *
896
     * @param string $deprecatedName
897
     * @param string $newName
898
     * @return void
899
     */
900
    protected function logDeprecated($deprecatedName, $newName)
901
    {
902
        $this->deprecations[] = sprintf(
903
            'The --%s option is deprecated, please use --%s instead.',
904
            $deprecatedName,
905
            $newName
906
        );
907
    }
908
909
    /**
910
     * This method takes the given input file, reads the newline separated paths
911
     * from that file and creates a comma separated string of the file paths. If
912
     * the given <b>$inputFile</b> not exists, this method will throw an
913
     * exception.
914
     *
915
     * @param string $inputFile Specified input file name.
916
     * @return string
917
     * @throws InvalidArgumentException If the specified input file does not exist.
918
     * @since 1.1.0
919
     */
920
    protected function readInputFile($inputFile)
921
    {
922
        if (file_exists($inputFile)) {
923
            return implode(',', array_map('trim', file($inputFile)));
924
        }
925
        throw new InvalidArgumentException("Input file '{$inputFile}' not exists.");
926
    }
927
928
    /**
929
     * Throw an exception if a boolean option has a value (is followed by equal).
930
     *
931
     * @param string[] $equalChunk The CLI parameter split in 2 by "=" sign
932
     *
933
     * @throws InvalidArgumentException if a boolean option has a value (is followed by equal)
934
     */
935
    private function refuseValue(array $equalChunk)
936
    {
937
        if (count($equalChunk) > 1) {
938
            throw new InvalidArgumentException($equalChunk[0] . ' option does not accept a value');
939
        }
940
    }
941
942
    /**
943
     * Return value for an option either what is after "=" sign if present, else take the next CLI parameter.
944
     *
945
     * @param string[] $equalChunk The CLI parameter split in 2 by "=" sign
946
     * @param string[] &$args      The remaining CLI parameters not yet parsed
947
     *
948
     * @return string|null
949
     */
950
    private function readValue(array $equalChunk, array &$args)
951
    {
952
        if (count($equalChunk) > 1) {
953
            return $equalChunk[1];
954
        }
955
956
        return array_shift($args);
957
    }
958
}
959