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

PHPCSStandards / PHP_CodeSniffer / 18429397455

11 Oct 2025 12:29PM UTC coverage: 78.814% (+0.04%) from 78.779%
18429397455

Pull #1291

github

web-flow
Merge 61fcf511f into 892320f0b
Pull Request #1291: Runner: allow suppressing "No files were checked" error

3 of 8 new or added lines in 2 files covered. (37.5%)

281 existing lines in 1 file now uncovered.

19735 of 25040 relevant lines covered (78.81%)

96.8 hits per line

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

38.25
/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-2023 Squiz Pty Ltd (ABN 77 084 670 600)
10
 * @copyright 2023 PHPCSStandards and contributors
11
 * @license   https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/HEAD/licence.txt BSD Licence
12
 */
13

14
namespace PHP_CodeSniffer;
15

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

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

89
    /**
90
     * The current version.
91
     *
92
     * @var string
93
     */
94
    public const VERSION = '4.0.1';
95

96
    /**
97
     * Package stability; either stable, RC, beta or alpha.
98
     *
99
     * @var string
100
     */
101
    public const STABILITY = 'stable';
102

103
    /**
104
     * Default report width when no report width is provided and 'auto' does not yield a valid width.
105
     *
106
     * @var int
107
     */
108
    public const DEFAULT_REPORT_WIDTH = 80;
109

110
    /**
111
     * Translation table for config settings which can be changed via multiple CLI flags.
112
     *
113
     * If the flag name matches the setting name, there is no need to add it to this translation table.
114
     * Similarly, if there is only one flag which can change a setting, there is no need to include
115
     * it in this table, even if the flag name and the setting name don't match.
116
     *
117
     * @var array<string, string> Key is the CLI flag name, value the corresponding config setting name.
118
     */
119
    public const CLI_FLAGS_TO_SETTING_NAME = [
120
        'n'                => 'warningSeverity',
121
        'w'                => 'warningSeverity',
122
        'warning-severity' => 'warningSeverity',
123
        'no-colors'        => 'colors',
124
        'no-cache'         => 'cache',
125
    ];
126

127
    /**
128
     * A list of valid generators.
129
     *
130
     * @var array<string, string> Keys are the lowercase version of the generator name, while values
131
     *                            are the name of the associated PHP generator class.
132
     */
133
    private const VALID_GENERATORS = [
134
        'text'     => 'Text',
135
        'html'     => 'HTML',
136
        'markdown' => 'Markdown',
137
    ];
138

139
    /**
140
     * The default configuration file names supported by PHPCS.
141
     *
142
     * @var array<string> The supported file names in order of precedence (highest first).
143
     */
144
    private const CONFIG_FILENAMES = [
145
        '.phpcs.xml',
146
        'phpcs.xml',
147
        '.phpcs.xml.dist',
148
        'phpcs.xml.dist',
149
    ];
150

151
    /**
152
     * An array of settings that PHPCS and PHPCBF accept.
153
     *
154
     * This array is not meant to be accessed directly. Instead, use the settings
155
     * as if they are class member vars so the __get() and __set() magic methods
156
     * can be used to validate the values. For example, to set the verbosity level to
157
     * level 2, use $this->verbosity = 2; instead of accessing this property directly.
158
     *
159
     * Each of these settings is described in the class comment property list.
160
     *
161
     * @var array<string, mixed>
162
     */
163
    private $settings = [
164
        'files'              => null,
165
        'standards'          => null,
166
        'verbosity'          => null,
167
        'interactive'        => null,
168
        'parallel'           => null,
169
        'cache'              => null,
170
        'cacheFile'          => null,
171
        'colors'             => null,
172
        'explain'            => null,
173
        'local'              => null,
174
        'showSources'        => null,
175
        'showProgress'       => null,
176
        'quiet'              => null,
177
        'annotations'        => null,
178
        'tabWidth'           => null,
179
        'encoding'           => null,
180
        'extensions'         => null,
181
        'sniffs'             => null,
182
        'exclude'            => null,
183
        'ignored'            => null,
184
        'reportFile'         => null,
185
        'generator'          => null,
186
        'filter'             => null,
187
        'bootstrap'          => null,
188
        'reports'            => null,
189
        'basepath'           => null,
190
        'reportWidth'        => null,
191
        'errorSeverity'      => null,
192
        'warningSeverity'    => null,
193
        'recordErrors'       => null,
194
        'suffix'             => null,
195
        'stdin'              => null,
196
        'stdinContent'       => null,
197
        'stdinPath'          => null,
198
        'trackTime'          => null,
199
        'unknown'            => null,
200
        'allowEmptyFileList' => null,
201
    ];
202

203
    /**
204
     * Whether or not to kill the process when an unknown command line arg is found.
205
     *
206
     * If FALSE, arguments that are not command line options or file/directory paths
207
     * will be ignored and execution will continue. These values will be stored in
208
     * $this->unknown.
209
     *
210
     * @var boolean
211
     */
212
    public $dieOnUnknownArg;
213

214
    /**
215
     * The current command line arguments we are processing.
216
     *
217
     * @var string[]
218
     */
219
    private $cliArgs = [];
220

221
    /**
222
     * Command line values that the user has supplied directly.
223
     *
224
     * @var array<string, true|array<string, true>>
225
     */
226
    private $overriddenDefaults = [];
227

228
    /**
229
     * Config file data that has been loaded for the run.
230
     *
231
     * @var array<string, string>
232
     */
233
    private static $configData = null;
234

235
    /**
236
     * The full path to the config data file that has been loaded.
237
     *
238
     * @var string
239
     */
240
    private static $configDataFile = null;
241

242
    /**
243
     * Automatically discovered executable utility paths.
244
     *
245
     * @var array<string, string>
246
     */
247
    private static $executablePaths = [];
248

249

250
    /**
251
     * Get the value of an inaccessible property.
252
     *
253
     * @param string $name The name of the property.
254
     *
255
     * @return mixed
256
     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the setting name is invalid.
257
     */
258
    public function __get(string $name)
48✔
259
    {
260
        if (array_key_exists($name, $this->settings) === false) {
48✔
UNCOV
261
            throw new RuntimeException("ERROR: unable to get value of property \"$name\"");
×
262
        }
263

264
        // Figure out what the terminal width needs to be for "auto".
265
        if ($name === 'reportWidth' && $this->settings[$name] === 'auto') {
48✔
266
            if (function_exists('shell_exec') === true) {
9✔
267
                $dimensions = shell_exec('stty size 2>&1');
9✔
268
                if (is_string($dimensions) === true && preg_match('|\d+ (\d+)|', $dimensions, $matches) === 1) {
9✔
UNCOV
269
                    $this->settings[$name] = (int) $matches[1];
×
270
                }
271
            }
272

273
            if ($this->settings[$name] === 'auto') {
9✔
274
                // If shell_exec wasn't available or didn't yield a usable value, set to the default.
275
                // This will prevent subsequent retrievals of the reportWidth from making another call to stty.
276
                $this->settings[$name] = self::DEFAULT_REPORT_WIDTH;
9✔
277
            }
278
        }
279

280
        return $this->settings[$name];
48✔
281
    }
282

283

284
    /**
285
     * Set the value of an inaccessible property.
286
     *
287
     * @param string $name  The name of the property.
288
     * @param mixed  $value The value of the property.
289
     *
290
     * @return void
291
     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the setting name is invalid.
292
     */
293
    public function __set(string $name, $value)
48✔
294
    {
295
        if (array_key_exists($name, $this->settings) === false) {
48✔
UNCOV
296
            throw new RuntimeException("Can't __set() $name; setting doesn't exist");
×
297
        }
298

299
        switch ($name) {
16✔
300
            case 'reportWidth' :
48✔
301
                if (is_string($value) === true && $value === 'auto') {
48✔
302
                    // Nothing to do. Leave at 'auto'.
303
                    break;
48✔
304
                }
305

306
                if (is_int($value) === true) {
39✔
307
                    $value = abs($value);
6✔
308
                } elseif (is_string($value) === true && preg_match('`^\d+$`', $value) === 1) {
33✔
309
                    $value = (int) $value;
15✔
310
                } else {
311
                    $value = self::DEFAULT_REPORT_WIDTH;
18✔
312
                }
313
                break;
39✔
314

315
            case 'standards' :
48✔
316
                $cleaned = [];
48✔
317

318
                // Check if the standard name is valid, or if the case is invalid.
319
                $installedStandards = Standards::getInstalledStandards();
48✔
320
                foreach ($value as $standard) {
48✔
321
                    foreach ($installedStandards as $validStandard) {
48✔
322
                        if (strtolower($standard) === strtolower($validStandard)) {
48✔
323
                            $standard = $validStandard;
48✔
324
                            break;
48✔
325
                        }
326
                    }
327

328
                    $cleaned[] = $standard;
48✔
329
                }
330

331
                $value = $cleaned;
48✔
332
                break;
48✔
333

334
            // Only track time when explicitly needed.
335
            case 'verbosity':
48✔
336
                if ($value > 2) {
48✔
UNCOV
337
                    $this->settings['trackTime'] = true;
×
338
                }
339
                break;
48✔
340
            case 'reports':
48✔
341
                $reports = array_change_key_case($value, CASE_LOWER);
48✔
342
                if (array_key_exists('performance', $reports) === true) {
48✔
UNCOV
343
                    $this->settings['trackTime'] = true;
×
344
                }
345
                break;
48✔
346

347
            default :
348
                // No validation required.
349
                break;
48✔
350
        }
351

352
        $this->settings[$name] = $value;
48✔
353
    }
16✔
354

355

356
    /**
357
     * Check if the value of an inaccessible property is set.
358
     *
359
     * @param string $name The name of the property.
360
     *
361
     * @return bool
362
     */
363
    public function __isset(string $name)
×
364
    {
UNCOV
365
        return isset($this->settings[$name]);
×
366
    }
367

368

369
    /**
370
     * Unset the value of an inaccessible property.
371
     *
372
     * @param string $name The name of the property.
373
     *
374
     * @return void
375
     */
376
    public function __unset(string $name)
×
377
    {
UNCOV
378
        $this->settings[$name] = null;
×
379
    }
380

381

382
    /**
383
     * Get the array of all config settings.
384
     *
385
     * @return array<string, mixed>
386
     */
387
    public function getSettings()
×
388
    {
UNCOV
389
        return $this->settings;
×
390
    }
391

392

393
    /**
394
     * Set the array of all config settings.
395
     *
396
     * @param array<string, mixed> $settings The array of config settings.
397
     *
398
     * @return void
399
     */
400
    public function setSettings(array $settings)
×
401
    {
UNCOV
402
        $this->settings = $settings;
×
403
    }
404

405

406
    /**
407
     * Creates a Config object and populates it with command line values.
408
     *
409
     * @param array $cliArgs         An array of values gathered from CLI args.
410
     * @param bool  $dieOnUnknownArg Whether or not to kill the process when an
411
     *                               unknown command line arg is found.
412
     *
413
     * @return void
414
     */
415
    public function __construct(array $cliArgs = [], bool $dieOnUnknownArg = true)
×
416
    {
UNCOV
417
        if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
×
418
            // Let everything through during testing so that we can
419
            // make use of PHPUnit command line arguments as well.
420
            $this->dieOnUnknownArg = false;
×
421
        } else {
UNCOV
422
            $this->dieOnUnknownArg = $dieOnUnknownArg;
×
423
        }
424

425
        if (empty($cliArgs) === true) {
×
UNCOV
426
            $cliArgs = $_SERVER['argv'];
×
UNCOV
427
            array_shift($cliArgs);
×
428
        }
429

UNCOV
430
        $this->restoreDefaults();
×
431
        $this->setCommandLineValues($cliArgs);
×
432

UNCOV
433
        if (isset($this->overriddenDefaults['standards']) === false) {
×
434
            // They did not supply a standard to use.
435
            // Look for a default ruleset in the current directory or higher.
UNCOV
436
            $currentDir = getcwd();
×
437

438
            do {
439
                foreach (self::CONFIG_FILENAMES as $defaultFilename) {
×
440
                    $default = $currentDir . DIRECTORY_SEPARATOR . $defaultFilename;
×
441
                    if (is_file($default) === true) {
×
UNCOV
442
                        $this->standards = [$default];
×
UNCOV
443
                        break(2);
×
444
                    }
445
                }
446

447
                $lastDir    = $currentDir;
×
UNCOV
448
                $currentDir = dirname($currentDir);
×
UNCOV
449
            } while ($currentDir !== '.' && $currentDir !== $lastDir && Common::isReadable($currentDir) === true);
×
450
        }
451

UNCOV
452
        if (defined('STDIN') === false
×
453
            || PHP_OS_FAMILY === 'Windows'
×
454
        ) {
UNCOV
455
            return;
×
456
        }
457

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

460
        // Check for content on STDIN.
461
        if ($this->stdin === true
×
UNCOV
462
            || (Common::isStdinATTY() === false
×
463
            && feof($handle) === false)
×
464
        ) {
UNCOV
465
            $readStreams = [$handle];
×
466
            $writeSteams = null;
×
467

UNCOV
468
            $fileContents = '';
×
469
            while (is_resource($handle) === true && feof($handle) === false) {
×
470
                // Set a timeout of 200ms.
UNCOV
471
                if (stream_select($readStreams, $writeSteams, $writeSteams, 0, 200000) === 0) {
×
UNCOV
472
                    break;
×
473
                }
474

UNCOV
475
                $fileContents .= fgets($handle);
×
476
            }
477

478
            if (trim($fileContents) !== '') {
×
479
                $this->stdin        = true;
×
480
                $this->stdinContent = $fileContents;
×
UNCOV
481
                $this->overriddenDefaults['stdin']        = true;
×
UNCOV
482
                $this->overriddenDefaults['stdinContent'] = true;
×
483
            }
484
        }
485

UNCOV
486
        fclose($handle);
×
487
    }
488

489

490
    /**
491
     * Set the command line values.
492
     *
493
     * @param array $args An array of command line arguments to set.
494
     *
495
     * @return void
496
     */
497
    public function setCommandLineValues(array $args)
×
498
    {
UNCOV
499
        $this->cliArgs = $args;
×
500
        $numArgs       = count($args);
×
501

502
        for ($i = 0; $i < $numArgs; $i++) {
×
503
            $arg = $this->cliArgs[$i];
×
UNCOV
504
            if ($arg === '') {
×
UNCOV
505
                continue;
×
506
            }
507

UNCOV
508
            if ($arg[0] === '-') {
×
509
                if ($arg === '-') {
×
510
                    // Asking to read from STDIN.
511
                    $this->stdin = true;
×
UNCOV
512
                    $this->overriddenDefaults['stdin'] = true;
×
UNCOV
513
                    continue;
×
514
                }
515

516
                if ($arg === '--') {
×
517
                    // Empty argument, ignore it.
UNCOV
518
                    continue;
×
519
                }
520

UNCOV
521
                if ($arg[1] === '-') {
×
522
                    $this->processLongArgument(substr($arg, 2), $i);
×
523
                } else {
524
                    $switches = str_split($arg);
×
525
                    foreach ($switches as $switch) {
×
UNCOV
526
                        if ($switch === '-') {
×
UNCOV
527
                            continue;
×
528
                        }
529

UNCOV
530
                        $this->processShortArgument($switch, $i);
×
531
                    }
532
                }
533
            } else {
UNCOV
534
                $this->processUnknownArgument($arg, $i);
×
535
            }
536
        }
537
    }
538

539

540
    /**
541
     * Restore default values for all possible command line arguments.
542
     *
543
     * @return void
544
     */
545
    public function restoreDefaults()
9✔
546
    {
547
        $this->files           = [];
9✔
548
        $this->standards       = ['PSR12'];
9✔
549
        $this->verbosity       = 0;
9✔
550
        $this->interactive     = false;
9✔
551
        $this->cache           = false;
9✔
552
        $this->cacheFile       = null;
9✔
553
        $this->colors          = false;
9✔
554
        $this->explain         = false;
9✔
555
        $this->local           = false;
9✔
556
        $this->showSources     = false;
9✔
557
        $this->showProgress    = false;
9✔
558
        $this->quiet           = false;
9✔
559
        $this->annotations     = true;
9✔
560
        $this->parallel        = 1;
9✔
561
        $this->tabWidth        = 0;
9✔
562
        $this->encoding        = 'utf-8';
9✔
563
        $this->extensions      = [
9✔
564
            'php' => 'PHP',
6✔
565
            'inc' => 'PHP',
6✔
566
        ];
6✔
567
        $this->sniffs          = [];
9✔
568
        $this->exclude         = [];
9✔
569
        $this->ignored         = [];
9✔
570
        $this->reportFile      = null;
9✔
571
        $this->generator       = null;
9✔
572
        $this->filter          = null;
9✔
573
        $this->bootstrap       = [];
9✔
574
        $this->basepath        = null;
9✔
575
        $this->reports         = ['full' => null];
9✔
576
        $this->reportWidth     = 'auto';
9✔
577
        $this->errorSeverity   = 5;
9✔
578
        $this->warningSeverity = 5;
9✔
579
        $this->recordErrors    = true;
9✔
580
        $this->suffix          = '';
9✔
581
        $this->stdin           = false;
9✔
582
        $this->stdinContent    = null;
9✔
583
        $this->stdinPath       = null;
9✔
584
        $this->trackTime       = false;
9✔
585
        $this->unknown         = [];
9✔
586
        $this->allowEmptyFileList = false;
9✔
587

588
        $standard = self::getConfigData('default_standard');
9✔
589
        if ($standard !== null) {
9✔
590
            $this->standards = explode(',', $standard);
6✔
591
        }
592

593
        $reportFormat = self::getConfigData('report_format');
9✔
594
        if ($reportFormat !== null) {
9✔
UNCOV
595
            $this->reports = [$reportFormat => null];
×
596
        }
597

598
        $tabWidth = self::getConfigData('tab_width');
9✔
599
        if ($tabWidth !== null) {
9✔
UNCOV
600
            $this->tabWidth = (int) $tabWidth;
×
601
        }
602

603
        $encoding = self::getConfigData('encoding');
9✔
604
        if ($encoding !== null) {
9✔
UNCOV
605
            $this->encoding = strtolower($encoding);
×
606
        }
607

608
        $severity = self::getConfigData('severity');
9✔
609
        if ($severity !== null) {
9✔
UNCOV
610
            $this->errorSeverity   = (int) $severity;
×
UNCOV
611
            $this->warningSeverity = (int) $severity;
×
612
        }
613

614
        $severity = self::getConfigData('error_severity');
9✔
615
        if ($severity !== null) {
9✔
UNCOV
616
            $this->errorSeverity = (int) $severity;
×
617
        }
618

619
        $severity = self::getConfigData('warning_severity');
9✔
620
        if ($severity !== null) {
9✔
UNCOV
621
            $this->warningSeverity = (int) $severity;
×
622
        }
623

624
        $showWarnings = self::getConfigData('show_warnings');
9✔
625
        if ($showWarnings !== null) {
9✔
626
            $showWarnings = (bool) $showWarnings;
3✔
627
            if ($showWarnings === false) {
3✔
628
                $this->warningSeverity = 0;
3✔
629
            }
630
        }
631

632
        $reportWidth = self::getConfigData('report_width');
9✔
633
        if ($reportWidth !== null) {
9✔
634
            $this->reportWidth = $reportWidth;
3✔
635
        }
636

637
        $showProgress = self::getConfigData('show_progress');
9✔
638
        if ($showProgress !== null) {
9✔
UNCOV
639
            $this->showProgress = (bool) $showProgress;
×
640
        }
641

642
        $quiet = self::getConfigData('quiet');
9✔
643
        if ($quiet !== null) {
9✔
UNCOV
644
            $this->quiet = (bool) $quiet;
×
645
        }
646

647
        $colors = self::getConfigData('colors');
9✔
648
        if ($colors !== null) {
9✔
UNCOV
649
            $this->colors = (bool) $colors;
×
650
        }
651

652
        $cache = self::getConfigData('cache');
9✔
653
        if ($cache !== null) {
9✔
UNCOV
654
            $this->cache = (bool) $cache;
×
655
        }
656

657
        $parallel = self::getConfigData('parallel');
9✔
658
        if ($parallel !== null) {
9✔
UNCOV
659
            $this->parallel = max((int) $parallel, 1);
×
660
        }
661
    }
3✔
662

663

664
    /**
665
     * Processes a short (-e) command line argument.
666
     *
667
     * @param string $arg The command line argument.
668
     * @param int    $pos The position of the argument on the command line.
669
     *
670
     * @return void
671
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException
672
     */
673
    public function processShortArgument(string $arg, int $pos)
30✔
674
    {
675
        switch ($arg) {
9✔
676
            case 'h':
30✔
677
            case '?':
30✔
678
                $this->printUsage();
×
679
                throw new DeepExitException('', ExitCode::OKAY);
×
680
            case 'i' :
30✔
681
                $output = Standards::prepareInstalledStandardsForDisplay() . PHP_EOL;
×
UNCOV
682
                throw new DeepExitException($output, ExitCode::OKAY);
×
683
            case 'v' :
30✔
UNCOV
684
                if ($this->quiet === true) {
×
685
                    // Ignore when quiet mode is enabled.
686
                    break;
×
687
                }
688

UNCOV
689
                $this->verbosity++;
×
690
                $this->overriddenDefaults['verbosity'] = true;
×
691
                break;
×
692
            case 'l' :
30✔
UNCOV
693
                $this->local = true;
×
694
                $this->overriddenDefaults['local'] = true;
×
695
                break;
×
696
            case 's' :
30✔
UNCOV
697
                $this->showSources = true;
×
698
                $this->overriddenDefaults['showSources'] = true;
×
699
                break;
×
700
            case 'a' :
30✔
UNCOV
701
                $this->interactive = true;
×
702
                $this->overriddenDefaults['interactive'] = true;
×
703
                break;
×
704
            case 'e':
30✔
UNCOV
705
                $this->explain = true;
×
706
                $this->overriddenDefaults['explain'] = true;
×
UNCOV
707
                break;
×
708
            case 'p' :
30✔
UNCOV
709
                if ($this->quiet === true) {
×
710
                    // Ignore when quiet mode is enabled.
711
                    break;
×
712
                }
713

UNCOV
714
                $this->showProgress = true;
×
UNCOV
715
                $this->overriddenDefaults['showProgress'] = true;
×
716
                break;
×
717
            case 'q' :
30✔
718
                // Quiet mode disables a few other settings as well.
UNCOV
719
                $this->quiet        = true;
×
720
                $this->showProgress = false;
×
721
                $this->verbosity    = 0;
×
722

723
                $this->overriddenDefaults['quiet'] = true;
×
724
                break;
×
725
            case 'm' :
30✔
UNCOV
726
                $this->recordErrors = false;
×
UNCOV
727
                $this->overriddenDefaults['recordErrors'] = true;
×
UNCOV
728
                break;
×
729
            case 'd' :
30✔
730
                $ini = explode('=', $this->cliArgs[($pos + 1)]);
30✔
731
                $this->cliArgs[($pos + 1)] = '';
30✔
732
                if (isset($ini[1]) === false) {
30✔
733
                    // Set to true.
734
                    $ini[1] = '1';
3✔
735
                }
736

737
                $current = ini_get($ini[0]);
30✔
738
                if ($current === false) {
30✔
739
                    // Ini setting which doesn't exist, or is from an unavailable extension.
740
                    // Silently ignore it.
741
                    break;
4✔
742
                }
743

744
                $changed = ini_set($ini[0], $ini[1]);
26✔
745
                if ($changed === false && ini_get($ini[0]) !== $ini[1]) {
26✔
746
                    $error  = sprintf('ERROR: Ini option "%s" cannot be changed at runtime.', $ini[0]) . PHP_EOL;
12✔
747
                    $error .= $this->printShortUsage(true);
12✔
748
                    throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
12✔
749
                }
750
                break;
14✔
751
            case 'n' :
×
UNCOV
752
                if (isset($this->overriddenDefaults['warningSeverity']) === false) {
×
753
                    $this->warningSeverity = 0;
×
754
                    $this->overriddenDefaults['warningSeverity'] = true;
×
755
                }
756
                break;
×
757
            case 'w' :
×
UNCOV
758
                if (isset($this->overriddenDefaults['warningSeverity']) === false) {
×
759
                    $this->warningSeverity = $this->errorSeverity;
×
UNCOV
760
                    $this->overriddenDefaults['warningSeverity'] = true;
×
761
                }
762
                break;
×
763
            default:
764
                if ($this->dieOnUnknownArg === false) {
×
UNCOV
765
                    $unknown       = $this->unknown;
×
766
                    $unknown[]     = $arg;
×
UNCOV
767
                    $this->unknown = $unknown;
×
768
                } else {
UNCOV
769
                    $this->processUnknownArgument('-' . $arg, $pos);
×
770
                }
771
        }
772
    }
5✔
773

774

775
    /**
776
     * Processes a long (--example) command-line argument.
777
     *
778
     * @param string $arg The command line argument.
779
     * @param int    $pos The position of the argument on the command line.
780
     *
781
     * @return void
782
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException
783
     */
784
    public function processLongArgument(string $arg, int $pos)
189✔
785
    {
786
        switch ($arg) {
63✔
787
            case 'help':
189✔
788
                $this->printUsage();
×
789
                throw new DeepExitException('', ExitCode::OKAY);
×
790
            case 'version':
189✔
UNCOV
791
                $output  = 'PHP_CodeSniffer version ' . self::VERSION . ' (' . self::STABILITY . ') ';
×
792
                $output .= 'by Squiz and PHPCSStandards' . PHP_EOL;
×
793
                throw new DeepExitException($output, ExitCode::OKAY);
×
794
            case 'colors':
189✔
UNCOV
795
                if (isset($this->overriddenDefaults['colors']) === true) {
×
796
                    break;
×
797
                }
798

UNCOV
799
                $this->colors = true;
×
800
                $this->overriddenDefaults['colors'] = true;
×
801
                break;
×
802
            case 'no-colors':
189✔
UNCOV
803
                if (isset($this->overriddenDefaults['colors']) === true) {
×
804
                    break;
×
805
                }
806

UNCOV
807
                $this->colors = false;
×
808
                $this->overriddenDefaults['colors'] = true;
×
809
                break;
×
810
            case 'cache':
189✔
UNCOV
811
                if (isset($this->overriddenDefaults['cache']) === true) {
×
812
                    break;
×
813
                }
814

UNCOV
815
                $this->cache = true;
×
816
                $this->overriddenDefaults['cache'] = true;
×
817
                break;
×
818
            case 'no-cache':
189✔
UNCOV
819
                if (isset($this->overriddenDefaults['cache']) === true) {
×
820
                    break;
×
821
                }
822

UNCOV
823
                $this->cache = false;
×
824
                $this->overriddenDefaults['cache'] = true;
×
825
                break;
×
826
            case 'ignore-annotations':
189✔
UNCOV
827
                if (isset($this->overriddenDefaults['annotations']) === true) {
×
828
                    break;
×
829
                }
830

UNCOV
831
                $this->annotations = false;
×
832
                $this->overriddenDefaults['annotations'] = true;
×
833
                break;
×
834
            case 'allow-empty-file-list':
189✔
NEW
835
                if (isset($this->overriddenDefaults['allowEmptyFileList']) === true) {
×
NEW
836
                    break;
×
837
                }
838

NEW
UNCOV
839
                $this->allowEmptyFileList = true;
×
NEW
840
                $this->overriddenDefaults['allowEmptyFileList'] = true;
×
NEW
841
                break;
×
842
            case 'config-set':
189✔
UNCOV
843
                if (isset($this->cliArgs[($pos + 1)]) === false
×
UNCOV
844
                    || isset($this->cliArgs[($pos + 2)]) === false
×
845
                ) {
846
                    $error  = 'ERROR: Setting a config option requires a name and value' . PHP_EOL . PHP_EOL;
×
847
                    $error .= $this->printShortUsage(true);
×
UNCOV
848
                    throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
×
849
                }
850

UNCOV
851
                $key     = $this->cliArgs[($pos + 1)];
×
852
                $value   = $this->cliArgs[($pos + 2)];
×
853
                $current = self::getConfigData($key);
×
854

855
                try {
UNCOV
856
                    $this->setConfigData($key, $value);
×
857
                } catch (Exception $e) {
×
UNCOV
858
                    throw new DeepExitException($e->getMessage() . PHP_EOL, ExitCode::PROCESS_ERROR);
×
859
                }
860

861
                $output = 'Using config file: ' . self::$configDataFile . PHP_EOL . PHP_EOL;
×
862

UNCOV
863
                if ($current === null) {
×
UNCOV
864
                    $output .= "Config value \"$key\" added successfully" . PHP_EOL;
×
865
                } else {
UNCOV
866
                    $output .= "Config value \"$key\" updated successfully; old value was \"$current\"" . PHP_EOL;
×
867
                }
868
                throw new DeepExitException($output, ExitCode::OKAY);
×
869
            case 'config-delete':
189✔
870
                if (isset($this->cliArgs[($pos + 1)]) === false) {
×
UNCOV
871
                    $error  = 'ERROR: Deleting a config option requires the name of the option' . PHP_EOL . PHP_EOL;
×
UNCOV
872
                    $error .= $this->printShortUsage(true);
×
873
                    throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
×
874
                }
875

UNCOV
876
                $output = 'Using config file: ' . self::$configDataFile . PHP_EOL . PHP_EOL;
×
877

878
                $key     = $this->cliArgs[($pos + 1)];
×
UNCOV
879
                $current = self::getConfigData($key);
×
880
                if ($current === null) {
×
UNCOV
881
                    $output .= "Config value \"$key\" has not been set" . PHP_EOL;
×
882
                } else {
883
                    try {
884
                        $this->setConfigData($key, null);
×
885
                    } catch (Exception $e) {
×
UNCOV
886
                        throw new DeepExitException($e->getMessage() . PHP_EOL, ExitCode::PROCESS_ERROR);
×
887
                    }
888

UNCOV
889
                    $output .= "Config value \"$key\" removed successfully; old value was \"$current\"" . PHP_EOL;
×
890
                }
891
                throw new DeepExitException($output, ExitCode::OKAY);
×
892
            case 'config-show':
189✔
UNCOV
893
                $data    = self::getAllConfigData();
×
UNCOV
894
                $output  = 'Using config file: ' . self::$configDataFile . PHP_EOL . PHP_EOL;
×
895
                $output .= $this->prepareConfigDataForDisplay($data);
×
896
                throw new DeepExitException($output, ExitCode::OKAY);
×
897
            case 'runtime-set':
189✔
898
                if (isset($this->cliArgs[($pos + 1)]) === false
×
899
                    || isset($this->cliArgs[($pos + 2)]) === false
×
900
                ) {
901
                    $error  = 'ERROR: Setting a runtime config option requires a name and value' . PHP_EOL . PHP_EOL;
×
UNCOV
902
                    $error .= $this->printShortUsage(true);
×
UNCOV
903
                    throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
×
904
                }
905

UNCOV
906
                $key   = $this->cliArgs[($pos + 1)];
×
UNCOV
907
                $value = $this->cliArgs[($pos + 2)];
×
UNCOV
908
                $this->cliArgs[($pos + 1)] = '';
×
UNCOV
909
                $this->cliArgs[($pos + 2)] = '';
×
UNCOV
910
                $this->setConfigData($key, $value, true);
×
UNCOV
911
                if (isset($this->overriddenDefaults['runtime-set']) === false) {
×
UNCOV
912
                    $this->overriddenDefaults['runtime-set'] = [];
×
913
                }
914

UNCOV
915
                $this->overriddenDefaults['runtime-set'][$key] = true;
×
UNCOV
916
                break;
×
917
            default:
918
                if (substr($arg, 0, 7) === 'sniffs=') {
189✔
919
                    if (isset($this->overriddenDefaults['sniffs']) === true) {
57✔
920
                        break;
3✔
921
                    }
922

923
                    $this->sniffs = $this->parseSniffCodes(substr($arg, 7), 'sniffs');
57✔
924
                    $this->overriddenDefaults['sniffs'] = true;
21✔
925
                } elseif (substr($arg, 0, 8) === 'exclude=') {
132✔
926
                    if (isset($this->overriddenDefaults['exclude']) === true) {
57✔
927
                        break;
3✔
928
                    }
929

930
                    $this->exclude = $this->parseSniffCodes(substr($arg, 8), 'exclude');
57✔
931
                    $this->overriddenDefaults['exclude'] = true;
21✔
932
                } elseif (substr($arg, 0, 6) === 'cache=') {
75✔
933
                    if ((isset($this->overriddenDefaults['cache']) === true
×
UNCOV
934
                        && $this->cache === false)
×
UNCOV
935
                        || isset($this->overriddenDefaults['cacheFile']) === true
×
936
                    ) {
937
                        break;
×
938
                    }
939

940
                    // Turn caching on.
941
                    $this->cache = true;
×
942
                    $this->overriddenDefaults['cache'] = true;
×
943

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

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

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

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

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

975
                    $this->overriddenDefaults['cacheFile'] = true;
×
976

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

993
                        $bootstrap[] = $path;
×
994
                    }
995

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

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

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

1016
                        $this->processFilePath($inputFile);
×
1017
                    }
1018
                } elseif (substr($arg, 0, 11) === 'stdin-path=') {
75✔
1019
                    if (isset($this->overriddenDefaults['stdinPath']) === true) {
×
UNCOV
1020
                        break;
×
1021
                    }
1022

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

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

UNCOV
1030
                    $this->overriddenDefaults['stdinPath'] = true;
×
1031
                } elseif (substr($arg, 0, 12) === 'report-file=') {
75✔
1032
                    if (PHP_CODESNIFFER_CBF === true || isset($this->overriddenDefaults['reportFile']) === true) {
6✔
1033
                        break;
3✔
1034
                    }
1035

1036
                    $this->reportFile = Common::realpath(substr($arg, 12));
3✔
1037

1038
                    // It may not exist and return false instead.
1039
                    if ($this->reportFile === false) {
3✔
1040
                        $this->reportFile = substr($arg, 12);
3✔
1041

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

1049
                        $this->reportFile = $dir . '/' . basename($this->reportFile);
3✔
1050
                    }
1051

1052
                    $this->overriddenDefaults['reportFile'] = true;
3✔
1053

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

1064
                    $this->reportWidth = substr($arg, 13);
9✔
1065
                    $this->overriddenDefaults['reportWidth'] = true;
9✔
1066
                } elseif (substr($arg, 0, 9) === 'basepath=') {
69✔
1067
                    if (isset($this->overriddenDefaults['basepath']) === true) {
×
UNCOV
1068
                        break;
×
1069
                    }
1070

1071
                    $this->overriddenDefaults['basepath'] = true;
×
1072

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

1078
                    $basepath = Common::realpath(substr($arg, 9));
×
1079

1080
                    // It may not exist and return false instead.
UNCOV
1081
                    if ($basepath === false) {
×
1082
                        $this->basepath = substr($arg, 9);
×
1083
                    } else {
1084
                        $this->basepath = $basepath;
×
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;
×
1089
                        $error .= $this->printShortUsage(true);
×
UNCOV
1090
                        throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
×
1091
                    }
1092
                } elseif ((substr($arg, 0, 7) === 'report=' || substr($arg, 0, 7) === 'report-')) {
69✔
1093
                    $reports = [];
×
1094

UNCOV
1095
                    if ($arg[6] === '-') {
×
1096
                        // This is a report with file output.
1097
                        $split = strpos($arg, '=');
×
1098
                        if ($split === false) {
×
1099
                            $report = substr($arg, 7);
×
1100
                            $output = null;
×
1101
                        } else {
UNCOV
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) {
×
UNCOV
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, ExitCode::PROCESS_ERROR);
×
1112
                                }
1113

UNCOV
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, ExitCode::PROCESS_ERROR);
×
1120
                                }
1121
                            }
1122
                        }
1123

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

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

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

UNCOV
1144
                    $this->overriddenDefaults['reports'] = true;
×
1145
                } elseif (substr($arg, 0, 7) === 'filter=') {
69✔
UNCOV
1146
                    if (isset($this->overriddenDefaults['filter']) === true) {
×
UNCOV
1147
                        break;
×
1148
                    }
1149

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

1158
                    $this->overriddenDefaults['standards'] = true;
9✔
1159
                } elseif (substr($arg, 0, 11) === 'extensions=') {
60✔
1160
                    if (isset($this->overriddenDefaults['extensions']) === true) {
30✔
1161
                        break;
3✔
1162
                    }
1163

1164
                    $extensionsString = substr($arg, 11);
30✔
1165
                    $newExtensions    = [];
30✔
1166
                    if (empty($extensionsString) === false) {
30✔
1167
                        $extensions = explode(',', $extensionsString);
27✔
1168
                        foreach ($extensions as $ext) {
27✔
1169
                            if (strpos($ext, '/') !== false) {
27✔
1170
                                // They specified the tokenizer too.
1171
                                list($ext, $tokenizer) = explode('/', $ext);
12✔
1172
                                if (strtoupper($tokenizer) !== 'PHP') {
12✔
1173
                                    $error  = 'ERROR: Specifying the tokenizer to use for an extension is no longer supported.' . PHP_EOL;
9✔
1174
                                    $error .= 'PHP_CodeSniffer >= 4.0 only supports scanning PHP files.' . PHP_EOL;
9✔
1175
                                    $error .= 'Received: ' . substr($arg, 11) . PHP_EOL . PHP_EOL;
9✔
1176
                                    $error .= $this->printShortUsage(true);
9✔
1177
                                    throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
9✔
1178
                                }
1179
                            }
1180

1181
                            $newExtensions[$ext] = 'PHP';
21✔
1182
                        }
1183
                    }
1184

1185
                    $this->extensions = $newExtensions;
21✔
1186
                    $this->overriddenDefaults['extensions'] = true;
21✔
1187
                } elseif (substr($arg, 0, 7) === 'suffix=') {
30✔
1188
                    if (isset($this->overriddenDefaults['suffix']) === true) {
×
1189
                        break;
×
1190
                    }
1191

1192
                    $this->suffix = substr($arg, 7);
×
1193
                    $this->overriddenDefaults['suffix'] = true;
×
1194
                } elseif (substr($arg, 0, 9) === 'parallel=') {
30✔
UNCOV
1195
                    if (isset($this->overriddenDefaults['parallel']) === true) {
×
UNCOV
1196
                        break;
×
1197
                    }
1198

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

1208
                    if (isset($this->overriddenDefaults['warningSeverity']) === false) {
×
1209
                        $this->overriddenDefaults['warningSeverity'] = true;
×
1210
                    }
1211
                } elseif (substr($arg, 0, 15) === 'error-severity=') {
30✔
1212
                    if (isset($this->overriddenDefaults['errorSeverity']) === true) {
×
1213
                        break;
×
1214
                    }
1215

1216
                    $this->errorSeverity = (int) substr($arg, 15);
×
UNCOV
1217
                    $this->overriddenDefaults['errorSeverity'] = true;
×
1218
                } elseif (substr($arg, 0, 17) === 'warning-severity=') {
30✔
UNCOV
1219
                    if (isset($this->overriddenDefaults['warningSeverity']) === true) {
×
UNCOV
1220
                        break;
×
1221
                    }
1222

1223
                    $this->warningSeverity = (int) substr($arg, 17);
×
UNCOV
1224
                    $this->overriddenDefaults['warningSeverity'] = true;
×
1225
                } elseif (substr($arg, 0, 7) === 'ignore=') {
30✔
1226
                    if (isset($this->overriddenDefaults['ignored']) === true) {
×
1227
                        break;
×
1228
                    }
1229

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

1237
                    $ignored = [];
×
UNCOV
1238
                    foreach ($patterns as $pattern) {
×
UNCOV
1239
                        $pattern = trim($pattern);
×
UNCOV
1240
                        if ($pattern === '') {
×
UNCOV
1241
                            continue;
×
1242
                        }
1243

UNCOV
1244
                        $ignored[$pattern] = 'absolute';
×
1245
                    }
1246

UNCOV
1247
                    $this->ignored = $ignored;
×
UNCOV
1248
                    $this->overriddenDefaults['ignored'] = true;
×
1249
                } elseif (substr($arg, 0, 10) === 'generator='
30✔
1250
                    && PHP_CODESNIFFER_CBF === false
30✔
1251
                ) {
1252
                    if (isset($this->overriddenDefaults['generator']) === true) {
30✔
1253
                        break;
3✔
1254
                    }
1255

1256
                    $generatorName          = substr($arg, 10);
30✔
1257
                    $lowerCaseGeneratorName = strtolower($generatorName);
30✔
1258

1259
                    if (isset(self::VALID_GENERATORS[$lowerCaseGeneratorName]) === false) {
30✔
1260
                        $validOptions = implode(', ', self::VALID_GENERATORS);
9✔
1261
                        $validOptions = substr_replace($validOptions, ' and', strrpos($validOptions, ','), 1);
9✔
1262
                        $error        = sprintf(
9✔
1263
                            'ERROR: "%s" is not a valid generator. The following generators are supported: %s.' . PHP_EOL . PHP_EOL,
9✔
1264
                            $generatorName,
9✔
1265
                            $validOptions
9✔
1266
                        );
6✔
1267
                        $error       .= $this->printShortUsage(true);
9✔
1268
                        throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
9✔
1269
                    }
1270

1271
                    $this->generator = self::VALID_GENERATORS[$lowerCaseGeneratorName];
21✔
1272
                    $this->overriddenDefaults['generator'] = true;
21✔
UNCOV
1273
                } elseif (substr($arg, 0, 9) === 'encoding=') {
×
1274
                    if (isset($this->overriddenDefaults['encoding']) === true) {
×
1275
                        break;
×
1276
                    }
1277

1278
                    $this->encoding = strtolower(substr($arg, 9));
×
UNCOV
1279
                    $this->overriddenDefaults['encoding'] = true;
×
1280
                } elseif (substr($arg, 0, 10) === 'tab-width=') {
×
UNCOV
1281
                    if (isset($this->overriddenDefaults['tabWidth']) === true) {
×
1282
                        break;
×
1283
                    }
1284

1285
                    $this->tabWidth = (int) substr($arg, 10);
×
1286
                    $this->overriddenDefaults['tabWidth'] = true;
×
1287
                } else {
UNCOV
1288
                    if ($this->dieOnUnknownArg === false) {
×
UNCOV
1289
                        $eqPos = strpos($arg, '=');
×
1290
                        try {
1291
                            $unknown = $this->unknown;
×
1292

UNCOV
1293
                            if ($eqPos === false) {
×
UNCOV
1294
                                $unknown[$arg] = $arg;
×
1295
                            } else {
UNCOV
1296
                                $value         = substr($arg, ($eqPos + 1));
×
UNCOV
1297
                                $arg           = substr($arg, 0, $eqPos);
×
UNCOV
1298
                                $unknown[$arg] = $value;
×
1299
                            }
1300

UNCOV
1301
                            $this->unknown = $unknown;
×
UNCOV
1302
                        } catch (RuntimeException $e) {
×
1303
                            // Value is not valid, so just ignore it.
1304
                        }
1305
                    } else {
UNCOV
1306
                        $this->processUnknownArgument('--' . $arg, $pos);
×
1307
                    }
1308
                }
1309
                break;
96✔
1310
        }
1311
    }
33✔
1312

1313

1314
    /**
1315
     * Parse supplied string into a list of validated sniff codes.
1316
     *
1317
     * @param string $input    Comma-separated string of sniff codes.
1318
     * @param string $argument The name of the argument which is being processed.
1319
     *
1320
     * @return array<string>
1321
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException When any of the provided codes are not valid as sniff codes.
1322
     */
1323
    private function parseSniffCodes(string $input, string $argument)
114✔
1324
    {
1325
        $errors = [];
114✔
1326
        $sniffs = [];
114✔
1327

1328
        $possibleSniffs = array_filter(explode(',', $input));
114✔
1329

1330
        if ($possibleSniffs === []) {
114✔
1331
            $errors[] = 'No codes specified / empty argument';
18✔
1332
        }
1333

1334
        foreach ($possibleSniffs as $sniff) {
114✔
1335
            $sniff = trim($sniff);
96✔
1336

1337
            $partCount = substr_count($sniff, '.');
96✔
1338
            if ($partCount === 2) {
96✔
1339
                // Correct number of parts.
1340
                $sniffs[] = $sniff;
54✔
1341
                continue;
54✔
1342
            }
1343

1344
            if ($partCount === 0) {
54✔
1345
                $errors[] = 'Standard codes are not supported: ' . $sniff;
12✔
1346
            } elseif ($partCount === 1) {
42✔
1347
                $errors[] = 'Category codes are not supported: ' . $sniff;
18✔
1348
            } elseif ($partCount === 3) {
24✔
1349
                $errors[] = 'Message codes are not supported: ' . $sniff;
18✔
1350
            } else {
1351
                $errors[] = 'Too many parts: ' . $sniff;
12✔
1352
            }
1353

1354
            if ($partCount > 2) {
54✔
1355
                $parts    = explode('.', $sniff, 4);
24✔
1356
                $sniffs[] = $parts[0] . '.' . $parts[1] . '.' . $parts[2];
24✔
1357
            }
1358
        }
1359

1360
        $sniffs = array_reduce(
114✔
1361
            $sniffs,
114✔
1362
            static function ($carry, $item) {
76✔
1363
                $lower = strtolower($item);
78✔
1364

1365
                foreach ($carry as $found) {
78✔
1366
                    if ($lower === strtolower($found)) {
36✔
1367
                        // This sniff is already in our list.
1368
                        return $carry;
24✔
1369
                    }
1370
                }
1371

1372
                $carry[] = $item;
78✔
1373

1374
                return $carry;
78✔
1375
            },
114✔
1376
            []
114✔
1377
        );
76✔
1378

1379
        if ($errors !== []) {
114✔
1380
            $error  = 'ERROR: The --' . $argument . ' option only supports sniff codes.' . PHP_EOL;
72✔
1381
            $error .= 'Sniff codes are in the form "Standard.Category.Sniff".' . PHP_EOL;
72✔
1382
            $error .= PHP_EOL;
72✔
1383
            $error .= 'The following problems were detected:' . PHP_EOL;
72✔
1384
            $error .= '* ' . implode(PHP_EOL . '* ', $errors) . PHP_EOL;
72✔
1385

1386
            if ($sniffs !== []) {
72✔
1387
                $error .= PHP_EOL;
36✔
1388
                $error .= 'Perhaps try --' . $argument . '="' . implode(',', $sniffs) . '" instead.' . PHP_EOL;
36✔
1389
            }
1390

1391
            $error .= PHP_EOL;
72✔
1392
            $error .= $this->printShortUsage(true);
72✔
1393
            throw new DeepExitException(ltrim($error), ExitCode::PROCESS_ERROR);
72✔
1394
        }
1395

1396
        return $sniffs;
42✔
1397
    }
1398

1399

1400
    /**
1401
     * Processes an unknown command line argument.
1402
     *
1403
     * Assumes all unknown arguments are files and folders to check.
1404
     *
1405
     * @param string $arg The command line argument.
1406
     * @param int    $pos The position of the argument on the command line.
1407
     *
1408
     * @return void
1409
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException
1410
     */
UNCOV
1411
    public function processUnknownArgument(string $arg, int $pos)
×
1412
    {
1413
        // We don't know about any additional switches; just files.
UNCOV
1414
        if ($arg[0] === '-') {
×
UNCOV
1415
            if ($this->dieOnUnknownArg === false) {
×
UNCOV
1416
                return;
×
1417
            }
1418

UNCOV
1419
            $error  = "ERROR: option \"$arg\" not known" . PHP_EOL . PHP_EOL;
×
UNCOV
1420
            $error .= $this->printShortUsage(true);
×
UNCOV
1421
            throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
×
1422
        }
1423

UNCOV
1424
        $this->processFilePath($arg);
×
1425
    }
1426

1427

1428
    /**
1429
     * Processes a file path and add it to the file list.
1430
     *
1431
     * @param string $path The path to the file to add.
1432
     *
1433
     * @return void
1434
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException
1435
     */
UNCOV
1436
    public function processFilePath(string $path)
×
1437
    {
1438
        // If we are processing STDIN, don't record any files to check.
1439
        if ($this->stdin === true) {
×
1440
            return;
×
1441
        }
1442

UNCOV
1443
        $file = Common::realpath($path);
×
1444
        if (file_exists($file) === false) {
×
1445
            if ($this->dieOnUnknownArg === false) {
×
1446
                return;
×
1447
            }
1448

UNCOV
1449
            $error  = 'ERROR: The file "' . $path . '" does not exist.' . PHP_EOL . PHP_EOL;
×
UNCOV
1450
            $error .= $this->printShortUsage(true);
×
UNCOV
1451
            throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
×
1452
        } else {
1453
            // Can't modify the files array directly because it's not a real
1454
            // class member, so need to use this little get/modify/set trick.
UNCOV
1455
            $files       = $this->files;
×
UNCOV
1456
            $files[]     = $file;
×
1457
            $this->files = $files;
×
UNCOV
1458
            $this->overriddenDefaults['files'] = true;
×
1459
        }
1460
    }
1461

1462

1463
    /**
1464
     * Prints out the usage information for this script.
1465
     *
1466
     * @return void
1467
     */
UNCOV
1468
    public function printUsage()
×
1469
    {
UNCOV
1470
        echo PHP_EOL;
×
1471

UNCOV
1472
        if (PHP_CODESNIFFER_CBF === true) {
×
UNCOV
1473
            $this->printPHPCBFUsage();
×
1474
        } else {
UNCOV
1475
            $this->printPHPCSUsage();
×
1476
        }
1477

UNCOV
1478
        echo PHP_EOL;
×
1479
    }
1480

1481

1482
    /**
1483
     * Prints out the short usage information for this script.
1484
     *
1485
     * @param bool $returnOutput If TRUE, the usage string is returned
1486
     *                           instead of output to screen.
1487
     *
1488
     * @return string|void
1489
     */
1490
    public function printShortUsage(bool $returnOutput = false)
×
1491
    {
UNCOV
1492
        if (PHP_CODESNIFFER_CBF === true) {
×
1493
            $usage = 'Run "phpcbf --help" for usage information';
×
1494
        } else {
UNCOV
1495
            $usage = 'Run "phpcs --help" for usage information';
×
1496
        }
1497

UNCOV
1498
        $usage .= PHP_EOL . PHP_EOL;
×
1499

UNCOV
1500
        if ($returnOutput === true) {
×
UNCOV
1501
            return $usage;
×
1502
        }
1503

1504
        echo $usage;
×
1505
    }
1506

1507

1508
    /**
1509
     * Prints out the usage information for PHPCS.
1510
     *
1511
     * @return void
1512
     */
1513
    public function printPHPCSUsage()
×
1514
    {
UNCOV
1515
        $longOptions   = Help::DEFAULT_LONG_OPTIONS;
×
1516
        $longOptions[] = 'cache';
×
UNCOV
1517
        $longOptions[] = 'no-cache';
×
1518
        $longOptions[] = 'report';
×
UNCOV
1519
        $longOptions[] = 'report-file';
×
UNCOV
1520
        $longOptions[] = 'report-report';
×
UNCOV
1521
        $longOptions[] = 'config-explain';
×
UNCOV
1522
        $longOptions[] = 'config-set';
×
UNCOV
1523
        $longOptions[] = 'config-delete';
×
UNCOV
1524
        $longOptions[] = 'config-show';
×
UNCOV
1525
        $longOptions[] = 'generator';
×
1526

1527
        $shortOptions = Help::DEFAULT_SHORT_OPTIONS . 'aems';
×
1528

1529
        (new Help($this, $longOptions, $shortOptions))->display();
×
1530
    }
1531

1532

1533
    /**
1534
     * Prints out the usage information for PHPCBF.
1535
     *
1536
     * @return void
1537
     */
UNCOV
1538
    public function printPHPCBFUsage()
×
1539
    {
UNCOV
1540
        $longOptions   = Help::DEFAULT_LONG_OPTIONS;
×
UNCOV
1541
        $longOptions[] = 'suffix';
×
UNCOV
1542
        $shortOptions  = Help::DEFAULT_SHORT_OPTIONS;
×
1543

UNCOV
1544
        (new Help($this, $longOptions, $shortOptions))->display();
×
1545
    }
1546

1547

1548
    /**
1549
     * Get a single config value.
1550
     *
1551
     * @param string $key The name of the config value.
1552
     *
1553
     * @return string|null
1554
     * @see    setConfigData()
1555
     * @see    getAllConfigData()
1556
     */
1557
    public static function getConfigData(string $key)
6✔
1558
    {
1559
        $phpCodeSnifferConfig = self::getAllConfigData();
6✔
1560

1561
        if ($phpCodeSnifferConfig === null) {
6✔
UNCOV
1562
            return null;
×
1563
        }
1564

1565
        if (isset($phpCodeSnifferConfig[$key]) === false) {
6✔
1566
            return null;
6✔
1567
        }
1568

1569
        return $phpCodeSnifferConfig[$key];
6✔
1570
    }
1571

1572

1573
    /**
1574
     * Get the path to an executable utility.
1575
     *
1576
     * @param string $name The name of the executable utility.
1577
     *
1578
     * @return string|null
1579
     * @see    getConfigData()
1580
     */
UNCOV
1581
    public static function getExecutablePath(string $name)
×
1582
    {
1583
        $data = self::getConfigData($name . '_path');
×
UNCOV
1584
        if ($data !== null) {
×
UNCOV
1585
            return $data;
×
1586
        }
1587

UNCOV
1588
        if ($name === 'php') {
×
1589
            // For php, we know the executable path. There's no need to look it up.
UNCOV
1590
            return PHP_BINARY;
×
1591
        }
1592

1593
        if (array_key_exists($name, self::$executablePaths) === true) {
×
1594
            return self::$executablePaths[$name];
×
1595
        }
1596

1597
        if (PHP_OS_FAMILY === 'Windows') {
×
1598
            $cmd = 'where ' . escapeshellarg($name) . ' 2> nul';
×
1599
        } else {
UNCOV
1600
            $cmd = 'which ' . escapeshellarg($name) . ' 2> /dev/null';
×
1601
        }
1602

UNCOV
1603
        $result = exec($cmd, $output, $retVal);
×
UNCOV
1604
        if ($retVal !== 0) {
×
UNCOV
1605
            $result = null;
×
1606
        }
1607

UNCOV
1608
        self::$executablePaths[$name] = $result;
×
UNCOV
1609
        return $result;
×
1610
    }
1611

1612

1613
    /**
1614
     * Set a single config value.
1615
     *
1616
     * @param string      $key   The name of the config value.
1617
     * @param string|null $value The value to set. If null, the config
1618
     *                           entry is deleted, reverting it to the
1619
     *                           default value.
1620
     * @param boolean     $temp  Set this config data temporarily for this
1621
     *                           script run. This will not write the config
1622
     *                           data to the config file.
1623
     *
1624
     * @return bool
1625
     * @see    getConfigData()
1626
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException If the config file can not be written.
1627
     */
1628
    public function setConfigData(string $key, ?string $value, bool $temp = false)
×
1629
    {
UNCOV
1630
        if (isset($this->overriddenDefaults['runtime-set']) === true
×
1631
            && isset($this->overriddenDefaults['runtime-set'][$key]) === true
×
1632
        ) {
UNCOV
1633
            return false;
×
1634
        }
1635

UNCOV
1636
        if ($temp === false) {
×
1637
            $path = '';
×
1638
            if (is_callable('\Phar::running') === true) {
×
UNCOV
1639
                $path = Phar::running(false);
×
1640
            }
1641

UNCOV
1642
            if ($path !== '') {
×
UNCOV
1643
                $configFile = dirname($path) . DIRECTORY_SEPARATOR . 'CodeSniffer.conf';
×
1644
            } else {
1645
                $configFile = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'CodeSniffer.conf';
×
1646
            }
1647

1648
            if (is_file($configFile) === true
×
1649
                && is_writable($configFile) === false
×
1650
            ) {
UNCOV
1651
                $error = 'ERROR: Config file ' . $configFile . ' is not writable' . PHP_EOL . PHP_EOL;
×
1652
                throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
×
1653
            }
1654
        }
1655

1656
        $phpCodeSnifferConfig = self::getAllConfigData();
×
1657

1658
        if ($value === null) {
×
UNCOV
1659
            if (isset($phpCodeSnifferConfig[$key]) === true) {
×
1660
                unset($phpCodeSnifferConfig[$key]);
×
1661
            }
1662
        } else {
UNCOV
1663
            $phpCodeSnifferConfig[$key] = $value;
×
1664
        }
1665

UNCOV
1666
        if ($temp === false) {
×
UNCOV
1667
            $output  = '<' . '?php' . "\n" . ' $phpCodeSnifferConfig = ';
×
1668
            $output .= var_export($phpCodeSnifferConfig, true);
×
UNCOV
1669
            $output .= ";\n?" . '>';
×
1670

UNCOV
1671
            if (file_put_contents($configFile, $output) === false) {
×
1672
                $error = 'ERROR: Config file ' . $configFile . ' could not be written' . PHP_EOL . PHP_EOL;
×
1673
                throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
×
1674
            }
1675

UNCOV
1676
            self::$configDataFile = $configFile;
×
1677
        }
1678

1679
        self::$configData = $phpCodeSnifferConfig;
×
1680

1681
        // If the installed paths are being set, make sure all known
1682
        // standards paths are added to the autoloader.
UNCOV
1683
        if ($key === 'installed_paths') {
×
UNCOV
1684
            $installedStandards = Standards::getInstalledStandardDetails();
×
UNCOV
1685
            foreach ($installedStandards as $details) {
×
UNCOV
1686
                Autoload::addSearchPath($details['path'], $details['namespace']);
×
1687
            }
1688
        }
1689

1690
        return true;
×
1691
    }
1692

1693

1694
    /**
1695
     * Get all config data.
1696
     *
1697
     * @return array<string, string>
1698
     * @see    getConfigData()
1699
     * @throws \PHP_CodeSniffer\Exceptions\DeepExitException If the config file could not be read.
1700
     */
1701
    public static function getAllConfigData()
×
1702
    {
UNCOV
1703
        if (self::$configData !== null) {
×
1704
            return self::$configData;
×
1705
        }
1706

1707
        $path = '';
×
1708
        if (is_callable('\Phar::running') === true) {
×
1709
            $path = Phar::running(false);
×
1710
        }
1711

1712
        if ($path !== '') {
×
1713
            $configFile = dirname($path) . DIRECTORY_SEPARATOR . 'CodeSniffer.conf';
×
1714
        } else {
UNCOV
1715
            $configFile = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'CodeSniffer.conf';
×
1716
        }
1717

1718
        if (is_file($configFile) === false) {
×
1719
            self::$configData = [];
×
1720
            return [];
×
1721
        }
1722

UNCOV
1723
        if (Common::isReadable($configFile) === false) {
×
UNCOV
1724
            $error = 'ERROR: Config file ' . $configFile . ' is not readable' . PHP_EOL . PHP_EOL;
×
UNCOV
1725
            throw new DeepExitException($error, ExitCode::PROCESS_ERROR);
×
1726
        }
1727

UNCOV
1728
        include $configFile;
×
UNCOV
1729
        self::$configDataFile = $configFile;
×
UNCOV
1730
        self::$configData     = $phpCodeSnifferConfig;
×
UNCOV
1731
        return self::$configData;
×
1732
    }
1733

1734

1735
    /**
1736
     * Prepares the gathered config data for display.
1737
     *
1738
     * @param array<string, string> $data The config data to format for display.
1739
     *
1740
     * @return string
1741
     */
1742
    public function prepareConfigDataForDisplay(array $data)
18✔
1743
    {
1744
        if (empty($data) === true) {
18✔
1745
            return '';
6✔
1746
        }
1747

1748
        $max  = 0;
12✔
1749
        $keys = array_keys($data);
12✔
1750
        foreach ($keys as $key) {
12✔
1751
            $len = strlen($key);
12✔
1752
            if ($len > $max) {
12✔
1753
                $max = $len;
12✔
1754
            }
1755
        }
1756

1757
        $max += 2;
12✔
1758
        ksort($data);
12✔
1759

1760
        $output = '';
12✔
1761
        foreach ($data as $name => $value) {
12✔
1762
            $output .= str_pad($name . ': ', $max) . $value . PHP_EOL;
12✔
1763
        }
1764

1765
        return $output;
12✔
1766
    }
1767

1768

1769
    /**
1770
     * Prints out the gathered config data.
1771
     *
1772
     * @param array<string, string> $data The config data to print.
1773
     *
1774
     * @deprecated 4.0.0 Use `echo Config::prepareConfigDataForDisplay()` instead.
1775
     *
1776
     * @return void
1777
     */
1778
    public function printConfigData(array $data)
3✔
1779
    {
1780
        echo $this->prepareConfigDataForDisplay($data);
3✔
1781
    }
1✔
1782
}
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

© 2026 Coveralls, Inc