diff --git a/src/Standards/Generic/Sniffs/PHP/SyntaxSniff.php b/src/Standards/Generic/Sniffs/PHP/SyntaxSniff.php index 7297ebc134..803e3802db 100644 --- a/src/Standards/Generic/Sniffs/PHP/SyntaxSniff.php +++ b/src/Standards/Generic/Sniffs/PHP/SyntaxSniff.php @@ -56,10 +56,9 @@ public function process(File $phpcsFile, $stackPtr) $this->phpPath = Config::getExecutablePath('php'); } - $fileName = escapeshellarg($phpcsFile->getFilename()); - $cmd = Common::escapeshellcmd($this->phpPath)." -l -d display_errors=1 -d error_prepend_string='' $fileName 2>&1"; - $output = shell_exec($cmd); - $matches = []; + $cmd = $this->getPhpLintCommand($phpcsFile); + $output = shell_exec($cmd); + $matches = []; if (preg_match('/^.*error:(.*) in .* on line ([0-9]+)/m', trim($output), $matches) === 1) { $error = trim($matches[1]); $line = (int) $matches[2]; @@ -72,4 +71,34 @@ public function process(File $phpcsFile, $stackPtr) }//end process() + /** + * Returns the command used to lint PHP code. + * + * Uses a different command when the content is provided via STDIN. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The File object. + * + * @return string The command used to lint PHP code. + */ + private function getPhpLintCommand(File $phpcsFile) + { + if ($phpcsFile->getFilename() === 'STDIN') { + $content = $phpcsFile->getTokensAsString(0, $phpcsFile->numTokens); + return sprintf( + "echo %s | %s -l -d display_errors=1 -d error_prepend_string='' 2>&1", + escapeshellarg($content), + Common::escapeshellcmd($this->phpPath) + ); + } + + $fileName = escapeshellarg($phpcsFile->getFilename()); + return sprintf( + "%s -l -d display_errors=1 -d error_prepend_string='' %s 2>&1", + Common::escapeshellcmd($this->phpPath), + $fileName + ); + + }//end getPhpLintCommand() + + }//end class diff --git a/src/Standards/Generic/Tests/PHP/SyntaxUnitTest.php b/src/Standards/Generic/Tests/PHP/SyntaxUnitTest.php index 58433eb1d1..115677ae98 100644 --- a/src/Standards/Generic/Tests/PHP/SyntaxUnitTest.php +++ b/src/Standards/Generic/Tests/PHP/SyntaxUnitTest.php @@ -10,6 +10,9 @@ namespace PHP_CodeSniffer\Standards\Generic\Tests\PHP; +use PHP_CodeSniffer\Files\DummyFile; +use PHP_CodeSniffer\Ruleset; +use PHP_CodeSniffer\Tests\ConfigDouble; use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest; /** @@ -60,4 +63,108 @@ public function getWarningList() }//end getWarningList() + /** + * Test the sniff checks syntax when file contents are passed via STDIN. + * + * Note: this test doesn't run on Windows as PHPCS currently doesn't support STDIN on this OS. + * + * @param string $content The content to test. + * @param int $errorCount The expected number of errors. + * @param array $expectedErrors The expected errors. + * + * @dataProvider dataStdIn + * @requires OS ^(?!WIN).* + * + * @return void + */ + public function testStdIn($content, $errorCount, $expectedErrors) + { + $config = new ConfigDouble(); + $config->standards = ['Generic']; + $config->sniffs = ['Generic.PHP.Syntax']; + + $ruleset = new Ruleset($config); + + $file = new DummyFile($content, $ruleset, $config); + $file->process(); + + $this->assertSame( + $errorCount, + $file->getErrorCount(), + 'Error count does not match expected value' + ); + $this->assertSame( + 0, + $file->getWarningCount(), + 'Warning count does not match expected value' + ); + $this->assertSame( + $expectedErrors, + $file->getErrors(), + 'Error list does not match expected errors' + ); + + }//end testStdIn() + + + /** + * Data provider for testStdIn(). + * + * @return array[] + */ + public function dataStdIn() + { + // The error message changed in PHP 8+. + if (PHP_VERSION_ID >= 80000) { + $errorMessage = 'PHP syntax error: syntax error, unexpected token ";", expecting "]"'; + } else { + $errorMessage = 'PHP syntax error: syntax error, unexpected \';\', expecting \']\''; + } + + return [ + 'No syntax errors' => [ + ' [ + ' [ + 1 => [ + 0 => [ + 'message' => $errorMessage, + 'source' => 'Generic.PHP.Syntax.PHPSyntax', + 'listener' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\PHP\\SyntaxSniff', + 'severity' => 5, + 'fixable' => false, + ], + ], + ], + ], + ], + 'Single error reported even when there is more than one syntax error in the file' => [ + ' [ + 1 => [ + 0 => [ + 'message' => $errorMessage, + 'source' => 'Generic.PHP.Syntax.PHPSyntax', + 'listener' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\PHP\\SyntaxSniff', + 'severity' => 5, + 'fixable' => false, + ], + ], + ], + ], + ], + ]; + + }//end dataStdIn() + + }//end class