3
* A class to process command line phpcs scripts.
8
* @package PHP_CodeSniffer
9
* @author Greg Sherwood <gsherwood@squiz.net>
10
* @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
11
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
12
* @link http://pear.php.net/package/PHP_CodeSniffer
15
if (is_file(dirname(__FILE__).'/../CodeSniffer.php') === true) {
16
include_once dirname(__FILE__).'/../CodeSniffer.php';
18
include_once 'PHP/CodeSniffer.php';
22
* A class to process command line phpcs scripts.
25
* @package PHP_CodeSniffer
26
* @author Greg Sherwood <gsherwood@squiz.net>
27
* @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
28
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
29
* @version Release: 1.5.3
30
* @link http://pear.php.net/package/PHP_CodeSniffer
32
class PHP_CodeSniffer_CLI
36
* An array of all values specified on the command line.
40
protected $values = array();
43
* The minimum severity level errors must have to be displayed.
47
public $errorSeverity = 0;
50
* The minimum severity level warnings must have to be displayed.
54
public $warningSeverity = 0;
57
* Whether or not to kill the process when an unknown command line arg is found.
59
* If FALSE, arguments that are not command line options or file/directory paths
60
* will be ignored and execution will continue.
64
public $dieOnUnknownArg = true;
68
* Exits if the minimum requirements of PHP_CodSniffer are not met.
72
public function checkRequirements()
74
// Check the PHP version.
75
if (version_compare(PHP_VERSION, '5.1.2') === -1) {
76
echo 'ERROR: PHP_CodeSniffer requires PHP version 5.1.2 or greater.'.PHP_EOL;
80
if (extension_loaded('tokenizer') === false) {
81
echo 'ERROR: PHP_CodeSniffer requires the tokenizer extension to be enabled.'.PHP_EOL;
85
}//end checkRequirements()
89
* Get a list of default values for all possible command line arguments.
93
public function getDefaults()
95
// The default values for config settings.
96
$defaults['files'] = array();
97
$defaults['standard'] = null;
98
$defaults['verbosity'] = 0;
99
$defaults['interactive'] = false;
100
$defaults['explain'] = false;
101
$defaults['local'] = false;
102
$defaults['showSources'] = false;
103
$defaults['extensions'] = array();
104
$defaults['sniffs'] = array();
105
$defaults['ignored'] = array();
106
$defaults['reportFile'] = null;
107
$defaults['generator'] = '';
108
$defaults['reports'] = array();
109
$defaults['errorSeverity'] = null;
110
$defaults['warningSeverity'] = null;
112
$reportFormat = PHP_CodeSniffer::getConfigData('report_format');
113
if ($reportFormat !== null) {
114
$defaults['reports'][$reportFormat] = null;
117
$tabWidth = PHP_CodeSniffer::getConfigData('tab_width');
118
if ($tabWidth === null) {
119
$defaults['tabWidth'] = 0;
121
$defaults['tabWidth'] = (int) $tabWidth;
124
$encoding = PHP_CodeSniffer::getConfigData('encoding');
125
if ($encoding === null) {
126
$defaults['encoding'] = 'iso-8859-1';
128
$defaults['encoding'] = strtolower($encoding);
131
$severity = PHP_CodeSniffer::getConfigData('severity');
132
if ($severity !== null) {
133
$defaults['errorSeverity'] = (int) $severity;
134
$defaults['warningSeverity'] = (int) $severity;
137
$severity = PHP_CodeSniffer::getConfigData('error_severity');
138
if ($severity !== null) {
139
$defaults['errorSeverity'] = (int) $severity;
142
$severity = PHP_CodeSniffer::getConfigData('warning_severity');
143
if ($severity !== null) {
144
$defaults['warningSeverity'] = (int) $severity;
147
$showWarnings = PHP_CodeSniffer::getConfigData('show_warnings');
148
if ($showWarnings !== null) {
149
$showWarnings = (bool) $showWarnings;
150
if ($showWarnings === false) {
151
$defaults['warningSeverity'] = 0;
155
$reportWidth = PHP_CodeSniffer::getConfigData('report_width');
156
if ($reportWidth === null) {
157
$defaults['reportWidth'] = 80;
159
$defaults['reportWidth'] = (int) $reportWidth;
162
$showProgress = PHP_CodeSniffer::getConfigData('show_progress');
163
if ($showProgress === null) {
164
$defaults['showProgress'] = false;
166
$defaults['showProgress'] = (bool) $showProgress;
175
* Process the command line arguments and returns the values.
179
public function getCommandLineValues()
181
if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
185
if (empty($this->values) === false) {
186
return $this->values;
189
$values = $this->getDefaults();
191
for ($i = 1; $i < $_SERVER['argc']; $i++) {
192
$arg = $_SERVER['argv'][$i];
197
if ($arg{0} === '-') {
198
if ($arg === '-' || $arg === '--') {
199
// Empty argument, ignore it.
203
if ($arg{1} === '-') {
205
= $this->processLongArgument(substr($arg, 2), $i, $values);
207
$switches = str_split($arg);
208
foreach ($switches as $switch) {
209
if ($switch === '-') {
213
$values = $this->processShortArgument($switch, $i, $values);
217
$values = $this->processUnknownArgument($arg, $i, $values);
221
$this->values = $values;
224
}//end getCommandLineValues()
228
* Processes a short (-e) command line argument.
230
* @param string $arg The command line argument.
231
* @param int $pos The position of the argument on the command line.
232
* @param array $values An array of values determined from CLI args.
234
* @return array The updated CLI values.
235
* @see getCommandLineValues()
237
public function processShortArgument($arg, $pos, $values)
246
$this->printInstalledStandards();
250
$values['verbosity']++;
253
$values['local'] = true;
256
$values['showSources'] = true;
259
$values['interactive'] = true;
262
$values['explain'] = true;
265
$values['showProgress'] = true;
268
$ini = explode('=', $_SERVER['argv'][($pos + 1)]);
269
$_SERVER['argv'][($pos + 1)] = '';
270
if (isset($ini[1]) === true) {
271
ini_set($ini[0], $ini[1]);
273
ini_set($ini[0], true);
278
$values['warningSeverity'] = 0;
281
$values['warningSeverity'] = null;
284
$values = $this->processUnknownArgument('-'.$arg, $pos, $values);
289
}//end processShortArgument()
293
* Processes a long (--example) command line argument.
295
* @param string $arg The command line argument.
296
* @param int $pos The position of the argument on the command line.
297
* @param array $values An array of values determined from CLI args.
299
* @return array The updated CLI values.
300
* @see getCommandLineValues()
302
public function processLongArgument($arg, $pos, $values)
309
echo 'PHP_CodeSniffer version '.PHP_CodeSniffer::VERSION.' ('.PHP_CodeSniffer::STABILITY.') ';
310
echo 'by Squiz (http://www.squiz.net)'.PHP_EOL;
313
$key = $_SERVER['argv'][($pos + 1)];
314
$value = $_SERVER['argv'][($pos + 2)];
315
PHP_CodeSniffer::setConfigData($key, $value);
317
case 'config-delete':
318
$key = $_SERVER['argv'][($pos + 1)];
319
PHP_CodeSniffer::setConfigData($key, null);
322
$data = PHP_CodeSniffer::getAllConfigData();
326
$key = $_SERVER['argv'][($pos + 1)];
327
$value = $_SERVER['argv'][($pos + 2)];
328
$_SERVER['argv'][($pos + 1)] = '';
329
$_SERVER['argv'][($pos + 2)] = '';
330
PHP_CodeSniffer::setConfigData($key, $value, true);
333
if (substr($arg, 0, 7) === 'sniffs=') {
334
$sniffs = substr($arg, 7);
335
$values['sniffs'] = explode(',', $sniffs);
336
} else if (substr($arg, 0, 12) === 'report-file=') {
337
$values['reportFile'] = realpath(substr($arg, 12));
339
// It may not exist and return false instead.
340
if ($values['reportFile'] === false) {
341
$values['reportFile'] = substr($arg, 12);
344
if (is_dir($values['reportFile']) === true) {
345
echo 'ERROR: The specified report file path "'.$values['reportFile'].'" is a directory.'.PHP_EOL.PHP_EOL;
350
$dir = dirname($values['reportFile']);
351
if (is_dir($dir) === false) {
352
echo 'ERROR: The specified report file path "'.$values['reportFile'].'" points to a non-existent directory.'.PHP_EOL.PHP_EOL;
358
// Passed report file is a filename in the current directory.
359
$values['reportFile'] = getcwd().'/'.basename($values['reportFile']);
361
$dir = realpath(getcwd().'/'.$dir);
362
if ($dir !== false) {
363
// Report file path is relative.
364
$values['reportFile'] = $dir.'/'.basename($values['reportFile']);
367
} else if (substr($arg, 0, 13) === 'report-width=') {
368
$values['reportWidth'] = (int) substr($arg, 13);
369
} else if (substr($arg, 0, 7) === 'report='
370
|| substr($arg, 0, 7) === 'report-'
372
if ($arg[6] === '-') {
373
// This is a report with file output.
374
$split = strpos($arg, '=');
375
if ($split === false) {
376
$report = substr($arg, 7);
379
$report = substr($arg, 7, ($split - 7));
380
$output = substr($arg, ($split + 1));
381
if ($output === false) {
384
$dir = dirname($output);
386
// Passed report file is a filename in the current directory.
387
$output = getcwd().'/'.basename($output);
389
$dir = realpath(getcwd().'/'.$dir);
390
if ($dir !== false) {
391
// Report file path is relative.
392
$output = $dir.'/'.basename($output);
398
// This is a single report.
399
$report = substr($arg, 7);
403
$validReports = array(
419
if (in_array($report, $validReports) === false) {
420
echo 'ERROR: Report type "'.$report.'" not known.'.PHP_EOL;
424
$values['reports'][$report] = $output;
425
} else if (substr($arg, 0, 9) === 'standard=') {
426
$values['standard'] = explode(',', substr($arg, 9));
427
} else if (substr($arg, 0, 11) === 'extensions=') {
428
$values['extensions'] = explode(',', substr($arg, 11));
429
} else if (substr($arg, 0, 9) === 'severity=') {
430
$values['errorSeverity'] = (int) substr($arg, 9);
431
$values['warningSeverity'] = $values['errorSeverity'];
432
} else if (substr($arg, 0, 15) === 'error-severity=') {
433
$values['errorSeverity'] = (int) substr($arg, 15);
434
} else if (substr($arg, 0, 17) === 'warning-severity=') {
435
$values['warningSeverity'] = (int) substr($arg, 17);
436
} else if (substr($arg, 0, 7) === 'ignore=') {
437
// Split the ignore string on commas, unless the comma is escaped
438
// using 1 or 3 slashes (\, or \\\,).
439
$ignored = preg_split(
440
'/(?<=(?<!\\\\)\\\\\\\\),|(?<!\\\\),/',
443
foreach ($ignored as $pattern) {
444
$values['ignored'][$pattern] = 'absolute';
446
} else if (substr($arg, 0, 10) === 'generator=') {
447
$values['generator'] = substr($arg, 10);
448
} else if (substr($arg, 0, 9) === 'encoding=') {
449
$values['encoding'] = strtolower(substr($arg, 9));
450
} else if (substr($arg, 0, 10) === 'tab-width=') {
451
$values['tabWidth'] = (int) substr($arg, 10);
453
$values = $this->processUnknownArgument('--'.$arg, $pos, $values);
461
}//end processLongArgument()
465
* Processes an unknown command line argument.
467
* Assumes all unknown arguments are files and folders to check.
469
* @param string $arg The command line argument.
470
* @param int $pos The position of the argument on the command line.
471
* @param array $values An array of values determined from CLI args.
473
* @return array The updated CLI values.
474
* @see getCommandLineValues()
476
public function processUnknownArgument($arg, $pos, $values)
478
// We don't know about any additional switches; just files.
479
if ($arg{0} === '-') {
480
if ($this->dieOnUnknownArg === false) {
484
echo 'ERROR: option "'.$arg.'" not known.'.PHP_EOL.PHP_EOL;
489
$file = realpath($arg);
490
if (file_exists($file) === false) {
491
if ($this->dieOnUnknownArg === false) {
495
echo 'ERROR: The file "'.$arg.'" does not exist.'.PHP_EOL.PHP_EOL;
499
$values['files'][] = $file;
504
}//end processUnknownArgument()
508
* Runs PHP_CodeSniffer over files and directories.
510
* @param array $values An array of values determined from CLI args.
512
* @return int The number of error and warning messages shown.
513
* @see getCommandLineValues()
515
public function process($values=array())
517
if (empty($values) === true) {
518
$values = $this->getCommandLineValues();
520
$values = array_merge($this->getDefaults(), $values);
521
$this->values = $values;
524
if ($values['generator'] !== '') {
525
$phpcs = new PHP_CodeSniffer($values['verbosity']);
526
foreach ($values['standard'] as $standard) {
527
$phpcs->generateDocs(
537
// If no standard is supplied, get the default.
538
$values['standard'] = $this->validateStandard($values['standard']);
539
foreach ($values['standard'] as $standard) {
540
if (PHP_CodeSniffer::isInstalledStandard($standard) === false) {
541
// They didn't select a valid coding standard, so help them
542
// out by letting them know which standards are installed.
543
echo 'ERROR: the "'.$standard.'" coding standard is not installed. ';
544
$this->printInstalledStandards();
549
if ($values['explain'] === true) {
550
foreach ($values['standard'] as $standard) {
551
$this->explainStandard($standard);
558
if (empty($values['files']) === true) {
559
// Check if they are passing in the file contents.
560
$handle = fopen('php://stdin', 'r');
561
$fileContents = stream_get_contents($handle);
564
if ($fileContents === '') {
565
// No files and no content passed in.
566
echo 'ERROR: You must supply at least one file or directory to process.'.PHP_EOL.PHP_EOL;
572
$phpcs = new PHP_CodeSniffer(
573
$values['verbosity'],
576
$values['interactive']
579
// Set file extensions if they were specified. Otherwise,
580
// let PHP_CodeSniffer decide on the defaults.
581
if (empty($values['extensions']) === false) {
582
$phpcs->setAllowedFileExtensions($values['extensions']);
585
// Set ignore patterns if they were specified.
586
if (empty($values['ignored']) === false) {
587
$phpcs->setIgnorePatterns($values['ignored']);
590
// Set some convenience member vars.
591
if ($values['errorSeverity'] === null) {
592
$this->errorSeverity = PHPCS_DEFAULT_ERROR_SEV;
594
$this->errorSeverity = $values['errorSeverity'];
597
if ($values['warningSeverity'] === null) {
598
$this->warningSeverity = PHPCS_DEFAULT_WARN_SEV;
600
$this->warningSeverity = $values['warningSeverity'];
603
if (empty($values['reports']) === true) {
604
$this->values['reports']['full'] = $values['reportFile'];
607
$phpcs->setCli($this);
616
if ($fileContents !== '') {
617
$phpcs->processFile('STDIN', $fileContents);
620
// Interactive runs don't require a final report and it doesn't really
621
// matter what the retun value is because we know it isn't being read
623
if ($values['interactive'] === true) {
627
return $this->printErrorReport(
630
$values['showSources'],
631
$values['reportFile'],
632
$values['reportWidth']
639
* Prints the error report for the run.
641
* Note that this function may actually print multiple reports
642
* as the user may have specified a number of output formats.
644
* @param PHP_CodeSniffer $phpcs The PHP_CodeSniffer object containing
646
* @param array $reports A list of reports to print.
647
* @param bool $showSources TRUE if report should show error sources
648
* (not used by all reports).
649
* @param string $reportFile A default file to log report output to.
650
* @param int $reportWidth How wide the screen reports should be.
652
* @return int The number of error and warning messages shown.
654
public function printErrorReport(
655
PHP_CodeSniffer $phpcs,
661
if (empty($reports) === true) {
662
$reports['full'] = $reportFile;
668
foreach ($reports as $report => $output) {
669
if ($output === null) {
670
$output = $reportFile;
673
if ($reportFile === null) {
677
// We don't add errors here because the number of
678
// errors reported by each report type will always be the
679
// same, so we really just need 1 number.
680
$errors = $phpcs->reporting->printReport(
688
// Only print PHP_Timer output if no reports were
689
// printed to the screen so we don't put additional output
690
// in something like an XML report. If we are printing to screen,
691
// the report types would have already worked out who should
692
// print the timer info.
693
if ($toScreen === false
694
&& PHP_CODESNIFFER_INTERACTIVE === false
695
&& class_exists('PHP_Timer', false) === true
697
echo PHP_Timer::resourceUsage().PHP_EOL.PHP_EOL;
700
// They should all return the same value, so it
701
// doesn't matter which return value we end up using.
704
}//end printErrorReport()
708
* Convert the passed standards into valid standards.
710
* Checks things like default values and case.
712
* @param array $standards The standards to validate.
716
public function validateStandard($standards)
718
if ($standards === null) {
719
// They did not supply a standard to use.
720
// Try to get the default from the config system.
721
$standard = PHP_CodeSniffer::getConfigData('default_standard');
722
if ($standard === null) {
723
// Product default standard.
727
return array($standard);
732
// Check if the standard name is valid, or if the case is invalid.
733
$installedStandards = PHP_CodeSniffer::getInstalledStandards();
734
foreach ($standards as $standard) {
735
foreach ($installedStandards as $validStandard) {
736
if (strtolower($standard) === strtolower($validStandard)) {
737
$standard = $validStandard;
742
$cleaned[] = $standard;
747
}//end validateStandard()
751
* Prints a report showing the sniffs contained in a standard.
753
* @param string $standard The standard to validate.
757
public function explainStandard($standard)
759
$phpcs = new PHP_CodeSniffer();
760
$phpcs->process(array(), $standard);
761
$sniffs = $phpcs->getSniffs();
762
$sniffs = array_keys($sniffs);
769
$sniffCount = count($sniffs);
772
echo PHP_EOL."The $standard standard contains $sniffCount sniffs".PHP_EOL;
776
foreach ($sniffs as $sniff) {
777
$parts = explode('_', str_replace('\\', '_', $sniff));
778
if ($lastStandard === '') {
779
$lastStandard = $parts[0];
782
if ($parts[0] !== $lastStandard) {
783
$sniffList = ob_get_contents();
786
echo PHP_EOL.$lastStandard.' ('.$lastCount.' sniffs)'.PHP_EOL;
787
echo str_repeat('-', strlen($lastStandard.$lastCount) + 10);
791
$lastStandard = $parts[0];
797
echo ' '.$parts[0].'.'.$parts[2].'.'.substr($parts[3], 0, -5).PHP_EOL;
803
}//end explainStandard()
807
* Prints out the usage information for this script.
811
public function printUsage()
813
echo 'Usage: phpcs [-nwlsaepvi] [-d key[=value]]'.PHP_EOL;
814
echo ' [--report=<report>] [--report-file=<reportfile>] [--report-<report>=<reportfile>] ...'.PHP_EOL;
815
echo ' [--report-width=<reportWidth>] [--generator=<generator>] [--tab-width=<tabWidth>]'.PHP_EOL;
816
echo ' [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
817
echo ' [--runtime-set key value] [--config-set key value] [--config-delete key] [--config-show]'.PHP_EOL;
818
echo ' [--standard=<standard>] [--sniffs=<sniffs>] [--encoding=<encoding>]'.PHP_EOL;
819
echo ' [--extensions=<extensions>] [--ignore=<patterns>] <file> ...'.PHP_EOL;
820
echo ' Set runtime value (see --config-set) '.PHP_EOL;
821
echo ' -n Do not print warnings (shortcut for --warning-severity=0)'.PHP_EOL;
822
echo ' -w Print both warnings and errors (on by default)'.PHP_EOL;
823
echo ' -l Local directory only, no recursion'.PHP_EOL;
824
echo ' -s Show sniff codes in all reports'.PHP_EOL;
825
echo ' -a Run interactively'.PHP_EOL;
826
echo ' -e Explain a standard by showing the sniffs it includes'.PHP_EOL;
827
echo ' -p Show progress of the run'.PHP_EOL;
828
echo ' -v[v][v] Print verbose output'.PHP_EOL;
829
echo ' -i Show a list of installed coding standards'.PHP_EOL;
830
echo ' -d Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
831
echo ' --help Print this help message'.PHP_EOL;
832
echo ' --version Print version information'.PHP_EOL;
833
echo ' <file> One or more files and/or directories to check'.PHP_EOL;
834
echo ' <extensions> A comma separated list of file extensions to check'.PHP_EOL;
835
echo ' (only valid if checking a directory)'.PHP_EOL;
836
echo ' <patterns> A comma separated list of patterns to ignore files and directories'.PHP_EOL;
837
echo ' <encoding> The encoding of the files being checked (default is iso-8859-1)'.PHP_EOL;
838
echo ' <sniffs> A comma separated list of sniff codes to limit the check to'.PHP_EOL;
839
echo ' (all sniffs must be part of the specified standard)'.PHP_EOL;
840
echo ' <severity> The minimum severity required to display an error or warning'.PHP_EOL;
841
echo ' <standard> The name or path of the coding standard to use'.PHP_EOL;
842
echo ' <tabWidth> The number of spaces each tab represents'.PHP_EOL;
843
echo ' <generator> The name of a doc generator to use'.PHP_EOL;
844
echo ' (forces doc generation instead of checking)'.PHP_EOL;
845
echo ' <report> Print either the "full", "xml", "checkstyle", "csv", "json"'.PHP_EOL;
846
echo ' "emacs", "source", "summary", "svnblame", "gitblame", "hgblame" or'.PHP_EOL;
847
echo ' "notifysend" report'.PHP_EOL;
848
echo ' (the "full" report is printed by default)'.PHP_EOL;
849
echo ' <reportfile> Write the report to the specified file path'.PHP_EOL;
850
echo ' <reportWidth> How many columns wide screen reports should be printed'.PHP_EOL;
856
* Prints out a list of installed coding standards.
860
public function printInstalledStandards()
862
$installedStandards = PHP_CodeSniffer::getInstalledStandards();
863
$numStandards = count($installedStandards);
865
if ($numStandards === 0) {
866
echo 'No coding standards are installed.'.PHP_EOL;
868
$lastStandard = array_pop($installedStandards);
869
if ($numStandards === 1) {
870
echo "The only coding standard installed is $lastStandard".PHP_EOL;
872
$standardList = implode(', ', $installedStandards);
873
$standardList .= ' and '.$lastStandard;
874
echo 'The installed coding standards are '.$standardList.PHP_EOL;
878
}//end printInstalledStandards()