• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPCSStandards / PHP_CodeSniffer / 12395563686

18 Dec 2024 02:58PM UTC coverage: 78.197% (+0.008%) from 78.189%
12395563686

Pull #771

github

web-flow
Merge 6bb661dcd into e8edff7e7
Pull Request #771: Config: display user-friendly message when invalid generator is passed

3 of 11 new or added lines in 1 file covered. (27.27%)

206 existing lines in 1 file now uncovered.

24428 of 31239 relevant lines covered (78.2%)

65.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

28.75
/src/Config.php
1
<?php
2
/**
3
 * Stores the configuration used to run PHPCS and PHPCBF.
4
 *
5
 * Parses the command line to determine user supplied values
6
 * and provides functions to access data stored in config files.
7
 *
8
 * @author    Greg Sherwood <gsherwood@squiz.net>
9
 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
10
 * @license   https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
11
 */
12

13
namespace PHP_CodeSniffer;
14

15
use Exception;
16
use Phar;
17
use PHP_CodeSniffer\Exceptions\DeepExitException;
18
use PHP_CodeSniffer\Exceptions\RuntimeException;
19
use PHP_CodeSniffer\Util\Common;
20
use PHP_CodeSniffer\Util\Help;
21
use PHP_CodeSniffer\Util\Standards;
22

23
/**
24
 * Stores the configuration used to run PHPCS and PHPCBF.
25
 *
26
 * @property string[]   $files           The files and directories to check.
27
 * @property string[]   $standards       The standards being used for checking.
28
 * @property int        $verbosity       How verbose the output should be.
29
 *                                       0: no unnecessary output
30
 *                                       1: basic output for files being checked
31
 *                                       2: ruleset and file parsing output
32
 *                                       3: sniff execution output
33
 * @property bool       $interactive     Enable interactive checking mode.
34
 * @property int        $parallel        Check files in parallel.
35
 * @property bool       $cache           Enable the use of the file cache.
36
 * @property string     $cacheFile       Path to the file where the cache data should be written
37
 * @property bool       $colors          Display colours in output.
38
 * @property bool       $explain         Explain the coding standards.
39
 * @property bool       $local           Process local files in directories only (no recursion).
40
 * @property bool       $showSources     Show sniff source codes in report output.
41
 * @property bool       $showProgress    Show basic progress information while running.
42
 * @property bool       $quiet           Quiet mode; disables progress and verbose output.
43
 * @property bool       $annotations     Process phpcs: annotations.
44
 * @property int        $tabWidth        How many spaces each tab is worth.
45
 * @property string     $encoding        The encoding of the files being checked.
46
 * @property string[]   $sniffs          The sniffs that should be used for checking.
47
 *                                       If empty, all sniffs in the supplied standards will be used.
48
 * @property string[]   $exclude         The sniffs that should be excluded from checking.
49
 *                                       If empty, all sniffs in the supplied standards will be used.
50
 * @property string[]   $ignored         Regular expressions used to ignore files and folders during checking.
51
 * @property string     $reportFile      A file where the report output should be written.
52
 * @property string     $generator       The documentation generator to use.
53
 * @property string     $filter          The filter to use for the run.
54
 * @property string[]   $bootstrap       One of more files to include before the run begins.
55
 * @property int|string $reportWidth     The maximum number of columns that reports should use for output.
56
 *                                       Set to "auto" for have this value changed to the width of the terminal.
57
 * @property int        $errorSeverity   The minimum severity an error must have to be displayed.
58
 * @property int        $warningSeverity The minimum severity a warning must have to be displayed.
59
 * @property bool       $recordErrors    Record the content of error messages as well as error counts.
60
 * @property string     $suffix          A suffix to add to fixed files.
61
 * @property string     $basepath        A file system location to strip from the paths of files shown in reports.
62
 * @property bool       $stdin           Read content from STDIN instead of supplied files.
63
 * @property string     $stdinContent    Content passed directly to PHPCS on STDIN.
64
 * @property string     $stdinPath       The path to use for content passed on STDIN.
65
 * @property bool       $trackTime       Whether or not to track sniff run time.
66
 *
67
 * @property array<string, string>      $extensions File extensions that should be checked, and what tokenizer to use.
68
 *                                                  E.g., array('inc' => 'PHP');
69
 * @property array<string, string|null> $reports    The reports to use for printing output after the run.
70
 *                                                  The format of the array is:
71
 *                                                      array(
72
 *                                                          'reportName1' => 'outputFile',
73
 *                                                          'reportName2' => null,
74
 *                                                      );
75
 *                                                  If the array value is NULL, the report will be written to the screen.
76
 *
77
 * @property string[] $unknown Any arguments gathered on the command line that are unknown to us.
78
 *                             E.g., using `phpcs -c` will give array('c');
79
 */
80
class Config
81
{
82

83
    /**
84
     * The current version.
85
     *
86
     * @var string
87
     */
88
    const VERSION = '3.11.3';
89

90
    /**
91
     * Package stability; either stable, beta or alpha.
92
     *
93
     * @var string
94
     */
95
    const STABILITY = 'stable';
96

97
    /**
98
     * Default report width when no report width is provided and 'auto' does not yield a valid width.
99
     *
100
     * @var int
101
     */
102
    const DEFAULT_REPORT_WIDTH = 80;
103

104
    /**
105
     * An array of settings that PHPCS and PHPCBF accept.
106
     *
107
     * This array is not meant to be accessed directly. Instead, use the settings
108
     * as if they are class member vars so the __get() and __set() magic methods
109
     * can be used to validate the values. For example, to set the verbosity level to
110
     * level 2, use $this->verbosity = 2; instead of accessing this property directly.
111
     *
112
     * Each of these settings is described in the class comment property list.
113
     *
114
     * @var array<string, mixed>
115
     */
116
    private $settings = [
117
        'files'           => null,
118
        'standards'       => null,
119
        'verbosity'       => null,
120
        'interactive'     => null,
121
        'parallel'        => null,
122
        'cache'           => null,
123
        'cacheFile'       => null,
124
        'colors'          => null,
125
        'explain'         => null,
126
        'local'           => null,
127
        'showSources'     => null,
128
        'showProgress'    => null,
129
        'quiet'           => null,
130
        'annotations'     => null,
131
        'tabWidth'        => null,
132
        'encoding'        => null,
133
        'extensions'      => null,
134
        'sniffs'          => null,
135
        'exclude'         => null,
136
        'ignored'         => null,
137
        'reportFile'      => null,
138
        'generator'       => null,
139
        'filter'          => null,
140
        'bootstrap'       => null,
141
        'reports'         => null,
142
        'basepath'        => null,
143
        'reportWidth'     => null,
144
        'errorSeverity'   => null,
145
        'warningSeverity' => null,
146
        'recordErrors'    => null,
147
        'suffix'          => null,
148
        'stdin'           => null,
149
        'stdinContent'    => null,
150
        'stdinPath'       => null,
151
        'trackTime'       => null,
152
        'unknown'         => null,
153
    ];
154

155
    /**
156
     * Whether or not to kill the process when an unknown command line arg is found.
157
     *
158
     * If FALSE, arguments that are not command line options or file/directory paths
159
     * will be ignored and execution will continue. These values will be stored in
160
     * $this->unknown.
161
     *
162
     * @var boolean
163
     */
164
    public $dieOnUnknownArg;
165

166
    /**
167
     * The current command line arguments we are processing.
168
     *
169
     * @var string[]
170
     */
171
    private $cliArgs = [];
172

173
    /**
174
     * Command line values that the user has supplied directly.
175
     *
176
     * @var array<string, true|array<string, true>>
177
     */
178
    private static $overriddenDefaults = [];
179

180
    /**
181
     * Config file data that has been loaded for the run.
182
     *
183
     * @var array<string, string>
184
     */
185
    private static $configData = null;
186

187
    /**
188
     * The full path to the config data file that has been loaded.
189
     *
190
     * @var string
191
     */
192
    private static $configDataFile = null;
193

194
    /**
195
     * Automatically discovered executable utility paths.
196
     *
197
     * @var array<string, string>
198
     */
199
    private static $executablePaths = [];
200

201
    /**
202
     * A list of valid generators.
203
     *
204
     * - Keys: lowercase version of the generator name.
205
     * - Values: name of the generator PHP class.
206
     *
207
     * Note: once support for PHP < 5.6 is dropped, this property should be refactored into a class
208
     * constant.
209
     *
210
     * @var array<string, string>
211
     */
212
    private static $validGenerators = [
213
        'text'     => 'Text',
214
        'html'     => 'HTML',
215
        'markdown' => 'Markdown',
216
    ];
217

218

219
    /**
220
     * Get the value of an inaccessible property.
221
     *
222
     * @param string $name The name of the property.
223
     *
224
     * @return mixed
225
     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the setting name is invalid.
226
     */
227
    public function __get($name)
48✔
228
    {
229
        if (array_key_exists($name, $this->settings) === false) {
48✔
UNCOV
230
            throw new RuntimeException("ERROR: unable to get value of property \"$name\"");
×
231
        }
232

233
        // Figure out what the terminal width needs to be for "auto".
234
        if ($name === 'reportWidth' && $this->settings[$name] === 'auto') {
48✔
235
            if (function_exists('shell_exec') === true) {
9✔
236
                $dimensions = shell_exec('stty size 2>&1');
9✔
237
                if (is_string($dimensions) === true && preg_match('|\d+ (\d+)|', $dimensions, $matches) === 1) {
9✔
UNCOV
238
                    $this->settings[$name] = (int) $matches[1];
×
239
                }
240
            }
3✔
241

242
            if ($this->settings[$name] === 'auto') {
9✔
243
                // If shell_exec wasn't available or didn't yield a usable value, set to the default.
244
                // This will prevent subsequent retrievals of the reportWidth from making another call to stty.
245
                $this->settings[$name] = self::DEFAULT_REPORT_WIDTH;
9✔
246
            }
3✔
247
        }
3✔
248

249
        return $this->settings[$name];
48✔
250

251
    }//end __get()
252

253

254
    /**
255
     * Set the value of an inaccessible property.
256
     *
257
     * @param string $name  The name of the property.
258
     * @param mixed  $value The value of the property.
259
     *
260
     * @return void
261
     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the setting name is invalid.
262
     */
263
    public function __set($name, $value)
48✔
264
    {
265
        if (array_key_exists($name, $this->settings) === false) {
48✔
UNCOV
266
            throw new RuntimeException("Can't __set() $name; setting doesn't exist");
×
267
        }
268

269
        switch ($name) {
16✔
270
        case 'reportWidth' :
48✔
271
            if (is_string($value) === true && $value === 'auto') {
48✔
272
                // Nothing to do. Leave at 'auto'.
273
                break;
48✔
274
            }
275

276
            if (is_int($value) === true) {
39✔
277
                $value = abs($value);
6✔
278
            } else if (is_string($value) === true && preg_match('`^\d+$`', $value) === 1) {
35✔
279
                $value = (int) $value;
15✔
280
            } else {
5✔
281
                $value = self::DEFAULT_REPORT_WIDTH;
18✔
282
            }
283
            break;
39✔
284

285
        case 'standards' :
48✔
286
            $cleaned = [];
48✔
287

288
            // Check if the standard name is valid, or if the case is invalid.
289
            $installedStandards = Standards::getInstalledStandards();
48✔
290
            foreach ($value as $standard) {
48✔
291
                foreach ($installedStandards as $validStandard) {
48✔
292
                    if (strtolower($standard) === strtolower($validStandard)) {
48✔
293
                        $standard = $validStandard;
48✔
294
                        break;
48✔
295
                    }
296
                }
16✔
297

298
                $cleaned[] = $standard;
48✔
299
            }
16✔
300

301
            $value = $cleaned;
48✔
302
            break;
48✔
303

304
        // Only track time when explicitly needed.
305
        case 'verbosity':
48✔
306
            if ($value > 2) {
48✔
UNCOV
307
                $this->settings['trackTime'] = true;
×
308
            }
309
            break;
48✔
310
        case 'reports':
48✔
311
            $reports = array_change_key_case($value, CASE_LOWER);
48✔
312
            if (array_key_exists('performance', $reports) === true) {
48✔
UNCOV
313
                $this->settings['trackTime'] = true;
×
314
            }
315
            break;
48✔
316

317
        default :
16✔
318
            // No validation required.
319
            break;
48✔
320
        }//end switch
16✔
321

322
        $this->settings[$name] = $value;
48✔
323

324
    }//end __set()
32✔
325

326

327
    /**
328
     * Check if the value of an inaccessible property is set.
329
     *
330
     * @param string $name The name of the property.
331
     *
332
     * @return bool
333
     */
334
    public function __isset($name)
×
335
    {
UNCOV
336
        return isset($this->settings[$name]);
×
337

338
    }//end __isset()
339

340

341
    /**
342
     * Unset the value of an inaccessible property.
343
     *
344
     * @param string $name The name of the property.
345
     *
346
     * @return void
347
     */
348
    public function __unset($name)
×
349
    {
UNCOV
350
        $this->settings[$name] = null;
×
351

352
    }//end __unset()
353

354

355
    /**
356
     * Get the array of all config settings.
357
     *
358
     * @return array<string, mixed>
359
     */
360
    public function getSettings()
×
361
    {
UNCOV
362
        return $this->settings;
×
363

364
    }//end getSettings()
365

366

367
    /**
368
     * Set the array of all config settings.
369
     *
370
     * @param array<string, mixed> $settings The array of config settings.
371
     *
372
     * @return void
373
     */
374
    public function setSettings($settings)
×
375
    {
UNCOV
376
        return $this->settings = $settings;
×
377

378
    }//end setSettings()
379

380

381
    /**
382
     * Creates a Config object and populates it with command line values.
383
     *
384
     * @param array $cliArgs         An array of values gathered from CLI args.
385
     * @param bool  $dieOnUnknownArg Whether or not to kill the process when an
386
     *                               unknown command line arg is found.
387
     *
388
     * @return void
389
     */
390
    public function __construct(array $cliArgs=[], $dieOnUnknownArg=true)
×
391
    {
UNCOV
392
        if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
×
393
            // Let everything through during testing so that we can
394
            // make use of PHPUnit command line arguments as well.
395
            $this->dieOnUnknownArg = false;
×
396
        } else {
UNCOV
397
            $this->dieOnUnknownArg = $dieOnUnknownArg;
×
398
        }
399

400
        if (empty($cliArgs) === true) {
×
UNCOV
401
            $cliArgs = $_SERVER['argv'];
×
UNCOV
402
            array_shift($cliArgs);
×
403
        }
404

UNCOV
405
        $this->restoreDefaults();
×
406
        $this->setCommandLineValues($cliArgs);
×
407

UNCOV
408
        if (isset(self::$overriddenDefaults['standards']) === false) {
×
409
            // They did not supply a standard to use.
410
            // Look for a default ruleset in the current directory or higher.
UNCOV
411
            $currentDir = getcwd();
×
412

413
            $defaultFiles = [
UNCOV
414
                '.phpcs.xml',
×
415
                'phpcs.xml',
416
                '.phpcs.xml.dist',
417
                'phpcs.xml.dist',
418
            ];
419

420
            do {
421
                foreach ($defaultFiles as $defaultFilename) {
×
422
                    $default = $currentDir.DIRECTORY_SEPARATOR.$defaultFilename;
×
423
                    if (is_file($default) === true) {
×
UNCOV
424
                        $this->standards = [$default];
×
UNCOV
425
                        break(2);
×
426
                    }
427
                }
428

429
                $lastDir    = $currentDir;
×
UNCOV
430
                $currentDir = dirname($currentDir);
×
UNCOV
431
            } while ($currentDir !== '.' && $currentDir !== $lastDir && Common::isReadable($currentDir) === true);
×
432
        }//end if
433

UNCOV
434
        if (defined('STDIN') === false
×
435
            || stripos(PHP_OS, 'WIN') === 0
×
436
        ) {
UNCOV
437
            return;
×
438
        }
439

UNCOV
440
        $handle = fopen('php://stdin', 'r');
×
441

442
        // Check for content on STDIN.
443
        if ($this->stdin === true
×
UNCOV
444
            || (Common::isStdinATTY() === false
×
445
            && feof($handle) === false)
×
446
        ) {
UNCOV
447
            $readStreams = [$handle];
×
448
            $writeSteams = null;
×
449

UNCOV
450
            $fileContents = '';
×
451
            while (is_resource($handle) === true && feof($handle) === false) {
×
452
                // Set a timeout of 200ms.
UNCOV
453
                if (stream_select($readStreams, $writeSteams, $writeSteams, 0, 200000) === 0) {
×
UNCOV
454
                    break;
×
455
                }
456

UNCOV
457
                $fileContents .= fgets($handle);
×
458
            }
459

460
            if (trim($fileContents) !== '') {
×
461
                $this->stdin        = true;
×
462
                $this->stdinContent = $fileContents;
×
UNCOV
463
                self::$overriddenDefaults['stdin']        = true;
×
UNCOV
464
                self::$overriddenDefaults['stdinContent'] = true;
×
465
            }
466
        }//end if
467

UNCOV
468
        fclose($handle);
×
469

470
    }//end __construct()
471

472

473
    /**
474
     * Set the command line values.
475
     *
476
     * @param array $args An array of command line arguments to set.
477
     *
478
     * @return void
479
     */
480
    public function setCommandLineValues($args)
×
481
    {
UNCOV
482
        $this->cliArgs = $args;
×
483
        $numArgs       = count($args);
×
484

485
        for ($i = 0; $i < $numArgs; $i++) {
×
486
            $arg = $this->cliArgs[$i];
×
UNCOV
487
            if ($arg === '') {
×
UNCOV
488
                continue;
×
489
            }
490

UNCOV
491
            if ($arg[0] === '-') {
×
492
                if ($arg === '-') {
×
493
                    // Asking to read from STDIN.
494
                    $this->stdin = true;
×
UNCOV
495
                    self::$overriddenDefaults['stdin'] = true;
×
UNCOV
496
                    continue;
×
497
                }
498

499
                if ($arg === '--') {
×
500
                    // Empty argument, ignore it.
UNCOV
501
                    continue;
×
502
                }
503

UNCOV
504
                if ($arg[1] === '-') {
×
505
                    $this->processLongArgument(substr($arg, 2), $i);
×
506
                } else {
507
                    $switches = str_split($arg);
×
508
                    foreach ($switches as $switch) {
×
UNCOV
509
                        if ($switch === '-') {
×
UNCOV
510
                            continue;
×
511
                        }
512

UNCOV
513
                        $this->processShortArgument($switch, $i);
×
514
                    }
515
                }
516
            } else {
UNCOV
517
                $this->processUnknownArgument($arg, $i);
×
518
            }//end if
519
        }//end for
520

521
    }//end setCommandLineValues()
522

523

524
    /**
525
     * Restore default values for all possible command line arguments.
526
     *
527
     * @return void
528
     */
529
    public function restoreDefaults()
9✔
530
    {
531
        $this->files           = [];
9✔
532
        $this->standards       = ['PEAR'];
9✔
533
        $this->verbosity       = 0;
9✔
534
        $this->interactive     = false;
9✔
535
        $this->cache           = false;
9✔
536
        $this->cacheFile       = null;
9✔
537
        $this->colors          = false;
9✔
538
        $this->explain         = false;
9✔
539
        $this->local           = false;
9✔
540
        $this->showSources     = false;
9✔
541
        $this->showProgress    = false;
9✔
542
        $this->quiet           = false;
9✔
543
        $this->annotations     = true;
9✔
544
        $this->parallel        = 1;
9✔
545
        $this->tabWidth        = 0;
9✔
546
        $this->encoding        = 'utf-8';
9✔
547
        $this->extensions      = [
9✔
548
            'php' => 'PHP',
6✔
549
            'inc' => 'PHP',
6✔
550
            'js'  => 'JS',
6✔
551
            'css' => 'CSS',
6✔
552
        ];
3✔
553
        $this->sniffs          = [];
9✔
554
        $this->exclude         = [];
9✔
555
        $this->ignored         = [];
9✔
556
        $this->reportFile      = null;
9✔
557
        $this->generator       = null;
9✔
558
        $this->filter          = null;
9✔
559
        $this->bootstrap       = [];
9✔
560
        $this->basepath        = null;
9✔
561
        $this->reports         = ['full' => null];
9✔
562
        $this->reportWidth     = 'auto';
9✔
563
        $this->errorSeverity   = 5;
9✔
564
        $this->warningSeverity = 5;
9✔
565
        $this->recordErrors    = true;
9✔
566
        $this->suffix          = '';
9✔
567
        $this->stdin           = false;
9✔
568
        $this->stdinContent    = null;
9✔
569
        $this->stdinPath       = null;
9✔
570
        $this->trackTime       = false;
9✔
571
        $this->unknown         = [];
9✔
572

573
        $standard = self::getConfigData('default_standard');
9✔
574
        if ($standard !== null) {
9✔
575
            $this->standards = explode(',', $standard);
6✔
576
        }
2✔
577

578
        $reportFormat = self::getConfigData('report_format');
9✔
579
        if ($reportFormat !== null) {
9✔
UNCOV
580
            $this->reports = [$reportFormat => null];
×
581
        }
582

583
        $tabWidth = self::getConfigData('tab_width');
9✔
584
        if ($tabWidth !== null) {
9✔
UNCOV
585
            $this->tabWidth = (int) $tabWidth;
×
586
        }
587

588
        $encoding = self::getConfigData('encoding');
9✔
589
        if ($encoding !== null) {
9✔
UNCOV
590
            $this->encoding = strtolower($encoding);
×
591
        }
592

593
        $severity = self::getConfigData('severity');
9✔
594
        if ($severity !== null) {
9✔
UNCOV
595
            $this->errorSeverity   = (int) $severity;
×
UNCOV
596
            $this->warningSeverity = (int) $severity;
×
597
        }
598

599
        $severity = self::getConfigData('error_severity');
9✔
600
        if ($severity !== null) {
9✔
UNCOV
601
            $this->errorSeverity = (int) $severity;
×
602
        }
603

604
        $severity = self::getConfigData('warning_severity');
9✔
605
        if ($severity !== null) {
9✔
UNCOV
606
            $this->warningSeverity = (int) $severity;
×
607
        }
608

609
        $showWarnings = self::getConfigData('show_warnings');
9✔
610
        if ($showWarnings !== null) {
9✔
611
            $showWarnings = (bool) $showWarnings;
3✔
612
            if ($showWarnings === false) {
3✔
613
                $this->warningSeverity = 0;
3✔
614
            }
1✔
615
        }
1✔
616

617
        $reportWidth = self::getConfigData('report_width');
9✔
618
        if ($reportWidth !== null) {
9✔
619
            $this->reportWidth = $reportWidth;
3✔
620
        }
1✔
621

622
        $showProgress = self::getConfigData('show_progress');
9✔
623
        if ($showProgress !== null) {
9✔
UNCOV
624
            $this->showProgress = (bool) $showProgress;
×
625
        }
626

627
        $quiet = self::getConfigData('quiet');
9✔
628
        if ($quiet !== null) {
9✔
UNCOV
629
            $this->quiet = (bool) $quiet;
×
630
        }
631

632
        $colors = self::getConfigData('colors');
9✔
633
        if ($colors !== null) {
9✔
UNCOV
634
            $this->colors = (bool) $colors;
×
635
        }
636

637
        if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
9✔
638
            $cache = self::getConfigData('cache');
×
UNCOV
639
            if ($cache !== null) {
×
UNCOV
640
                $this->cache = (bool) $cache;
×
641
            }
642

643
            $parallel = self::getConfigData('parallel');
×
UNCOV
644
            if ($parallel !== null) {
×
UNCOV
645
                $this->parallel = max((int) $parallel, 1);
×
646
            }
647
        }
648

649
    }//end restoreDefaults()
6✔
650

651

652
    /**
653
     * Processes a short (-e) command line argument.
654
     *
655
     * @param string $arg The command line argument.
656
     * @param int    $pos The position of the argument on the command line.
657
     *
658
     * @return void
659
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException
660
     */
UNCOV
661
    public function processShortArgument($arg, $pos)
×
662
    {
663
        switch ($arg) {
664
        case 'h':
×
665
        case '?':
×
666
            ob_start();
×
667
            $this->printUsage();
×
668
            $output = ob_get_contents();
×
669
            ob_end_clean();
×
670
            throw new DeepExitException($output, 0);
×
671
        case 'i' :
×
672
            ob_start();
×
673
            Standards::printInstalledStandards();
×
674
            $output = ob_get_contents();
×
675
            ob_end_clean();
×
676
            throw new DeepExitException($output, 0);
×
UNCOV
677
        case 'v' :
×
678
            if ($this->quiet === true) {
×
679
                // Ignore when quiet mode is enabled.
UNCOV
680
                break;
×
681
            }
682

683
            $this->verbosity++;
×
684
            self::$overriddenDefaults['verbosity'] = true;
×
685
            break;
×
686
        case 'l' :
×
687
            $this->local = true;
×
688
            self::$overriddenDefaults['local'] = true;
×
689
            break;
×
690
        case 's' :
×
691
            $this->showSources = true;
×
692
            self::$overriddenDefaults['showSources'] = true;
×
693
            break;
×
694
        case 'a' :
×
695
            $this->interactive = true;
×
696
            self::$overriddenDefaults['interactive'] = true;
×
697
            break;
×
698
        case 'e':
×
699
            $this->explain = true;
×
700
            self::$overriddenDefaults['explain'] = true;
×
701
            break;
×
UNCOV
702
        case 'p' :
×
703
            if ($this->quiet === true) {
×
704
                // Ignore when quiet mode is enabled.
UNCOV
705
                break;
×
706
            }
707

708
            $this->showProgress = true;
×
709
            self::$overriddenDefaults['showProgress'] = true;
×
UNCOV
710
            break;
×
711
        case 'q' :
×
712
            // Quiet mode disables a few other settings as well.
713
            $this->quiet        = true;
×
UNCOV
714
            $this->showProgress = false;
×
715
            $this->verbosity    = 0;
×
716

717
            self::$overriddenDefaults['quiet'] = true;
×
718
            break;
×
719
        case 'm' :
×
720
            $this->recordErrors = false;
×
721
            self::$overriddenDefaults['recordErrors'] = true;
×
722
            break;
×
723
        case 'd' :
×
724
            $ini = explode('=', $this->cliArgs[($pos + 1)]);
×
725
            $this->cliArgs[($pos + 1)] = '';
×
UNCOV
726
            if (isset($ini[1]) === true) {
×
727
                ini_set($ini[0], $ini[1]);
×
728
            } else {
729
                ini_set($ini[0], true);
×
730
            }
731
            break;
×
732
        case 'n' :
×
733
            if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
×
UNCOV
734
                $this->warningSeverity = 0;
×
735
                self::$overriddenDefaults['warningSeverity'] = true;
×
736
            }
737
            break;
×
738
        case 'w' :
×
739
            if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
×
UNCOV
740
                $this->warningSeverity = $this->errorSeverity;
×
741
                self::$overriddenDefaults['warningSeverity'] = true;
×
742
            }
743
            break;
×
744
        default:
745
            if ($this->dieOnUnknownArg === false) {
×
746
                $unknown       = $this->unknown;
×
UNCOV
747
                $unknown[]     = $arg;
×
748
                $this->unknown = $unknown;
×
749
            } else {
UNCOV
750
                $this->processUnknownArgument('-'.$arg, $pos);
×
751
            }
752
        }//end switch
753

754
    }//end processShortArgument()
755

756

757
    /**
758
     * Processes a long (--example) command-line argument.
759
     *
760
     * @param string $arg The command line argument.
761
     * @param int    $pos The position of the argument on the command line.
762
     *
763
     * @return void
764
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException
765
     */
766
    public function processLongArgument($arg, $pos)
93✔
767
    {
768
        switch ($arg) {
31✔
769
        case 'help':
93✔
770
            ob_start();
×
771
            $this->printUsage();
×
772
            $output = ob_get_contents();
×
UNCOV
773
            ob_end_clean();
×
774
            throw new DeepExitException($output, 0);
×
775
        case 'version':
93✔
776
            $output  = 'PHP_CodeSniffer version '.self::VERSION.' ('.self::STABILITY.') ';
×
UNCOV
777
            $output .= 'by Squiz and PHPCSStandards'.PHP_EOL;
×
778
            throw new DeepExitException($output, 0);
×
779
        case 'colors':
93✔
UNCOV
780
            if (isset(self::$overriddenDefaults['colors']) === true) {
×
UNCOV
781
                break;
×
782
            }
783

784
            $this->colors = true;
×
UNCOV
785
            self::$overriddenDefaults['colors'] = true;
×
786
            break;
×
787
        case 'no-colors':
93✔
UNCOV
788
            if (isset(self::$overriddenDefaults['colors']) === true) {
×
UNCOV
789
                break;
×
790
            }
791

792
            $this->colors = false;
×
UNCOV
793
            self::$overriddenDefaults['colors'] = true;
×
794
            break;
×
795
        case 'cache':
93✔
UNCOV
796
            if (isset(self::$overriddenDefaults['cache']) === true) {
×
UNCOV
797
                break;
×
798
            }
799

800
            if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
×
UNCOV
801
                $this->cache = true;
×
802
                self::$overriddenDefaults['cache'] = true;
×
803
            }
804
            break;
×
805
        case 'no-cache':
93✔
UNCOV
806
            if (isset(self::$overriddenDefaults['cache']) === true) {
×
UNCOV
807
                break;
×
808
            }
809

810
            $this->cache = false;
×
UNCOV
811
            self::$overriddenDefaults['cache'] = true;
×
812
            break;
×
813
        case 'ignore-annotations':
93✔
UNCOV
814
            if (isset(self::$overriddenDefaults['annotations']) === true) {
×
UNCOV
815
                break;
×
816
            }
817

818
            $this->annotations = false;
×
UNCOV
819
            self::$overriddenDefaults['annotations'] = true;
×
820
            break;
×
821
        case 'config-set':
93✔
UNCOV
822
            if (isset($this->cliArgs[($pos + 1)]) === false
×
823
                || isset($this->cliArgs[($pos + 2)]) === false
×
824
            ) {
825
                $error  = 'ERROR: Setting a config option requires a name and value'.PHP_EOL.PHP_EOL;
×
UNCOV
826
                $error .= $this->printShortUsage(true);
×
UNCOV
827
                throw new DeepExitException($error, 3);
×
828
            }
829

830
            $key     = $this->cliArgs[($pos + 1)];
×
UNCOV
831
            $value   = $this->cliArgs[($pos + 2)];
×
UNCOV
832
            $current = self::getConfigData($key);
×
833

834
            try {
835
                $this->setConfigData($key, $value);
×
UNCOV
836
            } catch (Exception $e) {
×
UNCOV
837
                throw new DeepExitException($e->getMessage().PHP_EOL, 3);
×
838
            }
839

840
            $output = 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
×
841

UNCOV
842
            if ($current === null) {
×
843
                $output .= "Config value \"$key\" added successfully".PHP_EOL;
×
844
            } else {
845
                $output .= "Config value \"$key\" updated successfully; old value was \"$current\"".PHP_EOL;
×
846
            }
847
            throw new DeepExitException($output, 0);
×
848
        case 'config-delete':
93✔
849
            if (isset($this->cliArgs[($pos + 1)]) === false) {
×
850
                $error  = 'ERROR: Deleting a config option requires the name of the option'.PHP_EOL.PHP_EOL;
×
UNCOV
851
                $error .= $this->printShortUsage(true);
×
UNCOV
852
                throw new DeepExitException($error, 3);
×
853
            }
854

855
            $output = 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
×
856

857
            $key     = $this->cliArgs[($pos + 1)];
×
858
            $current = self::getConfigData($key);
×
UNCOV
859
            if ($current === null) {
×
UNCOV
860
                $output .= "Config value \"$key\" has not been set".PHP_EOL;
×
861
            } else {
862
                try {
863
                    $this->setConfigData($key, null);
×
UNCOV
864
                } catch (Exception $e) {
×
UNCOV
865
                    throw new DeepExitException($e->getMessage().PHP_EOL, 3);
×
866
                }
867

868
                $output .= "Config value \"$key\" removed successfully; old value was \"$current\"".PHP_EOL;
×
869
            }
870
            throw new DeepExitException($output, 0);
×
871
        case 'config-show':
93✔
872
            ob_start();
×
873
            $data = self::getAllConfigData();
×
874
            echo 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
×
875
            $this->printConfigData($data);
×
876
            $output = ob_get_contents();
×
UNCOV
877
            ob_end_clean();
×
878
            throw new DeepExitException($output, 0);
×
879
        case 'runtime-set':
93✔
UNCOV
880
            if (isset($this->cliArgs[($pos + 1)]) === false
×
881
                || isset($this->cliArgs[($pos + 2)]) === false
×
882
            ) {
883
                $error  = 'ERROR: Setting a runtime config option requires a name and value'.PHP_EOL.PHP_EOL;
×
UNCOV
884
                $error .= $this->printShortUsage(true);
×
UNCOV
885
                throw new DeepExitException($error, 3);
×
886
            }
887

888
            $key   = $this->cliArgs[($pos + 1)];
×
889
            $value = $this->cliArgs[($pos + 2)];
×
890
            $this->cliArgs[($pos + 1)] = '';
×
891
            $this->cliArgs[($pos + 2)] = '';
×
892
            self::setConfigData($key, $value, true);
×
UNCOV
893
            if (isset(self::$overriddenDefaults['runtime-set']) === false) {
×
UNCOV
894
                self::$overriddenDefaults['runtime-set'] = [];
×
895
            }
896

UNCOV
897
            self::$overriddenDefaults['runtime-set'][$key] = true;
×
UNCOV
898
            break;
×
899
        default:
31✔
900
            if (substr($arg, 0, 7) === 'sniffs=') {
93✔
901
                if (isset(self::$overriddenDefaults['sniffs']) === true) {
27✔
902
                    break;
3✔
903
                }
904

905
                $sniffs = explode(',', substr($arg, 7));
27✔
906
                foreach ($sniffs as $sniff) {
27✔
907
                    if (substr_count($sniff, '.') !== 2) {
27✔
908
                        $error  = 'ERROR: The specified sniff code "'.$sniff.'" is invalid'.PHP_EOL.PHP_EOL;
18✔
909
                        $error .= $this->printShortUsage(true);
18✔
910
                        throw new DeepExitException($error, 3);
18✔
911
                    }
912
                }
4✔
913

914
                $this->sniffs = $sniffs;
9✔
915
                self::$overriddenDefaults['sniffs'] = true;
9✔
916
            } else if (substr($arg, 0, 8) === 'exclude=') {
69✔
917
                if (isset(self::$overriddenDefaults['exclude']) === true) {
27✔
918
                    break;
3✔
919
                }
920

921
                $sniffs = explode(',', substr($arg, 8));
27✔
922
                foreach ($sniffs as $sniff) {
27✔
923
                    if (substr_count($sniff, '.') !== 2) {
27✔
924
                        $error  = 'ERROR: The specified sniff code "'.$sniff.'" is invalid'.PHP_EOL.PHP_EOL;
18✔
925
                        $error .= $this->printShortUsage(true);
18✔
926
                        throw new DeepExitException($error, 3);
18✔
927
                    }
928
                }
4✔
929

930
                $this->exclude = $sniffs;
9✔
931
                self::$overriddenDefaults['exclude'] = true;
9✔
932
            } else if (defined('PHP_CODESNIFFER_IN_TESTS') === false
42✔
933
                && substr($arg, 0, 6) === 'cache='
39✔
934
            ) {
13✔
935
                if ((isset(self::$overriddenDefaults['cache']) === true
×
UNCOV
936
                    && $this->cache === false)
×
937
                    || isset(self::$overriddenDefaults['cacheFile']) === true
×
938
                ) {
UNCOV
939
                    break;
×
940
                }
941

942
                // Turn caching on.
UNCOV
943
                $this->cache = true;
×
944
                self::$overriddenDefaults['cache'] = true;
×
945

UNCOV
946
                $this->cacheFile = Common::realpath(substr($arg, 6));
×
947

948
                // It may not exist and return false instead.
UNCOV
949
                if ($this->cacheFile === false) {
×
950
                    $this->cacheFile = substr($arg, 6);
×
951

952
                    $dir = dirname($this->cacheFile);
×
953
                    if (is_dir($dir) === false) {
×
954
                        $error  = 'ERROR: The specified cache file path "'.$this->cacheFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
×
UNCOV
955
                        $error .= $this->printShortUsage(true);
×
UNCOV
956
                        throw new DeepExitException($error, 3);
×
957
                    }
958

959
                    if ($dir === '.') {
×
960
                        // Passed cache file is a file in the current directory.
961
                        $this->cacheFile = getcwd().'/'.basename($this->cacheFile);
×
962
                    } else {
963
                        if ($dir[0] === '/') {
×
964
                            // An absolute path.
965
                            $dir = Common::realpath($dir);
×
966
                        } else {
UNCOV
967
                            $dir = Common::realpath(getcwd().'/'.$dir);
×
968
                        }
969

970
                        if ($dir !== false) {
×
971
                            // Cache file path is relative.
UNCOV
972
                            $this->cacheFile = $dir.'/'.basename($this->cacheFile);
×
973
                        }
974
                    }
975
                }//end if
976

977
                self::$overriddenDefaults['cacheFile'] = true;
×
978

979
                if (is_dir($this->cacheFile) === true) {
×
980
                    $error  = 'ERROR: The specified cache file path "'.$this->cacheFile.'" is a directory'.PHP_EOL.PHP_EOL;
×
UNCOV
981
                    $error .= $this->printShortUsage(true);
×
UNCOV
982
                    throw new DeepExitException($error, 3);
×
983
                }
984
            } else if (substr($arg, 0, 10) === 'bootstrap=') {
39✔
985
                $files     = explode(',', substr($arg, 10));
×
986
                $bootstrap = [];
×
987
                foreach ($files as $file) {
×
988
                    $path = Common::realpath($file);
×
989
                    if ($path === false) {
×
990
                        $error  = 'ERROR: The specified bootstrap file "'.$file.'" does not exist'.PHP_EOL.PHP_EOL;
×
UNCOV
991
                        $error .= $this->printShortUsage(true);
×
UNCOV
992
                        throw new DeepExitException($error, 3);
×
993
                    }
994

UNCOV
995
                    $bootstrap[] = $path;
×
996
                }
997

UNCOV
998
                $this->bootstrap = array_merge($this->bootstrap, $bootstrap);
×
999
                self::$overriddenDefaults['bootstrap'] = true;
×
1000
            } else if (substr($arg, 0, 10) === 'file-list=') {
39✔
1001
                $fileList = substr($arg, 10);
×
1002
                $path     = Common::realpath($fileList);
×
1003
                if ($path === false) {
×
1004
                    $error  = 'ERROR: The specified file list "'.$fileList.'" does not exist'.PHP_EOL.PHP_EOL;
×
UNCOV
1005
                    $error .= $this->printShortUsage(true);
×
UNCOV
1006
                    throw new DeepExitException($error, 3);
×
1007
                }
1008

1009
                $files = file($path);
×
UNCOV
1010
                foreach ($files as $inputFile) {
×
UNCOV
1011
                    $inputFile = trim($inputFile);
×
1012

1013
                    // Skip empty lines.
UNCOV
1014
                    if ($inputFile === '') {
×
UNCOV
1015
                        continue;
×
1016
                    }
1017

UNCOV
1018
                    $this->processFilePath($inputFile);
×
1019
                }
1020
            } else if (substr($arg, 0, 11) === 'stdin-path=') {
39✔
UNCOV
1021
                if (isset(self::$overriddenDefaults['stdinPath']) === true) {
×
UNCOV
1022
                    break;
×
1023
                }
1024

UNCOV
1025
                $this->stdinPath = Common::realpath(substr($arg, 11));
×
1026

1027
                // It may not exist and return false instead, so use whatever they gave us.
UNCOV
1028
                if ($this->stdinPath === false) {
×
UNCOV
1029
                    $this->stdinPath = trim(substr($arg, 11));
×
1030
                }
1031

1032
                self::$overriddenDefaults['stdinPath'] = true;
×
1033
            } else if (PHP_CODESNIFFER_CBF === false && substr($arg, 0, 12) === 'report-file=') {
39✔
UNCOV
1034
                if (isset(self::$overriddenDefaults['reportFile']) === true) {
×
UNCOV
1035
                    break;
×
1036
                }
1037

UNCOV
1038
                $this->reportFile = Common::realpath(substr($arg, 12));
×
1039

1040
                // It may not exist and return false instead.
UNCOV
1041
                if ($this->reportFile === false) {
×
1042
                    $this->reportFile = substr($arg, 12);
×
1043

1044
                    $dir = Common::realpath(dirname($this->reportFile));
×
1045
                    if (is_dir($dir) === false) {
×
1046
                        $error  = 'ERROR: The specified report file path "'.$this->reportFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
×
UNCOV
1047
                        $error .= $this->printShortUsage(true);
×
UNCOV
1048
                        throw new DeepExitException($error, 3);
×
1049
                    }
1050

UNCOV
1051
                    $this->reportFile = $dir.'/'.basename($this->reportFile);
×
1052
                }//end if
1053

1054
                self::$overriddenDefaults['reportFile'] = true;
×
1055

1056
                if (is_dir($this->reportFile) === true) {
×
1057
                    $error  = 'ERROR: The specified report file path "'.$this->reportFile.'" is a directory'.PHP_EOL.PHP_EOL;
×
UNCOV
1058
                    $error .= $this->printShortUsage(true);
×
UNCOV
1059
                    throw new DeepExitException($error, 3);
×
1060
                }
1061
            } else if (substr($arg, 0, 13) === 'report-width=') {
39✔
1062
                if (isset(self::$overriddenDefaults['reportWidth']) === true) {
9✔
1063
                    break;
3✔
1064
                }
1065

1066
                $this->reportWidth = substr($arg, 13);
9✔
1067
                self::$overriddenDefaults['reportWidth'] = true;
9✔
1068
            } else if (substr($arg, 0, 9) === 'basepath=') {
33✔
UNCOV
1069
                if (isset(self::$overriddenDefaults['basepath']) === true) {
×
UNCOV
1070
                    break;
×
1071
                }
1072

1073
                self::$overriddenDefaults['basepath'] = true;
×
1074

1075
                if (substr($arg, 9) === '') {
×
UNCOV
1076
                    $this->basepath = null;
×
UNCOV
1077
                    break;
×
1078
                }
1079

UNCOV
1080
                $this->basepath = Common::realpath(substr($arg, 9));
×
1081

1082
                // It may not exist and return false instead.
UNCOV
1083
                if ($this->basepath === false) {
×
UNCOV
1084
                    $this->basepath = substr($arg, 9);
×
1085
                }
1086

1087
                if (is_dir($this->basepath) === false) {
×
1088
                    $error  = 'ERROR: The specified basepath "'.$this->basepath.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
×
UNCOV
1089
                    $error .= $this->printShortUsage(true);
×
UNCOV
1090
                    throw new DeepExitException($error, 3);
×
1091
                }
1092
            } else if ((substr($arg, 0, 7) === 'report=' || substr($arg, 0, 7) === 'report-')) {
30✔
1093
                $reports = [];
×
1094

1095
                if ($arg[6] === '-') {
×
1096
                    // This is a report with file output.
1097
                    $split = strpos($arg, '=');
×
1098
                    if ($split === false) {
×
UNCOV
1099
                        $report = substr($arg, 7);
×
1100
                        $output = null;
×
1101
                    } else {
1102
                        $report = substr($arg, 7, ($split - 7));
×
1103
                        $output = substr($arg, ($split + 1));
×
UNCOV
1104
                        if ($output === false) {
×
1105
                            $output = null;
×
1106
                        } else {
1107
                            $dir = Common::realpath(dirname($output));
×
1108
                            if (is_dir($dir) === false) {
×
1109
                                $error  = 'ERROR: The specified '.$report.' report file path "'.$output.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
×
UNCOV
1110
                                $error .= $this->printShortUsage(true);
×
UNCOV
1111
                                throw new DeepExitException($error, 3);
×
1112
                            }
1113

1114
                            $output = $dir.'/'.basename($output);
×
1115

1116
                            if (is_dir($output) === true) {
×
1117
                                $error  = 'ERROR: The specified '.$report.' report file path "'.$output.'" is a directory'.PHP_EOL.PHP_EOL;
×
UNCOV
1118
                                $error .= $this->printShortUsage(true);
×
UNCOV
1119
                                throw new DeepExitException($error, 3);
×
1120
                            }
1121
                        }//end if
1122
                    }//end if
1123

UNCOV
1124
                    $reports[$report] = $output;
×
1125
                } else {
1126
                    // This is a single report.
UNCOV
1127
                    if (isset(self::$overriddenDefaults['reports']) === true) {
×
UNCOV
1128
                        break;
×
1129
                    }
1130

1131
                    $reportNames = explode(',', substr($arg, 7));
×
UNCOV
1132
                    foreach ($reportNames as $report) {
×
UNCOV
1133
                        $reports[$report] = null;
×
1134
                    }
1135
                }//end if
1136

1137
                // Remove the default value so the CLI value overrides it.
UNCOV
1138
                if (isset(self::$overriddenDefaults['reports']) === false) {
×
1139
                    $this->reports = $reports;
×
1140
                } else {
UNCOV
1141
                    $this->reports = array_merge($this->reports, $reports);
×
1142
                }
1143

1144
                self::$overriddenDefaults['reports'] = true;
×
1145
            } else if (substr($arg, 0, 7) === 'filter=') {
30✔
UNCOV
1146
                if (isset(self::$overriddenDefaults['filter']) === true) {
×
UNCOV
1147
                    break;
×
1148
                }
1149

UNCOV
1150
                $this->filter = substr($arg, 7);
×
1151
                self::$overriddenDefaults['filter'] = true;
×
1152
            } else if (substr($arg, 0, 9) === 'standard=') {
30✔
1153
                $standards = trim(substr($arg, 9));
×
UNCOV
1154
                if ($standards !== '') {
×
UNCOV
1155
                    $this->standards = explode(',', $standards);
×
1156
                }
1157

1158
                self::$overriddenDefaults['standards'] = true;
×
1159
            } else if (substr($arg, 0, 11) === 'extensions=') {
30✔
UNCOV
1160
                if (isset(self::$overriddenDefaults['extensions']) === true) {
×
UNCOV
1161
                    break;
×
1162
                }
1163

1164
                $extensions    = explode(',', substr($arg, 11));
×
1165
                $newExtensions = [];
×
1166
                foreach ($extensions as $ext) {
×
UNCOV
1167
                    $slash = strpos($ext, '/');
×
1168
                    if ($slash !== false) {
×
1169
                        // They specified the tokenizer too.
1170
                        list($ext, $tokenizer) = explode('/', $ext);
×
UNCOV
1171
                        $newExtensions[$ext]   = strtoupper($tokenizer);
×
UNCOV
1172
                        continue;
×
1173
                    }
1174

UNCOV
1175
                    if (isset($this->extensions[$ext]) === true) {
×
1176
                        $newExtensions[$ext] = $this->extensions[$ext];
×
1177
                    } else {
UNCOV
1178
                        $newExtensions[$ext] = 'PHP';
×
1179
                    }
1180
                }
1181

UNCOV
1182
                $this->extensions = $newExtensions;
×
1183
                self::$overriddenDefaults['extensions'] = true;
×
1184
            } else if (substr($arg, 0, 7) === 'suffix=') {
30✔
UNCOV
1185
                if (isset(self::$overriddenDefaults['suffix']) === true) {
×
UNCOV
1186
                    break;
×
1187
                }
1188

UNCOV
1189
                $this->suffix = substr($arg, 7);
×
1190
                self::$overriddenDefaults['suffix'] = true;
×
1191
            } else if (substr($arg, 0, 9) === 'parallel=') {
30✔
UNCOV
1192
                if (isset(self::$overriddenDefaults['parallel']) === true) {
×
UNCOV
1193
                    break;
×
1194
                }
1195

UNCOV
1196
                $this->parallel = max((int) substr($arg, 9), 1);
×
1197
                self::$overriddenDefaults['parallel'] = true;
×
1198
            } else if (substr($arg, 0, 9) === 'severity=') {
30✔
1199
                $this->errorSeverity   = (int) substr($arg, 9);
×
1200
                $this->warningSeverity = $this->errorSeverity;
×
UNCOV
1201
                if (isset(self::$overriddenDefaults['errorSeverity']) === false) {
×
UNCOV
1202
                    self::$overriddenDefaults['errorSeverity'] = true;
×
1203
                }
1204

UNCOV
1205
                if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
×
UNCOV
1206
                    self::$overriddenDefaults['warningSeverity'] = true;
×
1207
                }
1208
            } else if (substr($arg, 0, 15) === 'error-severity=') {
30✔
UNCOV
1209
                if (isset(self::$overriddenDefaults['errorSeverity']) === true) {
×
UNCOV
1210
                    break;
×
1211
                }
1212

UNCOV
1213
                $this->errorSeverity = (int) substr($arg, 15);
×
1214
                self::$overriddenDefaults['errorSeverity'] = true;
×
1215
            } else if (substr($arg, 0, 17) === 'warning-severity=') {
30✔
UNCOV
1216
                if (isset(self::$overriddenDefaults['warningSeverity']) === true) {
×
UNCOV
1217
                    break;
×
1218
                }
1219

UNCOV
1220
                $this->warningSeverity = (int) substr($arg, 17);
×
1221
                self::$overriddenDefaults['warningSeverity'] = true;
×
1222
            } else if (substr($arg, 0, 7) === 'ignore=') {
30✔
UNCOV
1223
                if (isset(self::$overriddenDefaults['ignored']) === true) {
×
UNCOV
1224
                    break;
×
1225
                }
1226

1227
                // Split the ignore string on commas, unless the comma is escaped
1228
                // using 1 or 3 slashes (\, or \\\,).
1229
                $patterns = preg_split(
×
UNCOV
1230
                    '/(?<=(?<!\\\\)\\\\\\\\),|(?<!\\\\),/',
×
UNCOV
1231
                    substr($arg, 7)
×
1232
                );
1233

NEW
1234
                $ignored = [];
×
NEW
1235
                foreach ($patterns as $pattern) {
×
NEW
1236
                    $pattern = trim($pattern);
×
NEW
UNCOV
1237
                    if ($pattern === '') {
×
NEW
UNCOV
1238
                        continue;
×
1239
                    }
1240

NEW
UNCOV
1241
                    $ignored[$pattern] = 'absolute';
×
1242
                }
1243

NEW
UNCOV
1244
                $this->ignored = $ignored;
×
NEW
UNCOV
1245
                self::$overriddenDefaults['ignored'] = true;
×
1246
            } else if (substr($arg, 0, 10) === 'generator='
30✔
1247
                && PHP_CODESNIFFER_CBF === false
30✔
1248
            ) {
10✔
1249
                if (isset(self::$overriddenDefaults['generator']) === true) {
30✔
1250
                    break;
3✔
1251
                }
1252

1253
                $generatorName          = substr($arg, 10);
30✔
1254
                $lowerCaseGeneratorName = strtolower($generatorName);
30✔
1255

1256
                if (isset(self::$validGenerators[$lowerCaseGeneratorName]) === false) {
30✔
1257
                    $validOptions = implode(', ', array_values(self::$validGenerators));
9✔
1258
                    $error        = sprintf(
9✔
1259
                        'ERROR: "%s" is not a valid generator. Valid options are: %s.'.PHP_EOL.PHP_EOL,
9✔
1260
                        $generatorName,
9✔
1261
                        $validOptions
6✔
1262
                    );
6✔
1263
                    $error       .= $this->printShortUsage(true);
9✔
1264
                    throw new DeepExitException($error, 3);
9✔
1265
                }
1266

1267
                $this->generator = self::$validGenerators[$lowerCaseGeneratorName];
21✔
1268
                self::$overriddenDefaults['generator'] = true;
21✔
1269
            } else if (substr($arg, 0, 9) === 'encoding=') {
7✔
1270
                if (isset(self::$overriddenDefaults['encoding']) === true) {
×
1271
                    break;
×
1272
                }
1273

1274
                $this->encoding = strtolower(substr($arg, 9));
×
1275
                self::$overriddenDefaults['encoding'] = true;
×
1276
            } else if (substr($arg, 0, 10) === 'tab-width=') {
×
1277
                if (isset(self::$overriddenDefaults['tabWidth']) === true) {
×
1278
                    break;
×
1279
                }
1280

1281
                $this->tabWidth = (int) substr($arg, 10);
×
1282
                self::$overriddenDefaults['tabWidth'] = true;
×
1283
            } else {
1284
                if ($this->dieOnUnknownArg === false) {
×
1285
                    $eqPos = strpos($arg, '=');
×
1286
                    try {
1287
                        $unknown = $this->unknown;
×
1288

1289
                        if ($eqPos === false) {
×
1290
                            $unknown[$arg] = $arg;
×
1291
                        } else {
1292
                            $value         = substr($arg, ($eqPos + 1));
×
1293
                            $arg           = substr($arg, 0, $eqPos);
×
1294
                            $unknown[$arg] = $value;
×
1295
                        }
1296

1297
                        $this->unknown = $unknown;
×
1298
                    } catch (RuntimeException $e) {
×
1299
                        // Value is not valid, so just ignore it.
1300
                    }
1301
                } else {
1302
                    $this->processUnknownArgument('--'.$arg, $pos);
×
1303
                }
1304
            }//end if
1305
            break;
48✔
1306
        }//end switch
31✔
1307

1308
    }//end processLongArgument()
32✔
1309

1310

1311
    /**
1312
     * Processes an unknown command line argument.
1313
     *
1314
     * Assumes all unknown arguments are files and folders to check.
1315
     *
1316
     * @param string $arg The command line argument.
1317
     * @param int    $pos The position of the argument on the command line.
1318
     *
1319
     * @return void
1320
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException
1321
     */
1322
    public function processUnknownArgument($arg, $pos)
×
1323
    {
1324
        // We don't know about any additional switches; just files.
1325
        if ($arg[0] === '-') {
×
1326
            if ($this->dieOnUnknownArg === false) {
×
1327
                return;
×
1328
            }
1329

1330
            $error  = "ERROR: option \"$arg\" not known".PHP_EOL.PHP_EOL;
×
1331
            $error .= $this->printShortUsage(true);
×
1332
            throw new DeepExitException($error, 3);
×
1333
        }
1334

1335
        $this->processFilePath($arg);
×
1336

1337
    }//end processUnknownArgument()
1338

1339

1340
    /**
1341
     * Processes a file path and add it to the file list.
1342
     *
1343
     * @param string $path The path to the file to add.
1344
     *
1345
     * @return void
1346
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException
1347
     */
1348
    public function processFilePath($path)
×
1349
    {
1350
        // If we are processing STDIN, don't record any files to check.
1351
        if ($this->stdin === true) {
×
1352
            return;
×
1353
        }
1354

1355
        $file = Common::realpath($path);
×
1356
        if (file_exists($file) === false) {
×
1357
            if ($this->dieOnUnknownArg === false) {
×
1358
                return;
×
1359
            }
1360

1361
            $error  = 'ERROR: The file "'.$path.'" does not exist.'.PHP_EOL.PHP_EOL;
×
1362
            $error .= $this->printShortUsage(true);
×
1363
            throw new DeepExitException($error, 3);
×
1364
        } else {
1365
            // Can't modify the files array directly because it's not a real
1366
            // class member, so need to use this little get/modify/set trick.
1367
            $files       = $this->files;
×
1368
            $files[]     = $file;
×
1369
            $this->files = $files;
×
1370
            self::$overriddenDefaults['files'] = true;
×
1371
        }
1372

1373
    }//end processFilePath()
1374

1375

1376
    /**
1377
     * Prints out the usage information for this script.
1378
     *
1379
     * @return void
1380
     */
1381
    public function printUsage()
×
1382
    {
1383
        echo PHP_EOL;
×
1384

1385
        if (PHP_CODESNIFFER_CBF === true) {
×
1386
            $this->printPHPCBFUsage();
×
1387
        } else {
1388
            $this->printPHPCSUsage();
×
1389
        }
1390

1391
        echo PHP_EOL;
×
1392

1393
    }//end printUsage()
1394

1395

1396
    /**
1397
     * Prints out the short usage information for this script.
1398
     *
1399
     * @param bool $return If TRUE, the usage string is returned
1400
     *                     instead of output to screen.
1401
     *
1402
     * @return string|void
1403
     */
1404
    public function printShortUsage($return=false)
×
1405
    {
1406
        if (PHP_CODESNIFFER_CBF === true) {
×
1407
            $usage = 'Run "phpcbf --help" for usage information';
×
1408
        } else {
1409
            $usage = 'Run "phpcs --help" for usage information';
×
1410
        }
1411

1412
        $usage .= PHP_EOL.PHP_EOL;
×
1413

1414
        if ($return === true) {
×
1415
            return $usage;
×
1416
        }
1417

1418
        echo $usage;
×
1419

1420
    }//end printShortUsage()
1421

1422

1423
    /**
1424
     * Prints out the usage information for PHPCS.
1425
     *
1426
     * @return void
1427
     */
1428
    public function printPHPCSUsage()
×
1429
    {
1430
        $longOptions   = explode(',', Help::DEFAULT_LONG_OPTIONS);
×
1431
        $longOptions[] = 'cache';
×
1432
        $longOptions[] = 'no-cache';
×
1433
        $longOptions[] = 'report';
×
1434
        $longOptions[] = 'report-file';
×
1435
        $longOptions[] = 'report-report';
×
1436
        $longOptions[] = 'config-explain';
×
1437
        $longOptions[] = 'config-set';
×
1438
        $longOptions[] = 'config-delete';
×
1439
        $longOptions[] = 'config-show';
×
1440
        $longOptions[] = 'generator';
×
1441

1442
        $shortOptions = Help::DEFAULT_SHORT_OPTIONS.'aems';
×
1443

1444
        (new Help($this, $longOptions, $shortOptions))->display();
×
1445

1446
    }//end printPHPCSUsage()
1447

1448

1449
    /**
1450
     * Prints out the usage information for PHPCBF.
1451
     *
1452
     * @return void
1453
     */
1454
    public function printPHPCBFUsage()
×
1455
    {
1456
        $longOptions   = explode(',', Help::DEFAULT_LONG_OPTIONS);
×
1457
        $longOptions[] = 'suffix';
×
1458
        $shortOptions  = Help::DEFAULT_SHORT_OPTIONS;
×
1459

1460
        (new Help($this, $longOptions, $shortOptions))->display();
×
1461

1462
    }//end printPHPCBFUsage()
1463

1464

1465
    /**
1466
     * Get a single config value.
1467
     *
1468
     * @param string $key The name of the config value.
1469
     *
1470
     * @return string|null
1471
     * @see    setConfigData()
1472
     * @see    getAllConfigData()
1473
     */
1474
    public static function getConfigData($key)
6✔
1475
    {
1476
        $phpCodeSnifferConfig = self::getAllConfigData();
6✔
1477

1478
        if ($phpCodeSnifferConfig === null) {
6✔
1479
            return null;
×
1480
        }
1481

1482
        if (isset($phpCodeSnifferConfig[$key]) === false) {
6✔
1483
            return null;
6✔
1484
        }
1485

1486
        return $phpCodeSnifferConfig[$key];
6✔
1487

1488
    }//end getConfigData()
1489

1490

1491
    /**
1492
     * Get the path to an executable utility.
1493
     *
1494
     * @param string $name The name of the executable utility.
1495
     *
1496
     * @return string|null
1497
     * @see    getConfigData()
1498
     */
1499
    public static function getExecutablePath($name)
4✔
1500
    {
1501
        $data = self::getConfigData($name.'_path');
4✔
1502
        if ($data !== null) {
4✔
1503
            return $data;
×
1504
        }
1505

1506
        if ($name === "php") {
4✔
1507
            // For php, we know the executable path. There's no need to look it up.
1508
            return PHP_BINARY;
×
1509
        }
1510

1511
        if (array_key_exists($name, self::$executablePaths) === true) {
4✔
1512
            return self::$executablePaths[$name];
2✔
1513
        }
1514

1515
        if (stripos(PHP_OS, 'WIN') === 0) {
4✔
1516
            $cmd = 'where '.escapeshellarg($name).' 2> nul';
2✔
1517
        } else {
1✔
1518
            $cmd = 'which '.escapeshellarg($name).' 2> /dev/null';
2✔
1519
        }
1520

1521
        $result = exec($cmd, $output, $retVal);
4✔
1522
        if ($retVal !== 0) {
4✔
1523
            $result = null;
×
1524
        }
1525

1526
        self::$executablePaths[$name] = $result;
4✔
1527
        return $result;
4✔
1528

1529
    }//end getExecutablePath()
1530

1531

1532
    /**
1533
     * Set a single config value.
1534
     *
1535
     * @param string      $key   The name of the config value.
1536
     * @param string|null $value The value to set. If null, the config
1537
     *                           entry is deleted, reverting it to the
1538
     *                           default value.
1539
     * @param boolean     $temp  Set this config data temporarily for this
1540
     *                           script run. This will not write the config
1541
     *                           data to the config file.
1542
     *
1543
     * @return bool
1544
     * @see    getConfigData()
1545
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException If the config file can not be written.
1546
     */
1547
    public static function setConfigData($key, $value, $temp=false)
×
1548
    {
1549
        if (isset(self::$overriddenDefaults['runtime-set']) === true
×
1550
            && isset(self::$overriddenDefaults['runtime-set'][$key]) === true
×
1551
        ) {
1552
            return false;
×
1553
        }
1554

1555
        if ($temp === false) {
×
1556
            $path = '';
×
1557
            if (is_callable('\Phar::running') === true) {
×
1558
                $path = Phar::running(false);
×
1559
            }
1560

1561
            if ($path !== '') {
×
1562
                $configFile = dirname($path).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
×
1563
            } else {
1564
                $configFile = dirname(__DIR__).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
×
1565
            }
1566

1567
            if (is_file($configFile) === true
×
1568
                && is_writable($configFile) === false
×
1569
            ) {
1570
                $error = 'ERROR: Config file '.$configFile.' is not writable'.PHP_EOL.PHP_EOL;
×
1571
                throw new DeepExitException($error, 3);
×
1572
            }
1573
        }//end if
1574

1575
        $phpCodeSnifferConfig = self::getAllConfigData();
×
1576

1577
        if ($value === null) {
×
1578
            if (isset($phpCodeSnifferConfig[$key]) === true) {
×
1579
                unset($phpCodeSnifferConfig[$key]);
×
1580
            }
1581
        } else {
1582
            $phpCodeSnifferConfig[$key] = $value;
×
1583
        }
1584

1585
        if ($temp === false) {
×
1586
            $output  = '<'.'?php'."\n".' $phpCodeSnifferConfig = ';
×
1587
            $output .= var_export($phpCodeSnifferConfig, true);
×
1588
            $output .= ";\n?".'>';
×
1589

1590
            if (file_put_contents($configFile, $output) === false) {
×
1591
                $error = 'ERROR: Config file '.$configFile.' could not be written'.PHP_EOL.PHP_EOL;
×
1592
                throw new DeepExitException($error, 3);
×
1593
            }
1594

1595
            self::$configDataFile = $configFile;
×
1596
        }
1597

1598
        self::$configData = $phpCodeSnifferConfig;
×
1599

1600
        // If the installed paths are being set, make sure all known
1601
        // standards paths are added to the autoloader.
1602
        if ($key === 'installed_paths') {
×
1603
            $installedStandards = Standards::getInstalledStandardDetails();
×
1604
            foreach ($installedStandards as $details) {
×
1605
                Autoload::addSearchPath($details['path'], $details['namespace']);
×
1606
            }
1607
        }
1608

1609
        return true;
×
1610

1611
    }//end setConfigData()
1612

1613

1614
    /**
1615
     * Get all config data.
1616
     *
1617
     * @return array<string, string>
1618
     * @see    getConfigData()
1619
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException If the config file could not be read.
1620
     */
1621
    public static function getAllConfigData()
×
1622
    {
1623
        if (self::$configData !== null) {
×
1624
            return self::$configData;
×
1625
        }
1626

1627
        $path = '';
×
1628
        if (is_callable('\Phar::running') === true) {
×
1629
            $path = Phar::running(false);
×
1630
        }
1631

1632
        if ($path !== '') {
×
1633
            $configFile = dirname($path).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
×
1634
        } else {
1635
            $configFile = dirname(__DIR__).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
×
1636
            if (is_file($configFile) === false
×
1637
                && strpos('@data_dir@', '@data_dir') === false
×
1638
            ) {
1639
                $configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf';
×
1640
            }
1641
        }
1642

1643
        if (is_file($configFile) === false) {
×
1644
            self::$configData = [];
×
1645
            return [];
×
1646
        }
1647

1648
        if (Common::isReadable($configFile) === false) {
×
1649
            $error = 'ERROR: Config file '.$configFile.' is not readable'.PHP_EOL.PHP_EOL;
×
1650
            throw new DeepExitException($error, 3);
×
1651
        }
1652

1653
        include $configFile;
×
1654
        self::$configDataFile = $configFile;
×
1655
        self::$configData     = $phpCodeSnifferConfig;
×
1656
        return self::$configData;
×
1657

1658
    }//end getAllConfigData()
1659

1660

1661
    /**
1662
     * Prints out the gathered config data.
1663
     *
1664
     * @param array $data The config data to print.
1665
     *
1666
     * @return void
1667
     */
1668
    public function printConfigData($data)
×
1669
    {
1670
        $max  = 0;
×
1671
        $keys = array_keys($data);
×
1672
        foreach ($keys as $key) {
×
1673
            $len = strlen($key);
×
1674
            if (strlen($key) > $max) {
×
1675
                $max = $len;
×
1676
            }
1677
        }
1678

1679
        if ($max === 0) {
×
1680
            return;
×
1681
        }
1682

1683
        $max += 2;
×
1684
        ksort($data);
×
1685
        foreach ($data as $name => $value) {
×
1686
            echo str_pad($name.': ', $max).$value.PHP_EOL;
×
1687
        }
1688

1689
    }//end printConfigData()
1690

1691

1692
}//end class
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc