From 58d0e56e7ca2c2bb806314711059f0fb674063e8 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 28 Apr 2026 16:43:30 +0200 Subject: [PATCH] Config: --parallel=auto detects the number of CPU cores --- src/Config.php | 45 ++++++++++++- src/Util/Help.php | 1 + tests/Core/Config/ParallelTest.php | 104 +++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 tests/Core/Config/ParallelTest.php diff --git a/src/Config.php b/src/Config.php index 6407f0b39f..b6554767ff 100644 --- a/src/Config.php +++ b/src/Config.php @@ -653,6 +653,10 @@ public function restoreDefaults() $parallel = self::getConfigData('parallel'); if ($parallel !== null) { + if ($parallel === 'auto') { + $parallel = self::detectNumberOfProcessors(); + } + $this->parallel = max((int) $parallel, 1); } } @@ -1185,7 +1189,12 @@ public function processLongArgument(string $arg, int $pos) break; } - $this->parallel = max((int) substr($arg, 9), 1); + $value = substr($arg, 9); + if ($value === 'auto') { + $value = self::detectNumberOfProcessors(); + } + + $this->parallel = max((int) $value, 1); $this->overriddenDefaults['parallel'] = true; } elseif (substr($arg, 0, 9) === 'severity=') { $this->errorSeverity = (int) substr($arg, 9); @@ -1721,6 +1730,40 @@ public static function getAllConfigData() } + /** + * Detect the number of CPU cores available. + * + * @return int + */ + private static function detectNumberOfProcessors() + { + if (PHP_OS_FAMILY === 'Windows') { + $count = getenv('NUMBER_OF_PROCESSORS'); + if (is_string($count) === true && preg_match('`\d+`', $count, $matches) === 1) { + return max((int) $matches[0], 1); + } + + return 1; + } + + if (function_exists('shell_exec') === true) { + // Linux, macOS with coreutils, and most modern Unix-like systems. + $count = shell_exec('nproc 2>/dev/null'); + if (is_string($count) === true && preg_match('`\d+`', $count, $matches) === 1) { + return max((int) $matches[0], 1); + } + + // MacOS / BSD fallback. + $count = shell_exec('sysctl -n hw.ncpu 2>/dev/null'); + if (is_string($count) === true && preg_match('`\d+`', $count, $matches) === 1) { + return max((int) $matches[0], 1); + } + } + + return 1; + } + + /** * Prepares the gathered config data for display. * diff --git a/src/Util/Help.php b/src/Util/Help.php index c9faf71d76..7cd2aabb3b 100644 --- a/src/Util/Help.php +++ b/src/Util/Help.php @@ -475,6 +475,7 @@ private function getAllOptions() 'parallel' => [ 'argument' => '--parallel=', 'description' => 'The number of files to be checked simultaneously. Defaults to 1 (no parallel processing).' . "\n" + . 'Set to "auto" to use a number of processes equal to the number of detected CPU cores.' . "\n" . 'If enabled, this option only takes effect if the PHP PCNTL (Process Control) extension is available.', ], 'suffix' => [ diff --git a/tests/Core/Config/ParallelTest.php b/tests/Core/Config/ParallelTest.php new file mode 100644 index 0000000000..11807215ec --- /dev/null +++ b/tests/Core/Config/ParallelTest.php @@ -0,0 +1,104 @@ +assertSame(1, $config->parallel); + } + + + /** + * Test that parallel can be set from a CLI argument. + * + * @return void + */ + public function testParallelCanBeSetFromCLI() + { + $_SERVER['argv'] = [ + 'phpcs', + '--standard=PSR1', + '--parallel=8', + ]; + + $config = new Config(); + $this->assertSame(8, $config->parallel); + } + + + /** + * Test that parallel can be set from a CodeSniffer.conf file. + * + * @return void + */ + public function testParallelCanBeSetFromConfFile() + { + $this->setStaticConfigProperty('configData', ['parallel' => '4']); + + $config = new Config(['--standard=PSR1']); + $this->assertSame(4, $config->parallel); + } + + + /** + * Test that "auto" passed on the CLI resolves to a non-0 positive integer. + * + * @return void + */ + public function testParallelInputHandlingForAutoFromCLI() + { + $_SERVER['argv'] = [ + 'phpcs', + '--standard=PSR1', + '--parallel=auto', + ]; + + $config = new Config(); + + // Can't test the exact value as "auto" will resolve differently depending on the machine running the tests. + $this->assertIsInt($config->parallel); + $this->assertGreaterThan(0, $config->parallel); + } + + + /** + * Test that "auto" set in the CodeSniffer.conf file resolves to a non-0 positive integer. + * + * @return void + */ + public function testParallelInputHandlingForAutoFromConfFile() + { + $this->setStaticConfigProperty('configData', ['parallel' => 'auto']); + + $config = new Config(['--standard=PSR1']); + + // Can't test the exact value as "auto" will resolve differently depending on the machine running the tests. + $this->assertIsInt($config->parallel); + $this->assertGreaterThan(0, $config->parallel); + } +}