From c3a9dfe84423e67ecb49f85365f53295d742be44 Mon Sep 17 00:00:00 2001 From: schlndh Date: Sat, 11 Oct 2025 14:08:54 +0200 Subject: [PATCH 1/4] Runner: allow suppressing "No files were checked" error Fixes https://github.com/squizlabs/PHP_CodeSniffer/issues/3556 The "No files were checked" error can be undesirable in situations where an external tools passes a list of files to phpcs to check, without necessarily knowing whether the files are excluded or not. Consider the following scenarios: - CI running only on changed files. - Git hooks. - Phpstorm PHPCS inspection (currently it gives an error notification on excluded files). --- src/Config.php | 83 +++++++++++-------- src/Runner.php | 2 +- .../Runner/RunAllFilesExcludedErrorTest.php | 57 +++++++++++++ 3 files changed, 105 insertions(+), 37 deletions(-) diff --git a/src/Config.php b/src/Config.php index fd8eb90358..acc9d8e458 100644 --- a/src/Config.php +++ b/src/Config.php @@ -65,6 +65,7 @@ * @property string $stdinContent Content passed directly to PHPCS on STDIN. * @property string $stdinPath The path to use for content passed on STDIN. * @property bool $trackTime Whether or not to track sniff run time. + * @property bool $allowEmptyFileList Suppresses "No files were checked" error. * * @property array $extensions File extensions that should be checked, and what tokenizer is used. * E.g., array('inc' => 'PHP'); @@ -160,42 +161,43 @@ class Config * @var array */ private $settings = [ - 'files' => null, - 'standards' => null, - 'verbosity' => null, - 'interactive' => null, - 'parallel' => null, - 'cache' => null, - 'cacheFile' => null, - 'colors' => null, - 'explain' => null, - 'local' => null, - 'showSources' => null, - 'showProgress' => null, - 'quiet' => null, - 'annotations' => null, - 'tabWidth' => null, - 'encoding' => null, - 'extensions' => null, - 'sniffs' => null, - 'exclude' => null, - 'ignored' => null, - 'reportFile' => null, - 'generator' => null, - 'filter' => null, - 'bootstrap' => null, - 'reports' => null, - 'basepath' => null, - 'reportWidth' => null, - 'errorSeverity' => null, - 'warningSeverity' => null, - 'recordErrors' => null, - 'suffix' => null, - 'stdin' => null, - 'stdinContent' => null, - 'stdinPath' => null, - 'trackTime' => null, - 'unknown' => null, + 'files' => null, + 'standards' => null, + 'verbosity' => null, + 'interactive' => null, + 'parallel' => null, + 'cache' => null, + 'cacheFile' => null, + 'colors' => null, + 'explain' => null, + 'local' => null, + 'showSources' => null, + 'showProgress' => null, + 'quiet' => null, + 'annotations' => null, + 'tabWidth' => null, + 'encoding' => null, + 'extensions' => null, + 'sniffs' => null, + 'exclude' => null, + 'ignored' => null, + 'reportFile' => null, + 'generator' => null, + 'filter' => null, + 'bootstrap' => null, + 'reports' => null, + 'basepath' => null, + 'reportWidth' => null, + 'errorSeverity' => null, + 'warningSeverity' => null, + 'recordErrors' => null, + 'suffix' => null, + 'stdin' => null, + 'stdinContent' => null, + 'stdinPath' => null, + 'trackTime' => null, + 'unknown' => null, + 'allowEmptyFileList' => null, ]; /** @@ -581,6 +583,7 @@ public function restoreDefaults() $this->stdinPath = null; $this->trackTime = false; $this->unknown = []; + $this->allowEmptyFileList = false; $standard = self::getConfigData('default_standard'); if ($standard !== null) { @@ -828,6 +831,14 @@ public function processLongArgument(string $arg, int $pos) $this->annotations = false; $this->overriddenDefaults['annotations'] = true; break; + case 'allow-empty-file-list': + if (isset($this->overriddenDefaults['allowEmptyFileList']) === true) { + break; + } + + $this->allowEmptyFileList = true; + $this->overriddenDefaults['allowEmptyFileList'] = true; + break; case 'config-set': if (isset($this->cliArgs[($pos + 1)]) === false || isset($this->cliArgs[($pos + 2)]) === false diff --git a/src/Runner.php b/src/Runner.php index db0e26d91b..de15ca9163 100644 --- a/src/Runner.php +++ b/src/Runner.php @@ -351,7 +351,7 @@ private function run() } $numFiles = count($todo); - if ($numFiles === 0) { + if ($numFiles === 0 && $this->config->allowEmptyFileList === false) { $error = 'ERROR: No files were checked.' . PHP_EOL; $error .= 'All specified files were excluded or did not match filtering rules.' . PHP_EOL . PHP_EOL; throw new DeepExitException($error, ExitCode::PROCESS_ERROR); diff --git a/tests/Core/Runner/RunAllFilesExcludedErrorTest.php b/tests/Core/Runner/RunAllFilesExcludedErrorTest.php index c38420cdae..e736a0225f 100644 --- a/tests/Core/Runner/RunAllFilesExcludedErrorTest.php +++ b/tests/Core/Runner/RunAllFilesExcludedErrorTest.php @@ -73,6 +73,63 @@ public function testPhpcbf($sourceDir, $extraArgs) } + /** + * Verify that the "All files were excluded" error message is not shown when all files are excluded and it is + * allowed via --allow-empty-file-list (PHPCS). + * + * @param string $sourceDir The (fixture) directory to scan for files. + * @param array $extraArgs Any extra arguments to pass on the command line. + * + * @dataProvider data + * + * @return void + */ + public function testPhpcsAllowEmpty($sourceDir, $extraArgs) + { + if (PHP_CODESNIFFER_CBF === true) { + $this->markTestSkipped('This test needs CS mode to run'); + } + + $extraArgs[] = '--allow-empty-file-list'; + $this->setupTest($sourceDir, $extraArgs); + + $runner = new Runner(); + $runner->runPHPCS(); + + $regex = '`^(Time: [0-9]+ms; Memory: [0-9\.]+MB' . PHP_EOL . ')?$`'; + $this->assertStderrOutputMatchesRegex($regex); + } + + + /** + * Verify that the "All files were excluded" error message is not shown when all files are excluded and it is + * * allowed via --allow-empty-file-list (PHPCBF). + * + * @param string $sourceDir The (fixture) directory to scan for files. + * @param array $extraArgs Any extra arguments to pass on the command line. + * + * @dataProvider data + * @group CBF + * + * @return void + */ + public function testPhpcbfAllowEmpty($sourceDir, $extraArgs) + { + if (PHP_CODESNIFFER_CBF === false) { + $this->markTestSkipped('This test needs CBF mode to run'); + } + + $extraArgs[] = '--allow-empty-file-list'; + $this->setupTest($sourceDir, $extraArgs); + + $runner = new Runner(); + $runner->runPHPCBF(); + + $regex = '`^.*No violations were found.*(Time: [0-9]+ms; Memory: [0-9\.]+MB' . PHP_EOL . ')?$`m'; + $this->assertStderrOutputMatchesRegex($regex); + } + + /** * Data provider. * From b104d672a47c0622c3a07be35b0f168a8a967f89 Mon Sep 17 00:00:00 2001 From: schlndh Date: Sat, 11 Oct 2025 14:42:49 +0200 Subject: [PATCH 2/4] add help entry for --allow-empty-file-list --- src/Util/Help.php | 43 +++++++++++++++++-------------- tests/Core/Util/Help/HelpTest.php | 6 ++--- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/Util/Help.php b/src/Util/Help.php index c9faf71d76..d41cc1dffc 100644 --- a/src/Util/Help.php +++ b/src/Util/Help.php @@ -36,6 +36,7 @@ final class Help * @var array List of the option names. */ public const DEFAULT_LONG_OPTIONS = [ + 'allow-empty-file-list', 'basepath', 'bootstrap', 'colors', @@ -491,79 +492,83 @@ private function getAllOptions() ]; $options['Reporting Options'] = [ - 'report' => [ + 'report' => [ 'argument' => '--report=', 'description' => 'A comma-separated list of reports to print. Available reports: "full", "xml", "checkstyle", "csv", "json", "junit", "emacs", "source", "summary", "diff", "svnblame", "gitblame", "hgblame", "notifysend" or "performance".' . "\n" . 'Or specify the path to a custom report class. By default, the "full" report is displayed.', ], - 'report-file' => [ + 'report-file' => [ 'argument' => '--report-file=', 'description' => 'Write the report to the specified file path.', ], - 'report-report' => [ + 'report-report' => [ 'argument' => '--report-=', 'description' => 'Write the report specified in to the specified file path.', ], - 'report-width' => [ + 'report-width' => [ 'argument' => '--report-width=', 'description' => 'How many columns wide screen reports should be. Set to "auto" to use current screen width, where supported.', ], - 'basepath' => [ + 'basepath' => [ 'argument' => '--basepath=', 'description' => 'Strip a path from the front of file paths inside reports.', ], - 'blank-line-1' => ['spacer' => ''], + 'blank-line-1' => ['spacer' => ''], - 'w' => [ + 'w' => [ 'argument' => '-w', 'description' => 'Include both warnings and errors (default).', ], - 'n' => [ + 'n' => [ 'argument' => '-n', 'description' => 'Do not include warnings. Shortcut for "--warning-severity=0".', ], - 'severity' => [ + 'severity' => [ 'argument' => '--severity=', 'description' => 'The minimum severity required to display an error or warning. Defaults to 5.', ], - 'error-severity' => [ + 'error-severity' => [ 'argument' => '--error-severity=', 'description' => 'The minimum severity required to display an error. Defaults to 5.', ], - 'warning-severity' => [ + 'warning-severity' => [ 'argument' => '--warning-severity=', 'description' => 'The minimum severity required to display a warning. Defaults to 5.', ], - 'blank-line-2' => ['spacer' => ''], + 'blank-line-2' => ['spacer' => ''], - 's' => [ + 's' => [ 'argument' => '-s', 'description' => 'Show sniff error codes in all reports.', ], - 'ignore-annotations' => [ + 'ignore-annotations' => [ 'argument' => '--ignore-annotations', 'description' => 'Ignore all "phpcs:..." annotations in code comments.', ], - 'colors' => [ + 'colors' => [ 'argument' => '--colors', 'description' => 'Use colors in screen output.', ], - 'no-colors' => [ + 'no-colors' => [ 'argument' => '--no-colors', 'description' => 'Do not use colors in screen output (default).', ], - 'p' => [ + 'p' => [ 'argument' => '-p', 'description' => 'Show progress of the run.', ], - 'q' => [ + 'q' => [ 'argument' => '-q', 'description' => 'Quiet mode; disables progress and verbose output.', ], - 'm' => [ + 'm' => [ 'argument' => '-m', 'description' => 'Stop error messages from being recorded. This saves a lot of memory but stops many reports from being used.', ], + 'allow-empty-file-list' => [ + 'argument' => '--allow-empty-file-list', + 'description' => 'Suppress "No files were checked" error.', + ], ]; $options['Configuration Options'] = [ diff --git a/tests/Core/Util/Help/HelpTest.php b/tests/Core/Util/Help/HelpTest.php index e1dbe1c62c..160b4f7fd4 100644 --- a/tests/Core/Util/Help/HelpTest.php +++ b/tests/Core/Util/Help/HelpTest.php @@ -235,7 +235,7 @@ public static function dataOptionFiltering() 'Scan targets' => 8, 'Rule Selection Options' => 7, 'Run Options' => 8, - 'Reporting Options' => 19, + 'Reporting Options' => 20, 'Configuration Options' => 8, 'Miscellaneous Options' => 5, ], @@ -247,7 +247,7 @@ public static function dataOptionFiltering() 'Scan targets' => 8, 'Rule Selection Options' => 5, 'Run Options' => 4, - 'Reporting Options' => 14, + 'Reporting Options' => 15, 'Configuration Options' => 4, 'Miscellaneous Options' => 5, ], @@ -273,7 +273,7 @@ public static function dataOptionFiltering() 'Scan targets' => 8, 'Rule Selection Options' => 7, 'Run Options' => 8, - 'Reporting Options' => 19, + 'Reporting Options' => 20, 'Configuration Options' => 8, ], ], From 3f117b8a19e86da347ea7a4a042eb2bef77bfa32 Mon Sep 17 00:00:00 2001 From: schlndh Date: Sat, 11 Oct 2025 18:47:15 +0200 Subject: [PATCH 3/4] Revert "add help entry for --allow-empty-file-list" This reverts commit b104d672a47c0622c3a07be35b0f168a8a967f89. --- src/Util/Help.php | 43 ++++++++++++++----------------- tests/Core/Util/Help/HelpTest.php | 6 ++--- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/Util/Help.php b/src/Util/Help.php index d41cc1dffc..c9faf71d76 100644 --- a/src/Util/Help.php +++ b/src/Util/Help.php @@ -36,7 +36,6 @@ final class Help * @var array List of the option names. */ public const DEFAULT_LONG_OPTIONS = [ - 'allow-empty-file-list', 'basepath', 'bootstrap', 'colors', @@ -492,83 +491,79 @@ private function getAllOptions() ]; $options['Reporting Options'] = [ - 'report' => [ + 'report' => [ 'argument' => '--report=', 'description' => 'A comma-separated list of reports to print. Available reports: "full", "xml", "checkstyle", "csv", "json", "junit", "emacs", "source", "summary", "diff", "svnblame", "gitblame", "hgblame", "notifysend" or "performance".' . "\n" . 'Or specify the path to a custom report class. By default, the "full" report is displayed.', ], - 'report-file' => [ + 'report-file' => [ 'argument' => '--report-file=', 'description' => 'Write the report to the specified file path.', ], - 'report-report' => [ + 'report-report' => [ 'argument' => '--report-=', 'description' => 'Write the report specified in to the specified file path.', ], - 'report-width' => [ + 'report-width' => [ 'argument' => '--report-width=', 'description' => 'How many columns wide screen reports should be. Set to "auto" to use current screen width, where supported.', ], - 'basepath' => [ + 'basepath' => [ 'argument' => '--basepath=', 'description' => 'Strip a path from the front of file paths inside reports.', ], - 'blank-line-1' => ['spacer' => ''], + 'blank-line-1' => ['spacer' => ''], - 'w' => [ + 'w' => [ 'argument' => '-w', 'description' => 'Include both warnings and errors (default).', ], - 'n' => [ + 'n' => [ 'argument' => '-n', 'description' => 'Do not include warnings. Shortcut for "--warning-severity=0".', ], - 'severity' => [ + 'severity' => [ 'argument' => '--severity=', 'description' => 'The minimum severity required to display an error or warning. Defaults to 5.', ], - 'error-severity' => [ + 'error-severity' => [ 'argument' => '--error-severity=', 'description' => 'The minimum severity required to display an error. Defaults to 5.', ], - 'warning-severity' => [ + 'warning-severity' => [ 'argument' => '--warning-severity=', 'description' => 'The minimum severity required to display a warning. Defaults to 5.', ], - 'blank-line-2' => ['spacer' => ''], + 'blank-line-2' => ['spacer' => ''], - 's' => [ + 's' => [ 'argument' => '-s', 'description' => 'Show sniff error codes in all reports.', ], - 'ignore-annotations' => [ + 'ignore-annotations' => [ 'argument' => '--ignore-annotations', 'description' => 'Ignore all "phpcs:..." annotations in code comments.', ], - 'colors' => [ + 'colors' => [ 'argument' => '--colors', 'description' => 'Use colors in screen output.', ], - 'no-colors' => [ + 'no-colors' => [ 'argument' => '--no-colors', 'description' => 'Do not use colors in screen output (default).', ], - 'p' => [ + 'p' => [ 'argument' => '-p', 'description' => 'Show progress of the run.', ], - 'q' => [ + 'q' => [ 'argument' => '-q', 'description' => 'Quiet mode; disables progress and verbose output.', ], - 'm' => [ + 'm' => [ 'argument' => '-m', 'description' => 'Stop error messages from being recorded. This saves a lot of memory but stops many reports from being used.', ], - 'allow-empty-file-list' => [ - 'argument' => '--allow-empty-file-list', - 'description' => 'Suppress "No files were checked" error.', - ], ]; $options['Configuration Options'] = [ diff --git a/tests/Core/Util/Help/HelpTest.php b/tests/Core/Util/Help/HelpTest.php index 160b4f7fd4..e1dbe1c62c 100644 --- a/tests/Core/Util/Help/HelpTest.php +++ b/tests/Core/Util/Help/HelpTest.php @@ -235,7 +235,7 @@ public static function dataOptionFiltering() 'Scan targets' => 8, 'Rule Selection Options' => 7, 'Run Options' => 8, - 'Reporting Options' => 20, + 'Reporting Options' => 19, 'Configuration Options' => 8, 'Miscellaneous Options' => 5, ], @@ -247,7 +247,7 @@ public static function dataOptionFiltering() 'Scan targets' => 8, 'Rule Selection Options' => 5, 'Run Options' => 4, - 'Reporting Options' => 15, + 'Reporting Options' => 14, 'Configuration Options' => 4, 'Miscellaneous Options' => 5, ], @@ -273,7 +273,7 @@ public static function dataOptionFiltering() 'Scan targets' => 8, 'Rule Selection Options' => 7, 'Run Options' => 8, - 'Reporting Options' => 20, + 'Reporting Options' => 19, 'Configuration Options' => 8, ], ], From fbb065b489fd4770b2bf31d167a2f4fb76719cfe Mon Sep 17 00:00:00 2001 From: schlndh Date: Sat, 11 Oct 2025 19:07:24 +0200 Subject: [PATCH 4/4] use configuration option instead of CLI argument --- src/Config.php | 83 ++++++++----------- src/Runner.php | 2 +- src/Util/Help.php | 2 +- .../Runner/RunAllFilesExcludedErrorTest.php | 8 +- 4 files changed, 44 insertions(+), 51 deletions(-) diff --git a/src/Config.php b/src/Config.php index acc9d8e458..fd8eb90358 100644 --- a/src/Config.php +++ b/src/Config.php @@ -65,7 +65,6 @@ * @property string $stdinContent Content passed directly to PHPCS on STDIN. * @property string $stdinPath The path to use for content passed on STDIN. * @property bool $trackTime Whether or not to track sniff run time. - * @property bool $allowEmptyFileList Suppresses "No files were checked" error. * * @property array $extensions File extensions that should be checked, and what tokenizer is used. * E.g., array('inc' => 'PHP'); @@ -161,43 +160,42 @@ class Config * @var array */ private $settings = [ - 'files' => null, - 'standards' => null, - 'verbosity' => null, - 'interactive' => null, - 'parallel' => null, - 'cache' => null, - 'cacheFile' => null, - 'colors' => null, - 'explain' => null, - 'local' => null, - 'showSources' => null, - 'showProgress' => null, - 'quiet' => null, - 'annotations' => null, - 'tabWidth' => null, - 'encoding' => null, - 'extensions' => null, - 'sniffs' => null, - 'exclude' => null, - 'ignored' => null, - 'reportFile' => null, - 'generator' => null, - 'filter' => null, - 'bootstrap' => null, - 'reports' => null, - 'basepath' => null, - 'reportWidth' => null, - 'errorSeverity' => null, - 'warningSeverity' => null, - 'recordErrors' => null, - 'suffix' => null, - 'stdin' => null, - 'stdinContent' => null, - 'stdinPath' => null, - 'trackTime' => null, - 'unknown' => null, - 'allowEmptyFileList' => null, + 'files' => null, + 'standards' => null, + 'verbosity' => null, + 'interactive' => null, + 'parallel' => null, + 'cache' => null, + 'cacheFile' => null, + 'colors' => null, + 'explain' => null, + 'local' => null, + 'showSources' => null, + 'showProgress' => null, + 'quiet' => null, + 'annotations' => null, + 'tabWidth' => null, + 'encoding' => null, + 'extensions' => null, + 'sniffs' => null, + 'exclude' => null, + 'ignored' => null, + 'reportFile' => null, + 'generator' => null, + 'filter' => null, + 'bootstrap' => null, + 'reports' => null, + 'basepath' => null, + 'reportWidth' => null, + 'errorSeverity' => null, + 'warningSeverity' => null, + 'recordErrors' => null, + 'suffix' => null, + 'stdin' => null, + 'stdinContent' => null, + 'stdinPath' => null, + 'trackTime' => null, + 'unknown' => null, ]; /** @@ -583,7 +581,6 @@ public function restoreDefaults() $this->stdinPath = null; $this->trackTime = false; $this->unknown = []; - $this->allowEmptyFileList = false; $standard = self::getConfigData('default_standard'); if ($standard !== null) { @@ -831,14 +828,6 @@ public function processLongArgument(string $arg, int $pos) $this->annotations = false; $this->overriddenDefaults['annotations'] = true; break; - case 'allow-empty-file-list': - if (isset($this->overriddenDefaults['allowEmptyFileList']) === true) { - break; - } - - $this->allowEmptyFileList = true; - $this->overriddenDefaults['allowEmptyFileList'] = true; - break; case 'config-set': if (isset($this->cliArgs[($pos + 1)]) === false || isset($this->cliArgs[($pos + 2)]) === false diff --git a/src/Runner.php b/src/Runner.php index de15ca9163..24d1d3009a 100644 --- a/src/Runner.php +++ b/src/Runner.php @@ -351,7 +351,7 @@ private function run() } $numFiles = count($todo); - if ($numFiles === 0 && $this->config->allowEmptyFileList === false) { + if ($numFiles === 0 && Config::getConfigData('allow_empty_file_list') !== '1') { $error = 'ERROR: No files were checked.' . PHP_EOL; $error .= 'All specified files were excluded or did not match filtering rules.' . PHP_EOL . PHP_EOL; throw new DeepExitException($error, ExitCode::PROCESS_ERROR); diff --git a/src/Util/Help.php b/src/Util/Help.php index c9faf71d76..323afbe467 100644 --- a/src/Util/Help.php +++ b/src/Util/Help.php @@ -579,7 +579,7 @@ private function getAllOptions() 'config-explain' => [ 'text' => 'Default values for a selection of options can be stored in a user-specific CodeSniffer.conf configuration file.' . "\n" - . 'This applies to the following options: "default_standard", "report_format", "tab_width", "encoding", "severity", "error_severity", "warning_severity", "show_warnings", "report_width", "show_progress", "quiet", "colors", "cache", "parallel", "installed_paths", "php_version", "ignore_errors_on_exit", "ignore_warnings_on_exit", "ignore_non_auto_fixable_on_exit".', + . 'This applies to the following options: "default_standard", "report_format", "tab_width", "encoding", "severity", "error_severity", "warning_severity", "show_warnings", "report_width", "show_progress", "quiet", "colors", "cache", "parallel", "installed_paths", "php_version", "ignore_errors_on_exit", "ignore_warnings_on_exit", "ignore_non_auto_fixable_on_exit", "allow_empty_file_list".', ], 'config-show' => [ 'argument' => '--config-show', diff --git a/tests/Core/Runner/RunAllFilesExcludedErrorTest.php b/tests/Core/Runner/RunAllFilesExcludedErrorTest.php index e736a0225f..145ecf32d9 100644 --- a/tests/Core/Runner/RunAllFilesExcludedErrorTest.php +++ b/tests/Core/Runner/RunAllFilesExcludedErrorTest.php @@ -90,7 +90,9 @@ public function testPhpcsAllowEmpty($sourceDir, $extraArgs) $this->markTestSkipped('This test needs CS mode to run'); } - $extraArgs[] = '--allow-empty-file-list'; + $extraArgs[] = '--runtime-set'; + $extraArgs[] = 'allow_empty_file_list'; + $extraArgs[] = '1'; $this->setupTest($sourceDir, $extraArgs); $runner = new Runner(); @@ -119,7 +121,9 @@ public function testPhpcbfAllowEmpty($sourceDir, $extraArgs) $this->markTestSkipped('This test needs CBF mode to run'); } - $extraArgs[] = '--allow-empty-file-list'; + $extraArgs[] = '--runtime-set'; + $extraArgs[] = 'allow_empty_file_list'; + $extraArgs[] = '1'; $this->setupTest($sourceDir, $extraArgs); $runner = new Runner();