Skip to content

Commit 90456d4

Browse files
committed
Better Ternary sniff.
1 parent 73f0b59 commit 90456d4

File tree

1 file changed

+154
-21
lines changed

1 file changed

+154
-21
lines changed

PhpCollective/Sniffs/WhiteSpace/TernarySpacingSniff.php

Lines changed: 154 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,56 +8,189 @@
88
namespace PhpCollective\Sniffs\WhiteSpace;
99

1010
use PHP_CodeSniffer\Files\File;
11-
use PhpCollective\Sniffs\AbstractSniffs\AbstractSniff;
11+
use PHP_CodeSniffer\Sniffs\Sniff;
12+
use PHP_CodeSniffer\Util\Tokens;
1213

1314
/**
14-
* Verifies that short ternary operators have valid spacing surrounding them.
15+
* Asserts single space between ternary operator parts (? and :) and surroundings.
16+
* Also asserts no whitespace between short ternary operator (?:), which was introduced in PHP 5.3.
1517
*
1618
* @author Mark Scherer
1719
* @license MIT
1820
*/
19-
class TernarySpacingSniff extends AbstractSniff
21+
class TernarySpacingSniff implements Sniff
2022
{
23+
/**
24+
* @inheritDoc
25+
*/
26+
public function register(): array
27+
{
28+
return [T_INLINE_THEN];
29+
}
30+
2131
/**
22-
* @var array<string>
32+
* @inheritDoc
2333
*/
24-
public array $supportedTokenizers = [
25-
'PHP',
26-
];
34+
public function process(File $phpcsFile, $stackPtr)
35+
{
36+
$this->assertSpaceBefore($phpcsFile, $stackPtr);
37+
$this->checkAfter($phpcsFile, $stackPtr);
38+
}
2739

2840
/**
29-
* @inheritDoc
41+
* @param \PHP_CodeSniffer\Files\File $phpcsFile
42+
* @param int $stackPtr
43+
*
44+
* @return void
3045
*/
31-
public function register(): array
46+
protected function assertSpaceBefore(File $phpcsFile, int $stackPtr): void
3247
{
33-
return [
34-
T_INLINE_THEN,
35-
];
48+
$previous = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true);
49+
if (!$previous) {
50+
return;
51+
}
52+
if ($stackPtr - $previous > 1) {
53+
$this->assertSingleSpaceBeforeIfNotMultiline($phpcsFile, $stackPtr, $previous);
54+
55+
return;
56+
}
57+
58+
$tokens = $phpcsFile->getTokens();
59+
if ($tokens[$previous]['code'] === 'PHPCS_T_INLINE_THEN') {
60+
return;
61+
}
62+
63+
$content = $tokens[$stackPtr]['content'];
64+
$error = 'There must be a single space before ternary operator part `' . $content . '`';
65+
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeInlineThen');
66+
if ($fix) {
67+
$phpcsFile->fixer->addContentBefore($stackPtr, ' ');
68+
}
3669
}
3770

3871
/**
39-
* @inheritDoc
72+
* @param \PHP_CodeSniffer\Files\File $phpcsFile
73+
* @param int $stackPtr
74+
* @param int $previous
75+
*
76+
* @return void
4077
*/
41-
public function process(File $phpcsFile, $stackPtr): void
78+
protected function assertSingleSpaceBeforeIfNotMultiline(File $phpcsFile, int $stackPtr, int $previous): void
4279
{
4380
$tokens = $phpcsFile->getTokens();
4481

45-
$nextIndex = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true);
46-
if (!$nextIndex || $nextIndex - 1 === $stackPtr || $tokens[$nextIndex]['code'] !== T_INLINE_ELSE) {
82+
if ($tokens[$stackPtr]['line'] !== $tokens[$previous]['line']) {
4783
return;
4884
}
85+
if ($tokens[$stackPtr - 1]['content'] === ' ') {
86+
return;
87+
}
88+
89+
$error = 'There must be a single space instead of `' . $tokens[$stackPtr - 1]['content'] . '`';
90+
$fix = $phpcsFile->addFixableError($error, $stackPtr - 1, 'InvalidSpaceBefore');
91+
if ($fix) {
92+
$phpcsFile->fixer->replaceToken($stackPtr - 1, ' ');
93+
}
94+
}
95+
96+
/**
97+
* @param \PHP_CodeSniffer\Files\File $phpcsFile
98+
* @param int $thenIndex
99+
*
100+
* @return void
101+
*/
102+
protected function checkAfter(File $phpcsFile, int $thenIndex): void
103+
{
104+
$elseIndex = $phpcsFile->findNext(T_INLINE_ELSE, $thenIndex + 1);
105+
if (!$elseIndex) {
106+
return;
107+
}
108+
109+
// Skip for short ternary
110+
$prevIndex = $phpcsFile->findPrevious(Tokens::$emptyTokens, $elseIndex - 1, null, true);
111+
if ($prevIndex === $thenIndex) {
112+
$this->assertNoSpaceBetween($phpcsFile, $thenIndex, $elseIndex);
113+
} else {
114+
$this->assertSpaceAfter($phpcsFile, $thenIndex);
115+
$this->assertSpaceBefore($phpcsFile, $elseIndex);
116+
}
117+
118+
$this->assertSpaceAfter($phpcsFile, $elseIndex);
119+
}
49120

50-
if ($tokens[$nextIndex - 1]['code'] !== T_WHITESPACE) {
121+
/**
122+
* @param \PHP_CodeSniffer\Files\File $phpcsFile
123+
* @param int $thenIndex
124+
* @param int $elseIndex
125+
*
126+
* @return void
127+
*/
128+
protected function assertNoSpaceBetween(File $phpcsFile, int $thenIndex, int $elseIndex): void
129+
{
130+
if ($elseIndex - $thenIndex === 1) {
51131
return;
52132
}
53133

54-
$fix = $phpcsFile->addFixableError('Additional whitespace found between short ternary', $stackPtr, 'InvalidWhitespace');
134+
$error = 'There must be no space between ? and : for short ternary';
135+
$fix = $phpcsFile->addFixableError($error, $thenIndex, 'SpaceInlineElse');
55136
if (!$fix) {
56137
return;
57138
}
58139

59-
$phpcsFile->fixer->beginChangeset();
60-
$phpcsFile->fixer->replaceToken($nextIndex - 1, '');
61-
$phpcsFile->fixer->endChangeset();
140+
for ($i = $thenIndex + 1; $i < $elseIndex; ++$i) {
141+
$phpcsFile->fixer->replaceToken($i, '');
142+
}
143+
}
144+
145+
/**
146+
* @param \PHP_CodeSniffer\Files\File $phpcsFile
147+
* @param int $stackPtr
148+
*
149+
* @return void
150+
*/
151+
protected function assertSpaceAfter(File $phpcsFile, int $stackPtr): void
152+
{
153+
$nextIndex = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true);
154+
if (!$nextIndex) {
155+
return;
156+
}
157+
if ($nextIndex - $stackPtr > 1) {
158+
$this->assertSingleSpaceAfterIfNotMultiline($phpcsFile, $stackPtr, $nextIndex);
159+
160+
return;
161+
}
162+
163+
$tokens = $phpcsFile->getTokens();
164+
$content = $tokens[$stackPtr]['content'];
165+
$error = 'There must be a single space after ternary operator part `' . $content . '`';
166+
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterInlineThen');
167+
if ($fix) {
168+
$phpcsFile->fixer->addContent($stackPtr, ' ');
169+
}
170+
}
171+
172+
/**
173+
* @param \PHP_CodeSniffer\Files\File $phpcsFile
174+
* @param int $stackPtr
175+
* @param int $next
176+
*
177+
* @return void
178+
*/
179+
protected function assertSingleSpaceAfterIfNotMultiline(File $phpcsFile, int $stackPtr, int $next): void
180+
{
181+
$tokens = $phpcsFile->getTokens();
182+
183+
if ($tokens[$stackPtr]['line'] !== $tokens[$next]['line']) {
184+
return;
185+
}
186+
if ($tokens[$stackPtr + 1]['content'] === ' ') {
187+
return;
188+
}
189+
190+
$error = 'There must be a single space instead of `' . $tokens[$stackPtr + 1]['content'] . '`';
191+
$fix = $phpcsFile->addFixableError($error, $stackPtr + 1, 'InvalidSpaceAfter');
192+
if ($fix) {
193+
$phpcsFile->fixer->replaceToken($stackPtr + 1, ' ');
194+
}
62195
}
63196
}

0 commit comments

Comments
 (0)