comparison vendor/squizlabs/php_codesniffer/src/Config.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents
children af1871eacc83
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
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/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
11 */
12
13 namespace PHP_CodeSniffer;
14
15 use PHP_CodeSniffer\Exceptions\RuntimeException;
16 use PHP_CodeSniffer\Exceptions\DeepExitException;
17
18 class Config
19 {
20
21 /**
22 * The current version.
23 *
24 * @var string
25 */
26 const VERSION = '3.4.0';
27
28 /**
29 * Package stability; either stable, beta or alpha.
30 *
31 * @var string
32 */
33 const STABILITY = 'stable';
34
35 /**
36 * An array of settings that PHPCS and PHPCBF accept.
37 *
38 * This array is not meant to be accessed directly. Instead, use the settings
39 * as if they are class member vars so the __get() and __set() magic methods
40 * can be used to validate the values. For example, to set the verbosity level to
41 * level 2, use $this->verbosity = 2; instead of accessing this property directly.
42 *
43 * The list of settings are:
44 *
45 * string[] files The files and directories to check.
46 * string[] standards The standards being used for checking.
47 * int verbosity How verbose the output should be.
48 * 0: no unnecessary output
49 * 1: basic output for files being checked
50 * 2: ruleset and file parsing output
51 * 3: sniff execution output
52 * bool interactive Enable interactive checking mode.
53 * bool parallel Check files in parallel.
54 * bool cache Enable the use of the file cache.
55 * bool cacheFile A file where the cache data should be written
56 * bool colors Display colours in output.
57 * bool explain Explain the coding standards.
58 * bool local Process local files in directories only (no recursion).
59 * bool showSources Show sniff source codes in report output.
60 * bool showProgress Show basic progress information while running.
61 * bool quiet Quiet mode; disables progress and verbose output.
62 * bool annotations Process phpcs: annotations.
63 * int tabWidth How many spaces each tab is worth.
64 * string encoding The encoding of the files being checked.
65 * string[] sniffs The sniffs that should be used for checking.
66 * If empty, all sniffs in the supplied standards will be used.
67 * string[] exclude The sniffs that should be excluded from checking.
68 * If empty, all sniffs in the supplied standards will be used.
69 * string[] ignored Regular expressions used to ignore files and folders during checking.
70 * string reportFile A file where the report output should be written.
71 * string generator The documentation generator to use.
72 * string filter The filter to use for the run.
73 * string[] bootstrap One of more files to include before the run begins.
74 * int reportWidth The maximum number of columns that reports should use for output.
75 * Set to "auto" for have this value changed to the width of the terminal.
76 * int errorSeverity The minimum severity an error must have to be displayed.
77 * int warningSeverity The minimum severity a warning must have to be displayed.
78 * bool recordErrors Record the content of error messages as well as error counts.
79 * string suffix A suffix to add to fixed files.
80 * string basepath A file system location to strip from the paths of files shown in reports.
81 * bool stdin Read content from STDIN instead of supplied files.
82 * string stdinContent Content passed directly to PHPCS on STDIN.
83 * string stdinPath The path to use for content passed on STDIN.
84 *
85 * array<string, string> extensions File extensions that should be checked, and what tokenizer to use.
86 * E.g., array('inc' => 'PHP');
87 * array<string, string|null> reports The reports to use for printing output after the run.
88 * The format of the array is:
89 * array(
90 * 'reportName1' => 'outputFile',
91 * 'reportName2' => null,
92 * );
93 * If the array value is NULL, the report will be written to the screen.
94 *
95 * string[] unknown Any arguments gathered on the command line that are unknown to us.
96 * E.g., using `phpcs -c` will give array('c');
97 *
98 * @var array<string, mixed>
99 */
100 private $settings = [
101 'files' => null,
102 'standards' => null,
103 'verbosity' => null,
104 'interactive' => null,
105 'parallel' => null,
106 'cache' => null,
107 'cacheFile' => null,
108 'colors' => null,
109 'explain' => null,
110 'local' => null,
111 'showSources' => null,
112 'showProgress' => null,
113 'quiet' => null,
114 'annotations' => null,
115 'tabWidth' => null,
116 'encoding' => null,
117 'extensions' => null,
118 'sniffs' => null,
119 'exclude' => null,
120 'ignored' => null,
121 'reportFile' => null,
122 'generator' => null,
123 'filter' => null,
124 'bootstrap' => null,
125 'reports' => null,
126 'basepath' => null,
127 'reportWidth' => null,
128 'errorSeverity' => null,
129 'warningSeverity' => null,
130 'recordErrors' => null,
131 'suffix' => null,
132 'stdin' => null,
133 'stdinContent' => null,
134 'stdinPath' => null,
135 'unknown' => null,
136 ];
137
138 /**
139 * Whether or not to kill the process when an unknown command line arg is found.
140 *
141 * If FALSE, arguments that are not command line options or file/directory paths
142 * will be ignored and execution will continue. These values will be stored in
143 * $this->unknown.
144 *
145 * @var boolean
146 */
147 public $dieOnUnknownArg;
148
149 /**
150 * The current command line arguments we are processing.
151 *
152 * @var string[]
153 */
154 private $cliArgs = [];
155
156 /**
157 * Command line values that the user has supplied directly.
158 *
159 * @var array<string, TRUE>
160 */
161 private static $overriddenDefaults = [];
162
163 /**
164 * Config file data that has been loaded for the run.
165 *
166 * @var array<string, string>
167 */
168 private static $configData = null;
169
170 /**
171 * The full path to the config data file that has been loaded.
172 *
173 * @var string
174 */
175 private static $configDataFile = null;
176
177 /**
178 * Automatically discovered executable utility paths.
179 *
180 * @var array<string, string>
181 */
182 private static $executablePaths = [];
183
184
185 /**
186 * Get the value of an inaccessible property.
187 *
188 * @param string $name The name of the property.
189 *
190 * @return mixed
191 * @throws RuntimeException If the setting name is invalid.
192 */
193 public function __get($name)
194 {
195 if (array_key_exists($name, $this->settings) === false) {
196 throw new RuntimeException("ERROR: unable to get value of property \"$name\"");
197 }
198
199 return $this->settings[$name];
200
201 }//end __get()
202
203
204 /**
205 * Set the value of an inaccessible property.
206 *
207 * @param string $name The name of the property.
208 * @param mixed $value The value of the property.
209 *
210 * @return void
211 * @throws RuntimeException If the setting name is invalid.
212 */
213 public function __set($name, $value)
214 {
215 if (array_key_exists($name, $this->settings) === false) {
216 throw new RuntimeException("Can't __set() $name; setting doesn't exist");
217 }
218
219 switch ($name) {
220 case 'reportWidth' :
221 // Support auto terminal width.
222 if ($value === 'auto' && preg_match('|\d+ (\d+)|', shell_exec('stty size 2>&1'), $matches) === 1) {
223 $value = (int) $matches[1];
224 } else {
225 $value = (int) $value;
226 }
227 break;
228 case 'standards' :
229 $cleaned = [];
230
231 // Check if the standard name is valid, or if the case is invalid.
232 $installedStandards = Util\Standards::getInstalledStandards();
233 foreach ($value as $standard) {
234 foreach ($installedStandards as $validStandard) {
235 if (strtolower($standard) === strtolower($validStandard)) {
236 $standard = $validStandard;
237 break;
238 }
239 }
240
241 $cleaned[] = $standard;
242 }
243
244 $value = $cleaned;
245 break;
246 default :
247 // No validation required.
248 break;
249 }//end switch
250
251 $this->settings[$name] = $value;
252
253 }//end __set()
254
255
256 /**
257 * Check if the value of an inaccessible property is set.
258 *
259 * @param string $name The name of the property.
260 *
261 * @return bool
262 */
263 public function __isset($name)
264 {
265 return isset($this->settings[$name]);
266
267 }//end __isset()
268
269
270 /**
271 * Unset the value of an inaccessible property.
272 *
273 * @param string $name The name of the property.
274 *
275 * @return void
276 */
277 public function __unset($name)
278 {
279 $this->settings[$name] = null;
280
281 }//end __unset()
282
283
284 /**
285 * Get the array of all config settings.
286 *
287 * @return array<string, mixed>
288 */
289 public function getSettings()
290 {
291 return $this->settings;
292
293 }//end getSettings()
294
295
296 /**
297 * Set the array of all config settings.
298 *
299 * @param array<string, mixed> $settings The array of config settings.
300 *
301 * @return void
302 */
303 public function setSettings($settings)
304 {
305 return $this->settings = $settings;
306
307 }//end setSettings()
308
309
310 /**
311 * Creates a Config object and populates it with command line values.
312 *
313 * @param array $cliArgs An array of values gathered from CLI args.
314 * @param bool $dieOnUnknownArg Whether or not to kill the process when an
315 * unknown command line arg is found.
316 *
317 * @return void
318 */
319 public function __construct(array $cliArgs=[], $dieOnUnknownArg=true)
320 {
321 if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
322 // Let everything through during testing so that we can
323 // make use of PHPUnit command line arguments as well.
324 $this->dieOnUnknownArg = false;
325 } else {
326 $this->dieOnUnknownArg = $dieOnUnknownArg;
327 }
328
329 if (empty($cliArgs) === true) {
330 $cliArgs = $_SERVER['argv'];
331 array_shift($cliArgs);
332 }
333
334 $this->restoreDefaults();
335 $this->setCommandLineValues($cliArgs);
336
337 if (isset(self::$overriddenDefaults['standards']) === false) {
338 // They did not supply a standard to use.
339 // Look for a default ruleset in the current directory or higher.
340 $currentDir = getcwd();
341
342 $defaultFiles = [
343 '.phpcs.xml',
344 'phpcs.xml',
345 '.phpcs.xml.dist',
346 'phpcs.xml.dist',
347 ];
348
349 do {
350 foreach ($defaultFiles as $defaultFilename) {
351 $default = $currentDir.DIRECTORY_SEPARATOR.$defaultFilename;
352 if (is_file($default) === true) {
353 $this->standards = [$default];
354 break(2);
355 }
356 }
357
358 $lastDir = $currentDir;
359 $currentDir = dirname($currentDir);
360 } while ($currentDir !== '.' && $currentDir !== $lastDir);
361 }//end if
362
363 if (defined('STDIN') === false
364 || strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'
365 ) {
366 return;
367 }
368
369 $handle = fopen('php://stdin', 'r');
370
371 // Check for content on STDIN.
372 if ($this->stdin === true
373 || (Util\Common::isStdinATTY() === false
374 && feof($handle) === false)
375 ) {
376 $readStreams = [$handle];
377 $writeSteams = null;
378
379 $fileContents = '';
380 while (is_resource($handle) === true && feof($handle) === false) {
381 // Set a timeout of 200ms.
382 if (stream_select($readStreams, $writeSteams, $writeSteams, 0, 200000) === 0) {
383 break;
384 }
385
386 $fileContents .= fgets($handle);
387 }
388
389 if (trim($fileContents) !== '') {
390 $this->stdin = true;
391 $this->stdinContent = $fileContents;
392 self::$overriddenDefaults['stdin'] = true;
393 self::$overriddenDefaults['stdinContent'] = true;
394 }
395 }//end if
396
397 fclose($handle);
398
399 }//end __construct()
400
401
402 /**
403 * Set the command line values.
404 *
405 * @param array $args An array of command line arguments to set.
406 *
407 * @return void
408 */
409 public function setCommandLineValues($args)
410 {
411 $this->cliArgs = $args;
412 $numArgs = count($args);
413
414 for ($i = 0; $i < $numArgs; $i++) {
415 $arg = $this->cliArgs[$i];
416 if ($arg === '') {
417 continue;
418 }
419
420 if ($arg{0} === '-') {
421 if ($arg === '-') {
422 // Asking to read from STDIN.
423 $this->stdin = true;
424 self::$overriddenDefaults['stdin'] = true;
425 continue;
426 }
427
428 if ($arg === '--') {
429 // Empty argument, ignore it.
430 continue;
431 }
432
433 if ($arg{1} === '-') {
434 $this->processLongArgument(substr($arg, 2), $i);
435 } else {
436 $switches = str_split($arg);
437 foreach ($switches as $switch) {
438 if ($switch === '-') {
439 continue;
440 }
441
442 $this->processShortArgument($switch, $i);
443 }
444 }
445 } else {
446 $this->processUnknownArgument($arg, $i);
447 }//end if
448 }//end for
449
450 }//end setCommandLineValues()
451
452
453 /**
454 * Restore default values for all possible command line arguments.
455 *
456 * @return array
457 */
458 public function restoreDefaults()
459 {
460 $this->files = [];
461 $this->standards = ['PEAR'];
462 $this->verbosity = 0;
463 $this->interactive = false;
464 $this->cache = false;
465 $this->cacheFile = null;
466 $this->colors = false;
467 $this->explain = false;
468 $this->local = false;
469 $this->showSources = false;
470 $this->showProgress = false;
471 $this->quiet = false;
472 $this->annotations = true;
473 $this->parallel = 1;
474 $this->tabWidth = 0;
475 $this->encoding = 'utf-8';
476 $this->extensions = [
477 'php' => 'PHP',
478 'inc' => 'PHP',
479 'js' => 'JS',
480 'css' => 'CSS',
481 ];
482 $this->sniffs = [];
483 $this->exclude = [];
484 $this->ignored = [];
485 $this->reportFile = null;
486 $this->generator = null;
487 $this->filter = null;
488 $this->bootstrap = [];
489 $this->basepath = null;
490 $this->reports = ['full' => null];
491 $this->reportWidth = 'auto';
492 $this->errorSeverity = 5;
493 $this->warningSeverity = 5;
494 $this->recordErrors = true;
495 $this->suffix = '';
496 $this->stdin = false;
497 $this->stdinContent = null;
498 $this->stdinPath = null;
499 $this->unknown = [];
500
501 $standard = self::getConfigData('default_standard');
502 if ($standard !== null) {
503 $this->standards = explode(',', $standard);
504 }
505
506 $reportFormat = self::getConfigData('report_format');
507 if ($reportFormat !== null) {
508 $this->reports = [$reportFormat => null];
509 }
510
511 $tabWidth = self::getConfigData('tab_width');
512 if ($tabWidth !== null) {
513 $this->tabWidth = (int) $tabWidth;
514 }
515
516 $encoding = self::getConfigData('encoding');
517 if ($encoding !== null) {
518 $this->encoding = strtolower($encoding);
519 }
520
521 $severity = self::getConfigData('severity');
522 if ($severity !== null) {
523 $this->errorSeverity = (int) $severity;
524 $this->warningSeverity = (int) $severity;
525 }
526
527 $severity = self::getConfigData('error_severity');
528 if ($severity !== null) {
529 $this->errorSeverity = (int) $severity;
530 }
531
532 $severity = self::getConfigData('warning_severity');
533 if ($severity !== null) {
534 $this->warningSeverity = (int) $severity;
535 }
536
537 $showWarnings = self::getConfigData('show_warnings');
538 if ($showWarnings !== null) {
539 $showWarnings = (bool) $showWarnings;
540 if ($showWarnings === false) {
541 $this->warningSeverity = 0;
542 }
543 }
544
545 $reportWidth = self::getConfigData('report_width');
546 if ($reportWidth !== null) {
547 $this->reportWidth = $reportWidth;
548 }
549
550 $showProgress = self::getConfigData('show_progress');
551 if ($showProgress !== null) {
552 $this->showProgress = (bool) $showProgress;
553 }
554
555 $quiet = self::getConfigData('quiet');
556 if ($quiet !== null) {
557 $this->quiet = (bool) $quiet;
558 }
559
560 $colors = self::getConfigData('colors');
561 if ($colors !== null) {
562 $this->colors = (bool) $colors;
563 }
564
565 if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
566 $cache = self::getConfigData('cache');
567 if ($cache !== null) {
568 $this->cache = (bool) $cache;
569 }
570
571 $parallel = self::getConfigData('parallel');
572 if ($parallel !== null) {
573 $this->parallel = max((int) $parallel, 1);
574 }
575 }
576
577 }//end restoreDefaults()
578
579
580 /**
581 * Processes a short (-e) command line argument.
582 *
583 * @param string $arg The command line argument.
584 * @param int $pos The position of the argument on the command line.
585 *
586 * @return void
587 */
588 public function processShortArgument($arg, $pos)
589 {
590 switch ($arg) {
591 case 'h':
592 case '?':
593 ob_start();
594 $this->printUsage();
595 $output = ob_get_contents();
596 ob_end_clean();
597 throw new DeepExitException($output, 0);
598 case 'i' :
599 ob_start();
600 Util\Standards::printInstalledStandards();
601 $output = ob_get_contents();
602 ob_end_clean();
603 throw new DeepExitException($output, 0);
604 case 'v' :
605 if ($this->quiet === true) {
606 // Ignore when quiet mode is enabled.
607 break;
608 }
609
610 $this->verbosity++;
611 self::$overriddenDefaults['verbosity'] = true;
612 break;
613 case 'l' :
614 $this->local = true;
615 self::$overriddenDefaults['local'] = true;
616 break;
617 case 's' :
618 $this->showSources = true;
619 self::$overriddenDefaults['showSources'] = true;
620 break;
621 case 'a' :
622 $this->interactive = true;
623 self::$overriddenDefaults['interactive'] = true;
624 break;
625 case 'e':
626 $this->explain = true;
627 self::$overriddenDefaults['explain'] = true;
628 break;
629 case 'p' :
630 if ($this->quiet === true) {
631 // Ignore when quiet mode is enabled.
632 break;
633 }
634
635 $this->showProgress = true;
636 self::$overriddenDefaults['showProgress'] = true;
637 break;
638 case 'q' :
639 // Quiet mode disables a few other settings as well.
640 $this->quiet = true;
641 $this->showProgress = false;
642 $this->verbosity = 0;
643
644 self::$overriddenDefaults['quiet'] = true;
645 break;
646 case 'm' :
647 $this->recordErrors = false;
648 self::$overriddenDefaults['recordErrors'] = true;
649 break;
650 case 'd' :
651 $ini = explode('=', $this->cliArgs[($pos + 1)]);
652 $this->cliArgs[($pos + 1)] = '';
653 if (isset($ini[1]) === true) {
654 ini_set($ini[0], $ini[1]);
655 } else {
656 ini_set($ini[0], true);
657 }
658 break;
659 case 'n' :
660 if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
661 $this->warningSeverity = 0;
662 self::$overriddenDefaults['warningSeverity'] = true;
663 }
664 break;
665 case 'w' :
666 if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
667 $this->warningSeverity = $this->errorSeverity;
668 self::$overriddenDefaults['warningSeverity'] = true;
669 }
670 break;
671 default:
672 if ($this->dieOnUnknownArg === false) {
673 $unknown = $this->unknown;
674 $unknown[] = $arg;
675 $this->unknown = $unknown;
676 } else {
677 $this->processUnknownArgument('-'.$arg, $pos);
678 }
679 }//end switch
680
681 }//end processShortArgument()
682
683
684 /**
685 * Processes a long (--example) command line argument.
686 *
687 * @param string $arg The command line argument.
688 * @param int $pos The position of the argument on the command line.
689 *
690 * @return void
691 */
692 public function processLongArgument($arg, $pos)
693 {
694 switch ($arg) {
695 case 'help':
696 ob_start();
697 $this->printUsage();
698 $output = ob_get_contents();
699 ob_end_clean();
700 throw new DeepExitException($output, 0);
701 case 'version':
702 $output = 'PHP_CodeSniffer version '.self::VERSION.' ('.self::STABILITY.') ';
703 $output .= 'by Squiz (http://www.squiz.net)'.PHP_EOL;
704 throw new DeepExitException($output, 0);
705 case 'colors':
706 if (isset(self::$overriddenDefaults['colors']) === true) {
707 break;
708 }
709
710 $this->colors = true;
711 self::$overriddenDefaults['colors'] = true;
712 break;
713 case 'no-colors':
714 if (isset(self::$overriddenDefaults['colors']) === true) {
715 break;
716 }
717
718 $this->colors = false;
719 self::$overriddenDefaults['colors'] = true;
720 break;
721 case 'cache':
722 if (isset(self::$overriddenDefaults['cache']) === true) {
723 break;
724 }
725
726 if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
727 $this->cache = true;
728 self::$overriddenDefaults['cache'] = true;
729 }
730 break;
731 case 'no-cache':
732 if (isset(self::$overriddenDefaults['cache']) === true) {
733 break;
734 }
735
736 $this->cache = false;
737 self::$overriddenDefaults['cache'] = true;
738 break;
739 case 'ignore-annotations':
740 if (isset(self::$overriddenDefaults['annotations']) === true) {
741 break;
742 }
743
744 $this->annotations = false;
745 self::$overriddenDefaults['annotations'] = true;
746 break;
747 case 'config-set':
748 if (isset($this->cliArgs[($pos + 1)]) === false
749 || isset($this->cliArgs[($pos + 2)]) === false
750 ) {
751 $error = 'ERROR: Setting a config option requires a name and value'.PHP_EOL.PHP_EOL;
752 $error .= $this->printShortUsage(true);
753 throw new DeepExitException($error, 3);
754 }
755
756 $key = $this->cliArgs[($pos + 1)];
757 $value = $this->cliArgs[($pos + 2)];
758 $current = self::getConfigData($key);
759
760 try {
761 $this->setConfigData($key, $value);
762 } catch (\Exception $e) {
763 throw new DeepExitException($e->getMessage().PHP_EOL, 3);
764 }
765
766 $output = 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
767
768 if ($current === null) {
769 $output .= "Config value \"$key\" added successfully".PHP_EOL;
770 } else {
771 $output .= "Config value \"$key\" updated successfully; old value was \"$current\"".PHP_EOL;
772 }
773 throw new DeepExitException($output, 0);
774 case 'config-delete':
775 if (isset($this->cliArgs[($pos + 1)]) === false) {
776 $error = 'ERROR: Deleting a config option requires the name of the option'.PHP_EOL.PHP_EOL;
777 $error .= $this->printShortUsage(true);
778 throw new DeepExitException($error, 3);
779 }
780
781 $output = 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
782
783 $key = $this->cliArgs[($pos + 1)];
784 $current = self::getConfigData($key);
785 if ($current === null) {
786 $output .= "Config value \"$key\" has not been set".PHP_EOL;
787 } else {
788 try {
789 $this->setConfigData($key, null);
790 } catch (\Exception $e) {
791 throw new DeepExitException($e->getMessage().PHP_EOL, 3);
792 }
793
794 $output .= "Config value \"$key\" removed successfully; old value was \"$current\"".PHP_EOL;
795 }
796 throw new DeepExitException($output, 0);
797 case 'config-show':
798 ob_start();
799 $data = self::getAllConfigData();
800 echo 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
801 $this->printConfigData($data);
802 $output = ob_get_contents();
803 ob_end_clean();
804 throw new DeepExitException($output, 0);
805 case 'runtime-set':
806 if (isset($this->cliArgs[($pos + 1)]) === false
807 || isset($this->cliArgs[($pos + 2)]) === false
808 ) {
809 $error = 'ERROR: Setting a runtime config option requires a name and value'.PHP_EOL.PHP_EOL;
810 $error .= $this->printShortUsage(true);
811 throw new DeepExitException($error, 3);
812 }
813
814 $key = $this->cliArgs[($pos + 1)];
815 $value = $this->cliArgs[($pos + 2)];
816 $this->cliArgs[($pos + 1)] = '';
817 $this->cliArgs[($pos + 2)] = '';
818 self::setConfigData($key, $value, true);
819 if (isset(self::$overriddenDefaults['runtime-set']) === false) {
820 self::$overriddenDefaults['runtime-set'] = [];
821 }
822
823 self::$overriddenDefaults['runtime-set'][$key] = true;
824 break;
825 default:
826 if (substr($arg, 0, 7) === 'sniffs=') {
827 if (isset(self::$overriddenDefaults['sniffs']) === true) {
828 break;
829 }
830
831 $sniffs = explode(',', substr($arg, 7));
832 foreach ($sniffs as $sniff) {
833 if (substr_count($sniff, '.') !== 2) {
834 $error = 'ERROR: The specified sniff code "'.$sniff.'" is invalid'.PHP_EOL.PHP_EOL;
835 $error .= $this->printShortUsage(true);
836 throw new DeepExitException($error, 3);
837 }
838 }
839
840 $this->sniffs = $sniffs;
841 self::$overriddenDefaults['sniffs'] = true;
842 } else if (substr($arg, 0, 8) === 'exclude=') {
843 if (isset(self::$overriddenDefaults['exclude']) === true) {
844 break;
845 }
846
847 $sniffs = explode(',', substr($arg, 8));
848 foreach ($sniffs as $sniff) {
849 if (substr_count($sniff, '.') !== 2) {
850 $error = 'ERROR: The specified sniff code "'.$sniff.'" is invalid'.PHP_EOL.PHP_EOL;
851 $error .= $this->printShortUsage(true);
852 throw new DeepExitException($error, 3);
853 }
854 }
855
856 $this->exclude = $sniffs;
857 self::$overriddenDefaults['exclude'] = true;
858 } else if (defined('PHP_CODESNIFFER_IN_TESTS') === false
859 && substr($arg, 0, 6) === 'cache='
860 ) {
861 if ((isset(self::$overriddenDefaults['cache']) === true
862 && $this->cache === false)
863 || isset(self::$overriddenDefaults['cacheFile']) === true
864 ) {
865 break;
866 }
867
868 // Turn caching on.
869 $this->cache = true;
870 self::$overriddenDefaults['cache'] = true;
871
872 $this->cacheFile = Util\Common::realpath(substr($arg, 6));
873
874 // It may not exist and return false instead.
875 if ($this->cacheFile === false) {
876 $this->cacheFile = substr($arg, 6);
877
878 $dir = dirname($this->cacheFile);
879 if (is_dir($dir) === false) {
880 $error = 'ERROR: The specified cache file path "'.$this->cacheFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
881 $error .= $this->printShortUsage(true);
882 throw new DeepExitException($error, 3);
883 }
884
885 if ($dir === '.') {
886 // Passed cache file is a file in the current directory.
887 $this->cacheFile = getcwd().'/'.basename($this->cacheFile);
888 } else {
889 if ($dir{0} === '/') {
890 // An absolute path.
891 $dir = Util\Common::realpath($dir);
892 } else {
893 $dir = Util\Common::realpath(getcwd().'/'.$dir);
894 }
895
896 if ($dir !== false) {
897 // Cache file path is relative.
898 $this->cacheFile = $dir.'/'.basename($this->cacheFile);
899 }
900 }
901 }//end if
902
903 self::$overriddenDefaults['cacheFile'] = true;
904
905 if (is_dir($this->cacheFile) === true) {
906 $error = 'ERROR: The specified cache file path "'.$this->cacheFile.'" is a directory'.PHP_EOL.PHP_EOL;
907 $error .= $this->printShortUsage(true);
908 throw new DeepExitException($error, 3);
909 }
910 } else if (substr($arg, 0, 10) === 'bootstrap=') {
911 $files = explode(',', substr($arg, 10));
912 $bootstrap = [];
913 foreach ($files as $file) {
914 $path = Util\Common::realpath($file);
915 if ($path === false) {
916 $error = 'ERROR: The specified bootstrap file "'.$file.'" does not exist'.PHP_EOL.PHP_EOL;
917 $error .= $this->printShortUsage(true);
918 throw new DeepExitException($error, 3);
919 }
920
921 $bootstrap[] = $path;
922 }
923
924 $this->bootstrap = array_merge($this->bootstrap, $bootstrap);
925 self::$overriddenDefaults['bootstrap'] = true;
926 } else if (substr($arg, 0, 10) === 'file-list=') {
927 $fileList = substr($arg, 10);
928 $path = Util\Common::realpath($fileList);
929 if ($path === false) {
930 $error = 'ERROR: The specified file list "'.$fileList.'" does not exist'.PHP_EOL.PHP_EOL;
931 $error .= $this->printShortUsage(true);
932 throw new DeepExitException($error, 3);
933 }
934
935 $files = file($path);
936 foreach ($files as $inputFile) {
937 $inputFile = trim($inputFile);
938
939 // Skip empty lines.
940 if ($inputFile === '') {
941 continue;
942 }
943
944 $this->processFilePath($inputFile);
945 }
946 } else if (substr($arg, 0, 11) === 'stdin-path=') {
947 if (isset(self::$overriddenDefaults['stdinPath']) === true) {
948 break;
949 }
950
951 $this->stdinPath = Util\Common::realpath(substr($arg, 11));
952
953 // It may not exist and return false instead, so use whatever they gave us.
954 if ($this->stdinPath === false) {
955 $this->stdinPath = trim(substr($arg, 11));
956 }
957
958 self::$overriddenDefaults['stdinPath'] = true;
959 } else if (PHP_CODESNIFFER_CBF === false && substr($arg, 0, 12) === 'report-file=') {
960 if (isset(self::$overriddenDefaults['reportFile']) === true) {
961 break;
962 }
963
964 $this->reportFile = Util\Common::realpath(substr($arg, 12));
965
966 // It may not exist and return false instead.
967 if ($this->reportFile === false) {
968 $this->reportFile = substr($arg, 12);
969
970 $dir = dirname($this->reportFile);
971 if (is_dir($dir) === false) {
972 $error = 'ERROR: The specified report file path "'.$this->reportFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
973 $error .= $this->printShortUsage(true);
974 throw new DeepExitException($error, 3);
975 }
976
977 if ($dir === '.') {
978 // Passed report file is a file in the current directory.
979 $this->reportFile = getcwd().'/'.basename($this->reportFile);
980 } else {
981 if ($dir{0} === '/') {
982 // An absolute path.
983 $dir = Util\Common::realpath($dir);
984 } else {
985 $dir = Util\Common::realpath(getcwd().'/'.$dir);
986 }
987
988 if ($dir !== false) {
989 // Report file path is relative.
990 $this->reportFile = $dir.'/'.basename($this->reportFile);
991 }
992 }
993 }//end if
994
995 self::$overriddenDefaults['reportFile'] = true;
996
997 if (is_dir($this->reportFile) === true) {
998 $error = 'ERROR: The specified report file path "'.$this->reportFile.'" is a directory'.PHP_EOL.PHP_EOL;
999 $error .= $this->printShortUsage(true);
1000 throw new DeepExitException($error, 3);
1001 }
1002 } else if (substr($arg, 0, 13) === 'report-width=') {
1003 if (isset(self::$overriddenDefaults['reportWidth']) === true) {
1004 break;
1005 }
1006
1007 $this->reportWidth = substr($arg, 13);
1008 self::$overriddenDefaults['reportWidth'] = true;
1009 } else if (substr($arg, 0, 9) === 'basepath=') {
1010 if (isset(self::$overriddenDefaults['basepath']) === true) {
1011 break;
1012 }
1013
1014 self::$overriddenDefaults['basepath'] = true;
1015
1016 if (substr($arg, 9) === '') {
1017 $this->basepath = null;
1018 break;
1019 }
1020
1021 $this->basepath = Util\Common::realpath(substr($arg, 9));
1022
1023 // It may not exist and return false instead.
1024 if ($this->basepath === false) {
1025 $this->basepath = substr($arg, 9);
1026 }
1027
1028 if (is_dir($this->basepath) === false) {
1029 $error = 'ERROR: The specified basepath "'.$this->basepath.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
1030 $error .= $this->printShortUsage(true);
1031 throw new DeepExitException($error, 3);
1032 }
1033 } else if ((substr($arg, 0, 7) === 'report=' || substr($arg, 0, 7) === 'report-')) {
1034 $reports = [];
1035
1036 if ($arg[6] === '-') {
1037 // This is a report with file output.
1038 $split = strpos($arg, '=');
1039 if ($split === false) {
1040 $report = substr($arg, 7);
1041 $output = null;
1042 } else {
1043 $report = substr($arg, 7, ($split - 7));
1044 $output = substr($arg, ($split + 1));
1045 if ($output === false) {
1046 $output = null;
1047 } else {
1048 $dir = dirname($output);
1049 if (is_dir($dir) === false) {
1050 $error = 'ERROR: The specified '.$report.' report file path "'.$output.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
1051 $error .= $this->printShortUsage(true);
1052 throw new DeepExitException($error, 3);
1053 }
1054
1055 if ($dir === '.') {
1056 // Passed report file is a filename in the current directory.
1057 $output = getcwd().'/'.basename($output);
1058 } else {
1059 if ($dir{0} === '/') {
1060 // An absolute path.
1061 $dir = Util\Common::realpath($dir);
1062 } else {
1063 $dir = Util\Common::realpath(getcwd().'/'.$dir);
1064 }
1065
1066 if ($dir !== false) {
1067 // Report file path is relative.
1068 $output = $dir.'/'.basename($output);
1069 }
1070 }
1071 }//end if
1072 }//end if
1073
1074 $reports[$report] = $output;
1075 } else {
1076 // This is a single report.
1077 if (isset(self::$overriddenDefaults['reports']) === true) {
1078 break;
1079 }
1080
1081 $reportNames = explode(',', substr($arg, 7));
1082 foreach ($reportNames as $report) {
1083 $reports[$report] = null;
1084 }
1085 }//end if
1086
1087 // Remove the default value so the CLI value overrides it.
1088 if (isset(self::$overriddenDefaults['reports']) === false) {
1089 $this->reports = $reports;
1090 } else {
1091 $this->reports = array_merge($this->reports, $reports);
1092 }
1093
1094 self::$overriddenDefaults['reports'] = true;
1095 } else if (substr($arg, 0, 7) === 'filter=') {
1096 if (isset(self::$overriddenDefaults['filter']) === true) {
1097 break;
1098 }
1099
1100 $this->filter = substr($arg, 7);
1101 self::$overriddenDefaults['filter'] = true;
1102 } else if (substr($arg, 0, 9) === 'standard=') {
1103 $standards = trim(substr($arg, 9));
1104 if ($standards !== '') {
1105 $this->standards = explode(',', $standards);
1106 }
1107
1108 self::$overriddenDefaults['standards'] = true;
1109 } else if (substr($arg, 0, 11) === 'extensions=') {
1110 if (isset(self::$overriddenDefaults['extensions']) === true) {
1111 break;
1112 }
1113
1114 $extensions = explode(',', substr($arg, 11));
1115 $newExtensions = [];
1116 foreach ($extensions as $ext) {
1117 $slash = strpos($ext, '/');
1118 if ($slash !== false) {
1119 // They specified the tokenizer too.
1120 list($ext, $tokenizer) = explode('/', $ext);
1121 $newExtensions[$ext] = strtoupper($tokenizer);
1122 continue;
1123 }
1124
1125 if (isset($this->extensions[$ext]) === true) {
1126 $newExtensions[$ext] = $this->extensions[$ext];
1127 } else {
1128 $newExtensions[$ext] = 'PHP';
1129 }
1130 }
1131
1132 $this->extensions = $newExtensions;
1133 self::$overriddenDefaults['extensions'] = true;
1134 } else if (substr($arg, 0, 7) === 'suffix=') {
1135 if (isset(self::$overriddenDefaults['suffix']) === true) {
1136 break;
1137 }
1138
1139 $this->suffix = substr($arg, 7);
1140 self::$overriddenDefaults['suffix'] = true;
1141 } else if (substr($arg, 0, 9) === 'parallel=') {
1142 if (isset(self::$overriddenDefaults['parallel']) === true) {
1143 break;
1144 }
1145
1146 $this->parallel = max((int) substr($arg, 9), 1);
1147 self::$overriddenDefaults['parallel'] = true;
1148 } else if (substr($arg, 0, 9) === 'severity=') {
1149 $this->errorSeverity = (int) substr($arg, 9);
1150 $this->warningSeverity = $this->errorSeverity;
1151 if (isset(self::$overriddenDefaults['errorSeverity']) === false) {
1152 self::$overriddenDefaults['errorSeverity'] = true;
1153 }
1154
1155 if (isset(self::$overriddenDefaults['warningSeverity']) === false) {
1156 self::$overriddenDefaults['warningSeverity'] = true;
1157 }
1158 } else if (substr($arg, 0, 15) === 'error-severity=') {
1159 if (isset(self::$overriddenDefaults['errorSeverity']) === true) {
1160 break;
1161 }
1162
1163 $this->errorSeverity = (int) substr($arg, 15);
1164 self::$overriddenDefaults['errorSeverity'] = true;
1165 } else if (substr($arg, 0, 17) === 'warning-severity=') {
1166 if (isset(self::$overriddenDefaults['warningSeverity']) === true) {
1167 break;
1168 }
1169
1170 $this->warningSeverity = (int) substr($arg, 17);
1171 self::$overriddenDefaults['warningSeverity'] = true;
1172 } else if (substr($arg, 0, 7) === 'ignore=') {
1173 if (isset(self::$overriddenDefaults['ignored']) === true) {
1174 break;
1175 }
1176
1177 // Split the ignore string on commas, unless the comma is escaped
1178 // using 1 or 3 slashes (\, or \\\,).
1179 $patterns = preg_split(
1180 '/(?<=(?<!\\\\)\\\\\\\\),|(?<!\\\\),/',
1181 substr($arg, 7)
1182 );
1183
1184 $ignored = [];
1185 foreach ($patterns as $pattern) {
1186 $pattern = trim($pattern);
1187 if ($pattern === '') {
1188 continue;
1189 }
1190
1191 $ignored[$pattern] = 'absolute';
1192 }
1193
1194 $this->ignored = $ignored;
1195 self::$overriddenDefaults['ignored'] = true;
1196 } else if (substr($arg, 0, 10) === 'generator='
1197 && PHP_CODESNIFFER_CBF === false
1198 ) {
1199 if (isset(self::$overriddenDefaults['generator']) === true) {
1200 break;
1201 }
1202
1203 $this->generator = substr($arg, 10);
1204 self::$overriddenDefaults['generator'] = true;
1205 } else if (substr($arg, 0, 9) === 'encoding=') {
1206 if (isset(self::$overriddenDefaults['encoding']) === true) {
1207 break;
1208 }
1209
1210 $this->encoding = strtolower(substr($arg, 9));
1211 self::$overriddenDefaults['encoding'] = true;
1212 } else if (substr($arg, 0, 10) === 'tab-width=') {
1213 if (isset(self::$overriddenDefaults['tabWidth']) === true) {
1214 break;
1215 }
1216
1217 $this->tabWidth = (int) substr($arg, 10);
1218 self::$overriddenDefaults['tabWidth'] = true;
1219 } else {
1220 if ($this->dieOnUnknownArg === false) {
1221 $eqPos = strpos($arg, '=');
1222 try {
1223 if ($eqPos === false) {
1224 $this->values[$arg] = $arg;
1225 } else {
1226 $value = substr($arg, ($eqPos + 1));
1227 $arg = substr($arg, 0, $eqPos);
1228 $this->values[$arg] = $value;
1229 }
1230 } catch (RuntimeException $e) {
1231 // Value is not valid, so just ignore it.
1232 }
1233 } else {
1234 $this->processUnknownArgument('--'.$arg, $pos);
1235 }
1236 }//end if
1237 break;
1238 }//end switch
1239
1240 }//end processLongArgument()
1241
1242
1243 /**
1244 * Processes an unknown command line argument.
1245 *
1246 * Assumes all unknown arguments are files and folders to check.
1247 *
1248 * @param string $arg The command line argument.
1249 * @param int $pos The position of the argument on the command line.
1250 *
1251 * @return void
1252 */
1253 public function processUnknownArgument($arg, $pos)
1254 {
1255 // We don't know about any additional switches; just files.
1256 if ($arg{0} === '-') {
1257 if ($this->dieOnUnknownArg === false) {
1258 return;
1259 }
1260
1261 $error = "ERROR: option \"$arg\" not known".PHP_EOL.PHP_EOL;
1262 $error .= $this->printShortUsage(true);
1263 throw new DeepExitException($error, 3);
1264 }
1265
1266 $this->processFilePath($arg);
1267
1268 }//end processUnknownArgument()
1269
1270
1271 /**
1272 * Processes a file path and add it to the file list.
1273 *
1274 * @param string $path The path to the file to add.
1275 *
1276 * @return void
1277 */
1278 public function processFilePath($path)
1279 {
1280 // If we are processing STDIN, don't record any files to check.
1281 if ($this->stdin === true) {
1282 return;
1283 }
1284
1285 $file = Util\Common::realpath($path);
1286 if (file_exists($file) === false) {
1287 if ($this->dieOnUnknownArg === false) {
1288 return;
1289 }
1290
1291 $error = 'ERROR: The file "'.$path.'" does not exist.'.PHP_EOL.PHP_EOL;
1292 $error .= $this->printShortUsage(true);
1293 throw new DeepExitException($error, 3);
1294 } else {
1295 // Can't modify the files array directly because it's not a real
1296 // class member, so need to use this little get/modify/set trick.
1297 $files = $this->files;
1298 $files[] = $file;
1299 $this->files = $files;
1300 self::$overriddenDefaults['files'] = true;
1301 }
1302
1303 }//end processFilePath()
1304
1305
1306 /**
1307 * Prints out the usage information for this script.
1308 *
1309 * @return void
1310 */
1311 public function printUsage()
1312 {
1313 echo PHP_EOL;
1314
1315 if (PHP_CODESNIFFER_CBF === true) {
1316 $this->printPHPCBFUsage();
1317 } else {
1318 $this->printPHPCSUsage();
1319 }
1320
1321 echo PHP_EOL;
1322
1323 }//end printUsage()
1324
1325
1326 /**
1327 * Prints out the short usage information for this script.
1328 *
1329 * @param bool $return If TRUE, the usage string is returned
1330 * instead of output to screen.
1331 *
1332 * @return string|void
1333 */
1334 public function printShortUsage($return=false)
1335 {
1336 if (PHP_CODESNIFFER_CBF === true) {
1337 $usage = 'Run "phpcbf --help" for usage information';
1338 } else {
1339 $usage = 'Run "phpcs --help" for usage information';
1340 }
1341
1342 $usage .= PHP_EOL.PHP_EOL;
1343
1344 if ($return === true) {
1345 return $usage;
1346 }
1347
1348 echo $usage;
1349
1350 }//end printShortUsage()
1351
1352
1353 /**
1354 * Prints out the usage information for PHPCS.
1355 *
1356 * @return void
1357 */
1358 public function printPHPCSUsage()
1359 {
1360 echo 'Usage: phpcs [-nwlsaepqvi] [-d key[=value]] [--colors] [--no-colors]'.PHP_EOL;
1361 echo ' [--cache[=<cacheFile>]] [--no-cache] [--tab-width=<tabWidth>]'.PHP_EOL;
1362 echo ' [--report=<report>] [--report-file=<reportFile>] [--report-<report>=<reportFile>]'.PHP_EOL;
1363 echo ' [--report-width=<reportWidth>] [--basepath=<basepath>] [--bootstrap=<bootstrap>]'.PHP_EOL;
1364 echo ' [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
1365 echo ' [--runtime-set key value] [--config-set key value] [--config-delete key] [--config-show]'.PHP_EOL;
1366 echo ' [--standard=<standard>] [--sniffs=<sniffs>] [--exclude=<sniffs>]'.PHP_EOL;
1367 echo ' [--encoding=<encoding>] [--parallel=<processes>] [--generator=<generator>]'.PHP_EOL;
1368 echo ' [--extensions=<extensions>] [--ignore=<patterns>] [--ignore-annotations]'.PHP_EOL;
1369 echo ' [--stdin-path=<stdinPath>] [--file-list=<fileList>] <file> - ...'.PHP_EOL;
1370 echo PHP_EOL;
1371 echo ' - Check STDIN instead of local files and directories'.PHP_EOL;
1372 echo ' -n Do not print warnings (shortcut for --warning-severity=0)'.PHP_EOL;
1373 echo ' -w Print both warnings and errors (this is the default)'.PHP_EOL;
1374 echo ' -l Local directory only, no recursion'.PHP_EOL;
1375 echo ' -s Show sniff codes in all reports'.PHP_EOL;
1376 echo ' -a Run interactively'.PHP_EOL;
1377 echo ' -e Explain a standard by showing the sniffs it includes'.PHP_EOL;
1378 echo ' -p Show progress of the run'.PHP_EOL;
1379 echo ' -q Quiet mode; disables progress and verbose output'.PHP_EOL;
1380 echo ' -m Stop error messages from being recorded'.PHP_EOL;
1381 echo ' (saves a lot of memory, but stops many reports from being used)'.PHP_EOL;
1382 echo ' -v Print processed files'.PHP_EOL;
1383 echo ' -vv Print ruleset and token output'.PHP_EOL;
1384 echo ' -vvv Print sniff processing information'.PHP_EOL;
1385 echo ' -i Show a list of installed coding standards'.PHP_EOL;
1386 echo ' -d Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
1387 echo PHP_EOL;
1388 echo ' --help Print this help message'.PHP_EOL;
1389 echo ' --version Print version information'.PHP_EOL;
1390 echo ' --colors Use colors in output'.PHP_EOL;
1391 echo ' --no-colors Do not use colors in output (this is the default)'.PHP_EOL;
1392 echo ' --cache Cache results between runs'.PHP_EOL;
1393 echo ' --no-cache Do not cache results between runs (this is the default)'.PHP_EOL;
1394 echo ' --ignore-annotations Ignore all phpcs: annotations in code comments'.PHP_EOL;
1395 echo PHP_EOL;
1396 echo ' <cacheFile> Use a specific file for caching (uses a temporary file by default)'.PHP_EOL;
1397 echo ' <basepath> A path to strip from the front of file paths inside reports'.PHP_EOL;
1398 echo ' <bootstrap> A comma separated list of files to run before processing begins'.PHP_EOL;
1399 echo ' <file> One or more files and/or directories to check'.PHP_EOL;
1400 echo ' <fileList> A file containing a list of files and/or directories to check (one per line)'.PHP_EOL;
1401 echo ' <encoding> The encoding of the files being checked (default is utf-8)'.PHP_EOL;
1402 echo ' <extensions> A comma separated list of file extensions to check'.PHP_EOL;
1403 echo ' The type of the file can be specified using: ext/type'.PHP_EOL;
1404 echo ' e.g., module/php,es/js'.PHP_EOL;
1405 echo ' <generator> Uses either the "HTML", "Markdown" or "Text" generator'.PHP_EOL;
1406 echo ' (forces documentation generation instead of checking)'.PHP_EOL;
1407 echo ' <patterns> A comma separated list of patterns to ignore files and directories'.PHP_EOL;
1408 echo ' <processes> How many files should be checked simultaneously (default is 1)'.PHP_EOL;
1409 echo ' <report> Print either the "full", "xml", "checkstyle", "csv"'.PHP_EOL;
1410 echo ' "json", "junit", "emacs", "source", "summary", "diff"'.PHP_EOL;
1411 echo ' "svnblame", "gitblame", "hgblame" or "notifysend" report'.PHP_EOL;
1412 echo ' (the "full" report is printed by default)'.PHP_EOL;
1413 echo ' <reportFile> Write the report to the specified file path'.PHP_EOL;
1414 echo ' <reportWidth> How many columns wide screen reports should be printed'.PHP_EOL;
1415 echo ' or set to "auto" to use current screen width, where supported'.PHP_EOL;
1416 echo ' <severity> The minimum severity required to display an error or warning'.PHP_EOL;
1417 echo ' <sniffs> A comma separated list of sniff codes to include or exclude from checking'.PHP_EOL;
1418 echo ' (all sniffs must be part of the specified standard)'.PHP_EOL;
1419 echo ' <standard> The name or path of the coding standard to use'.PHP_EOL;
1420 echo ' <stdinPath> If processing STDIN, the file path that STDIN will be processed as'.PHP_EOL;
1421 echo ' <tabWidth> The number of spaces each tab represents'.PHP_EOL;
1422
1423 }//end printPHPCSUsage()
1424
1425
1426 /**
1427 * Prints out the usage information for PHPCBF.
1428 *
1429 * @return void
1430 */
1431 public function printPHPCBFUsage()
1432 {
1433 echo 'Usage: phpcbf [-nwli] [-d key[=value]] [--ignore-annotations] [--bootstrap=<bootstrap>]'.PHP_EOL;
1434 echo ' [--standard=<standard>] [--sniffs=<sniffs>] [--exclude=<sniffs>] [--suffix=<suffix>]'.PHP_EOL;
1435 echo ' [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
1436 echo ' [--tab-width=<tabWidth>] [--encoding=<encoding>] [--parallel=<processes>]'.PHP_EOL;
1437 echo ' [--basepath=<basepath>] [--extensions=<extensions>] [--ignore=<patterns>]'.PHP_EOL;
1438 echo ' [--stdin-path=<stdinPath>] [--file-list=<fileList>] <file> - ...'.PHP_EOL;
1439 echo PHP_EOL;
1440 echo ' - Fix STDIN instead of local files and directories'.PHP_EOL;
1441 echo ' -n Do not fix warnings (shortcut for --warning-severity=0)'.PHP_EOL;
1442 echo ' -w Fix both warnings and errors (on by default)'.PHP_EOL;
1443 echo ' -l Local directory only, no recursion'.PHP_EOL;
1444 echo ' -p Show progress of the run'.PHP_EOL;
1445 echo ' -q Quiet mode; disables progress and verbose output'.PHP_EOL;
1446 echo ' -v Print processed files'.PHP_EOL;
1447 echo ' -vv Print ruleset and token output'.PHP_EOL;
1448 echo ' -vvv Print sniff processing information'.PHP_EOL;
1449 echo ' -i Show a list of installed coding standards'.PHP_EOL;
1450 echo ' -d Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
1451 echo PHP_EOL;
1452 echo ' --help Print this help message'.PHP_EOL;
1453 echo ' --version Print version information'.PHP_EOL;
1454 echo ' --ignore-annotations Ignore all phpcs: annotations in code comments'.PHP_EOL;
1455 echo PHP_EOL;
1456 echo ' <basepath> A path to strip from the front of file paths inside reports'.PHP_EOL;
1457 echo ' <bootstrap> A comma separated list of files to run before processing begins'.PHP_EOL;
1458 echo ' <file> One or more files and/or directories to fix'.PHP_EOL;
1459 echo ' <fileList> A file containing a list of files and/or directories to fix (one per line)'.PHP_EOL;
1460 echo ' <encoding> The encoding of the files being fixed (default is utf-8)'.PHP_EOL;
1461 echo ' <extensions> A comma separated list of file extensions to fix'.PHP_EOL;
1462 echo ' The type of the file can be specified using: ext/type'.PHP_EOL;
1463 echo ' e.g., module/php,es/js'.PHP_EOL;
1464 echo ' <patterns> A comma separated list of patterns to ignore files and directories'.PHP_EOL;
1465 echo ' <processes> How many files should be fixed simultaneously (default is 1)'.PHP_EOL;
1466 echo ' <severity> The minimum severity required to fix an error or warning'.PHP_EOL;
1467 echo ' <sniffs> A comma separated list of sniff codes to include or exclude from fixing'.PHP_EOL;
1468 echo ' (all sniffs must be part of the specified standard)'.PHP_EOL;
1469 echo ' <standard> The name or path of the coding standard to use'.PHP_EOL;
1470 echo ' <stdinPath> If processing STDIN, the file path that STDIN will be processed as'.PHP_EOL;
1471 echo ' <suffix> Write modified files to a filename using this suffix'.PHP_EOL;
1472 echo ' ("diff" and "patch" are not used in this mode)'.PHP_EOL;
1473 echo ' <tabWidth> The number of spaces each tab represents'.PHP_EOL;
1474
1475 }//end printPHPCBFUsage()
1476
1477
1478 /**
1479 * Get a single config value.
1480 *
1481 * @param string $key The name of the config value.
1482 *
1483 * @return string|null
1484 * @see setConfigData()
1485 * @see getAllConfigData()
1486 */
1487 public static function getConfigData($key)
1488 {
1489 $phpCodeSnifferConfig = self::getAllConfigData();
1490
1491 if ($phpCodeSnifferConfig === null) {
1492 return null;
1493 }
1494
1495 if (isset($phpCodeSnifferConfig[$key]) === false) {
1496 return null;
1497 }
1498
1499 return $phpCodeSnifferConfig[$key];
1500
1501 }//end getConfigData()
1502
1503
1504 /**
1505 * Get the path to an executable utility.
1506 *
1507 * @param string $name The name of the executable utility.
1508 *
1509 * @return string|null
1510 * @see getConfigData()
1511 */
1512 public static function getExecutablePath($name)
1513 {
1514 $data = self::getConfigData($name.'_path');
1515 if ($data !== null) {
1516 return $data;
1517 }
1518
1519 if ($name === "php") {
1520 // For php, we know the executable path. There's no need to look it up.
1521 return PHP_BINARY;
1522 }
1523
1524 if (array_key_exists($name, self::$executablePaths) === true) {
1525 return self::$executablePaths[$name];
1526 }
1527
1528 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1529 $cmd = 'where '.escapeshellarg($name).' 2> nul';
1530 } else {
1531 $cmd = 'which '.escapeshellarg($name).' 2> /dev/null';
1532 }
1533
1534 $result = exec($cmd, $output, $retVal);
1535 if ($retVal !== 0) {
1536 $result = null;
1537 }
1538
1539 self::$executablePaths[$name] = $result;
1540 return $result;
1541
1542 }//end getExecutablePath()
1543
1544
1545 /**
1546 * Set a single config value.
1547 *
1548 * @param string $key The name of the config value.
1549 * @param string|null $value The value to set. If null, the config
1550 * entry is deleted, reverting it to the
1551 * default value.
1552 * @param boolean $temp Set this config data temporarily for this
1553 * script run. This will not write the config
1554 * data to the config file.
1555 *
1556 * @return bool
1557 * @see getConfigData()
1558 * @throws RuntimeException If the config file can not be written.
1559 */
1560 public static function setConfigData($key, $value, $temp=false)
1561 {
1562 if (isset(self::$overriddenDefaults['runtime-set']) === true
1563 && isset(self::$overriddenDefaults['runtime-set'][$key]) === true
1564 ) {
1565 return false;
1566 }
1567
1568 if ($temp === false) {
1569 $path = '';
1570 if (is_callable('\Phar::running') === true) {
1571 $path = \Phar::running(false);
1572 }
1573
1574 if ($path !== '') {
1575 $configFile = dirname($path).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1576 } else {
1577 $configFile = dirname(__DIR__).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1578 if (is_file($configFile) === false
1579 && strpos('@data_dir@', '@data_dir') === false
1580 ) {
1581 // If data_dir was replaced, this is a PEAR install and we can
1582 // use the PEAR data dir to store the conf file.
1583 $configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf';
1584 }
1585 }
1586
1587 if (is_file($configFile) === true
1588 && is_writable($configFile) === false
1589 ) {
1590 $error = 'ERROR: Config file '.$configFile.' is not writable'.PHP_EOL.PHP_EOL;
1591 throw new DeepExitException($error, 3);
1592 }
1593 }//end if
1594
1595 $phpCodeSnifferConfig = self::getAllConfigData();
1596
1597 if ($value === null) {
1598 if (isset($phpCodeSnifferConfig[$key]) === true) {
1599 unset($phpCodeSnifferConfig[$key]);
1600 }
1601 } else {
1602 $phpCodeSnifferConfig[$key] = $value;
1603 }
1604
1605 if ($temp === false) {
1606 $output = '<'.'?php'."\n".' $phpCodeSnifferConfig = ';
1607 $output .= var_export($phpCodeSnifferConfig, true);
1608 $output .= "\n?".'>';
1609
1610 if (file_put_contents($configFile, $output) === false) {
1611 $error = 'ERROR: Config file '.$configFile.' could not be written'.PHP_EOL.PHP_EOL;
1612 throw new DeepExitException($error, 3);
1613 }
1614
1615 self::$configDataFile = $configFile;
1616 }
1617
1618 self::$configData = $phpCodeSnifferConfig;
1619
1620 // If the installed paths are being set, make sure all known
1621 // standards paths are added to the autoloader.
1622 if ($key === 'installed_paths') {
1623 $installedStandards = Util\Standards::getInstalledStandardDetails();
1624 foreach ($installedStandards as $name => $details) {
1625 Autoload::addSearchPath($details['path'], $details['namespace']);
1626 }
1627 }
1628
1629 return true;
1630
1631 }//end setConfigData()
1632
1633
1634 /**
1635 * Get all config data.
1636 *
1637 * @return array<string, string>
1638 * @see getConfigData()
1639 */
1640 public static function getAllConfigData()
1641 {
1642 if (self::$configData !== null) {
1643 return self::$configData;
1644 }
1645
1646 $path = '';
1647 if (is_callable('\Phar::running') === true) {
1648 $path = \Phar::running(false);
1649 }
1650
1651 if ($path !== '') {
1652 $configFile = dirname($path).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1653 } else {
1654 $configFile = dirname(__DIR__).DIRECTORY_SEPARATOR.'CodeSniffer.conf';
1655 if (is_file($configFile) === false
1656 && strpos('@data_dir@', '@data_dir') === false
1657 ) {
1658 $configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf';
1659 }
1660 }
1661
1662 if (is_file($configFile) === false) {
1663 self::$configData = [];
1664 return [];
1665 }
1666
1667 if (is_readable($configFile) === false) {
1668 $error = 'ERROR: Config file '.$configFile.' is not readable'.PHP_EOL.PHP_EOL;
1669 throw new DeepExitException($error, 3);
1670 }
1671
1672 include $configFile;
1673 self::$configDataFile = $configFile;
1674 self::$configData = $phpCodeSnifferConfig;
1675 return self::$configData;
1676
1677 }//end getAllConfigData()
1678
1679
1680 /**
1681 * Prints out the gathered config data.
1682 *
1683 * @param array $data The config data to print.
1684 *
1685 * @return void
1686 */
1687 public function printConfigData($data)
1688 {
1689 $max = 0;
1690 $keys = array_keys($data);
1691 foreach ($keys as $key) {
1692 $len = strlen($key);
1693 if (strlen($key) > $max) {
1694 $max = $len;
1695 }
1696 }
1697
1698 if ($max === 0) {
1699 return;
1700 }
1701
1702 $max += 2;
1703 ksort($data);
1704 foreach ($data as $name => $value) {
1705 echo str_pad($name.': ', $max).$value.PHP_EOL;
1706 }
1707
1708 }//end printConfigData()
1709
1710
1711 }//end class