Skip to content

Commit b9bb2dc

Browse files
committed
Generic/Syntax: add support for inspecting code passed via STDIN
This commit improves the Generic.PHP.Syntax sniff to make it work when code is passed via STDIN. Before, any code passed this way would cause a false negative as the sniff was not taking into account that `$phpcsFile->getFilename()` might return `STDIN` instead of an actual file name.
1 parent 615e25a commit b9bb2dc

File tree

2 files changed

+128
-4
lines changed

2 files changed

+128
-4
lines changed

src/Standards/Generic/Sniffs/PHP/SyntaxSniff.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,9 @@ public function process(File $phpcsFile, $stackPtr)
5656
$this->phpPath = Config::getExecutablePath('php');
5757
}
5858

59-
$fileName = escapeshellarg($phpcsFile->getFilename());
60-
$cmd = Common::escapeshellcmd($this->phpPath)." -l -d display_errors=1 -d error_prepend_string='' $fileName 2>&1";
61-
$output = shell_exec($cmd);
62-
$matches = [];
59+
$cmd = $this->getPhpLintCommand($phpcsFile);
60+
$output = shell_exec($cmd);
61+
$matches = [];
6362
if (preg_match('/^.*error:(.*) in .* on line ([0-9]+)/m', trim($output), $matches) === 1) {
6463
$error = trim($matches[1]);
6564
$line = (int) $matches[2];
@@ -72,4 +71,25 @@ public function process(File $phpcsFile, $stackPtr)
7271
}//end process()
7372

7473

74+
/**
75+
* Returns the command used to lint PHP code. Uses a different command when the content is
76+
* provided via STDIN.
77+
*
78+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The File object.
79+
*
80+
* @return string The command used to lint PHP code.
81+
*/
82+
private function getPhpLintCommand(File $phpcsFile)
83+
{
84+
if ($phpcsFile->getFilename() === 'STDIN') {
85+
$content = $phpcsFile->getTokensAsString(0, $phpcsFile->numTokens);
86+
return "echo ".escapeshellarg($content)." | ".Common::escapeshellcmd($this->phpPath)." -l -d display_errors=1 -d error_prepend_string='' 2>&1";
87+
}
88+
89+
$fileName = escapeshellarg($phpcsFile->getFilename());
90+
return Common::escapeshellcmd($this->phpPath)." -l -d display_errors=1 -d error_prepend_string='' $fileName 2>&1";
91+
92+
}//end getPhpLintCommand()
93+
94+
7595
}//end class

src/Standards/Generic/Tests/PHP/SyntaxUnitTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
namespace PHP_CodeSniffer\Standards\Generic\Tests\PHP;
1212

13+
use PHP_CodeSniffer\Files\DummyFile;
14+
use PHP_CodeSniffer\Ruleset;
15+
use PHP_CodeSniffer\Tests\ConfigDouble;
1316
use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
1417

1518
/**
@@ -60,4 +63,105 @@ public function getWarningList()
6063
}//end getWarningList()
6164

6265

66+
/**
67+
* Test the sniff checks syntax when file contents are passed via STDIN.
68+
*
69+
* @param string $content The content to test.
70+
* @param int $errorCount The expected number of errors.
71+
* @param array $expectedErrors The expected errors.
72+
*
73+
* @dataProvider dataStdIn
74+
*
75+
* @return void
76+
*/
77+
public function testStdIn($content, $errorCount, $expectedErrors)
78+
{
79+
$config = new ConfigDouble();
80+
$config->standards = ['Generic'];
81+
$config->sniffs = ['Generic.PHP.Syntax'];
82+
83+
$ruleset = new Ruleset($config);
84+
85+
$file = new DummyFile($content, $ruleset, $config);
86+
$file->process();
87+
88+
$this->assertSame(
89+
$errorCount,
90+
$file->getErrorCount(),
91+
'Error count does not match expected value'
92+
);
93+
$this->assertSame(
94+
0,
95+
$file->getWarningCount(),
96+
'Warning count does not match expected value'
97+
);
98+
$this->assertSame(
99+
$expectedErrors,
100+
$file->getErrors(),
101+
'Error list does not match expected errors'
102+
);
103+
104+
}//end testStdIn()
105+
106+
107+
/**
108+
* Data provider for testStdIn().
109+
*
110+
* @return array[]
111+
*/
112+
public function dataStdIn()
113+
{
114+
// The error message changed in PHP 8+.
115+
if (PHP_VERSION_ID >= 80000) {
116+
$errorMessage = 'PHP syntax error: syntax error, unexpected token ";", expecting "]"';
117+
} else {
118+
$errorMessage = 'PHP syntax error: syntax error, unexpected \';\', expecting \']\'';
119+
}
120+
121+
return [
122+
'No syntax errors' => [
123+
'<?php $array = [1, 2, 3];',
124+
0,
125+
[],
126+
],
127+
'One syntax error' => [
128+
'<?php $array = [1, 2, 3; // Missing closing bracket.',
129+
1,
130+
[
131+
1 => [
132+
1 => [
133+
0 => [
134+
'message' => $errorMessage,
135+
'source' => 'Generic.PHP.Syntax.PHPSyntax',
136+
'listener' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\PHP\\SyntaxSniff',
137+
'severity' => 5,
138+
'fixable' => false,
139+
],
140+
],
141+
],
142+
],
143+
],
144+
'Single error reported even when there is more than syntax error in the file' => [
145+
'<?php $array = [1, 2, 3; // Missing closing bracket.
146+
$anotherArray = [4, 5, 6; // Another missing closing bracket.',
147+
1,
148+
[
149+
1 => [
150+
1 => [
151+
0 => [
152+
'message' => $errorMessage,
153+
'source' => 'Generic.PHP.Syntax.PHPSyntax',
154+
'listener' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\PHP\\SyntaxSniff',
155+
'severity' => 5,
156+
'fixable' => false,
157+
],
158+
],
159+
],
160+
],
161+
],
162+
];
163+
164+
}//end dataStdIn()
165+
166+
63167
}//end class

0 commit comments

Comments
 (0)