Skip to content

Commit 8330b5e

Browse files
committed
DeclareStrictTypesSniff updates
1 parent bce7c95 commit 8330b5e

12 files changed

+252
-48
lines changed

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

Lines changed: 180 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Checks that strict types are declared in the PHP file.
44
*
55
* @author Michał Bundyra <[email protected]>
6-
* @copyright 2006-2017 Squiz Pty Ltd (ABN 77 084 670 600)
6+
* @copyright 2006-2018 Squiz Pty Ltd (ABN 77 084 670 600)
77
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
88
*/
99

@@ -78,13 +78,58 @@ public function process(File $phpcsFile, $stackPtr)
7878

7979
$tokens = $phpcsFile->getTokens();
8080

81+
if ($stackPtr > 0) {
82+
$before = trim($phpcsFile->getTokensAsString(0, $stackPtr));
83+
84+
if ($before === '') {
85+
$error = 'Unexpected whitespace before PHP opening tag';
86+
$fix = $phpcsFile->addFixableError($error, 0, 'Whitespace');
87+
88+
if ($fix === true) {
89+
$phpcsFile->fixer->beginChangeset();
90+
for ($i = 0; $i < $stackPtr; ++$i) {
91+
$phpcsFile->fixer->replaceToken($i, '');
92+
}
93+
94+
$phpcsFile->fixer->endChangeset();
95+
}
96+
} else {
97+
$error = 'Missing strict type declaration as first statement in the script';
98+
$fix = $phpcsFile->addFixableError($error, 0, 'Missing');
99+
100+
if ($fix === true) {
101+
$phpcsFile->fixer->addContentBefore(
102+
0,
103+
sprintf('<?php %s ?>%s', $this->format, $phpcsFile->eolChar)
104+
);
105+
}
106+
}//end if
107+
108+
$this->checkOtherDeclarations($phpcsFile);
109+
110+
return $phpcsFile->numTokens;
111+
}//end if
112+
81113
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
82114

83115
if ($tokens[$next]['code'] === T_DECLARE) {
84-
$eos = $phpcsFile->findEndOfStatement($next);
85-
$string = $phpcsFile->getTokensAsString($next, ($eos - $next + 1));
116+
$string = $phpcsFile->findNext(
117+
T_STRING,
118+
($tokens[$next]['parenthesis_opener'] + 1),
119+
$tokens[$next]['parenthesis_closer']
120+
);
121+
122+
if ($string !== false
123+
&& stripos($tokens[$string]['content'], 'strict_types') !== false
124+
) {
125+
if (isset($tokens[$next]['scope_closer']) === true
126+
&& $next === $tokens[$next]['scope_condition']
127+
) {
128+
$eos = $tokens[$next]['scope_closer'];
129+
} else {
130+
$eos = $phpcsFile->findEndOfStatement($next);
131+
}
86132

87-
if (stripos($string, 'strict_types') !== false) {
88133
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($next - 1), null, true);
89134
$after = $phpcsFile->findNext(T_WHITESPACE, ($eos + 1), null, true);
90135

@@ -93,7 +138,7 @@ public function process(File $phpcsFile, $stackPtr)
93138
&& $tokens[$after]['code'] === T_CLOSE_TAG
94139
) {
95140
if ($tokens[$prev]['line'] !== $tokens[$next]['line']) {
96-
$error = 'PHP open tag must be in the same line as declaration.';
141+
$error = 'PHP open tag must be on the same line as strict type declaration.';
97142
$fix = $phpcsFile->addFixableError($error, $prev, 'OpenTag');
98143

99144
if ($fix === true) {
@@ -110,7 +155,7 @@ public function process(File $phpcsFile, $stackPtr)
110155
}//end if
111156

112157
if ($prev !== false && ($prev < ($next - 1) || $tokens[$prev]['content'] !== '<?php ')) {
113-
$error = 'Expected single space after PHP open tag and before declaration.';
158+
$error = 'Expected single space after PHP open tag and before strict type declaration.';
114159
$fix = $phpcsFile->addFixableError($error, $prev, 'OpenTagSpace');
115160

116161
if ($fix === true) {
@@ -125,7 +170,7 @@ public function process(File $phpcsFile, $stackPtr)
125170
}
126171

127172
if ($tokens[$after]['line'] !== $tokens[$eos]['line']) {
128-
$error = 'PHP close tag must be in the same line as declaration.';
173+
$error = 'PHP close tag must be on the same line as strict type declaration.';
129174
$fix = $phpcsFile->addFixableError($error, $after, 'CloseTag');
130175

131176
if ($fix === true) {
@@ -160,16 +205,22 @@ public function process(File $phpcsFile, $stackPtr)
160205
$after = false;
161206
}//end if
162207

163-
// Check how many blank lines is before declare statement.
208+
// Check how many blank lines there are before declare statement.
164209
if ($prev !== false) {
165210
$linesBefore = ($tokens[$next]['line'] - $tokens[$prev]['line'] - 1);
166211
if ($linesBefore !== $this->linesBefore) {
167-
$error = 'Invalid number of blank lines before declare statement; expected %d, but found %d';
168-
$data = [
169-
$this->linesBefore,
170-
$linesBefore,
171-
];
172-
$fix = $phpcsFile->addFixableError($error, $next, 'LinesBefore', $data);
212+
if ($linesBefore < 0) {
213+
$error = 'Strict type declaration must be in new line';
214+
$data = [];
215+
} else {
216+
$error = 'Invalid number of blank lines before declare statement; expected %d, but found %d';
217+
$data = [
218+
$this->linesBefore,
219+
$linesBefore,
220+
];
221+
}
222+
223+
$fix = $phpcsFile->addFixableError($error, $next, 'LinesBefore', $data);
173224

174225
if ($fix === true) {
175226
$phpcsFile->fixer->beginChangeset();
@@ -182,15 +233,22 @@ public function process(File $phpcsFile, $stackPtr)
182233
}
183234
}
184235
} else {
236+
// Clear whitespaces between prev and next, but no new lines.
237+
if ($linesBefore < 0) {
238+
for ($i = ($prev + 1); $i < $next; ++$i) {
239+
$phpcsFile->fixer->replaceToken($i, '');
240+
}
241+
}
242+
185243
// Add new blank line(s).
186244
while ($linesBefore < $this->linesBefore) {
187245
$phpcsFile->fixer->addNewlineBefore($next);
188246
++$linesBefore;
189247
}
190-
}
248+
}//end if
191249

192250
$phpcsFile->fixer->endChangeset();
193-
}
251+
}//end if
194252
}//end if
195253
}//end if
196254

@@ -202,12 +260,18 @@ public function process(File $phpcsFile, $stackPtr)
202260

203261
$linesAfter = ($tokens[$after]['line'] - $tokens[$eos]['line'] - 1);
204262
if ($linesAfter !== $this->linesAfter) {
205-
$error = 'Invalid number of blank lines after declare statement; expected %d, but found %d';
206-
$data = [
207-
$this->linesAfter,
208-
$linesAfter,
209-
];
210-
$fix = $phpcsFile->addFixableError($error, $eos, 'LinesAfter', $data);
263+
if ($linesAfter < 0) {
264+
$error = 'Strict type declaration must be the only statement in the line';
265+
$data = [];
266+
} else {
267+
$error = 'Invalid number of blank lines after declare statement; expected %d, but found %d';
268+
$data = [
269+
$this->linesAfter,
270+
$linesAfter,
271+
];
272+
}
273+
274+
$fix = $phpcsFile->addFixableError($error, $eos, 'LinesAfter', $data);
211275

212276
if ($fix === true) {
213277
$phpcsFile->fixer->beginChangeset();
@@ -219,42 +283,60 @@ public function process(File $phpcsFile, $stackPtr)
219283
}
220284
}
221285
} else {
286+
// Remove whitespaces between EOS and after token.
287+
if ($linesAfter < 0) {
288+
for ($i = ($eos + 1); $i < $after; ++$i) {
289+
$phpcsFile->fixer->replaceToken($i, '');
290+
}
291+
}
292+
293+
// Add new lines after the statement.
222294
while ($linesAfter < $this->linesAfter) {
223295
$phpcsFile->fixer->addNewline($eos);
224296
++$linesAfter;
225297
}
226-
}
298+
}//end if
227299

228300
$phpcsFile->fixer->endChangeset();
229-
}
301+
}//end if
230302
}//end if
231303
}//end if
232304

233305
// Check if declare statement match provided format.
306+
$string = $phpcsFile->getTokensAsString($next, ($eos - $next + 1));
234307
if ($string !== $this->format) {
235-
$error = 'Invalid format of declaration; expected "%s", but found "%s"';
308+
$error = 'Invalid format of strict type declaration; expected "%s", but found "%s"';
236309
$data = [
237310
$this->format,
238311
$string,
239312
];
240-
$fix = $phpcsFile->addFixableError($error, $next, 'InvalidFormat', $data);
241313

242-
if ($fix === true) {
243-
$phpcsFile->fixer->beginChangeset();
244-
for ($i = $next; $i < $eos; ++$i) {
245-
$phpcsFile->fixer->replaceToken($i, '');
246-
}
314+
if ($this->normalize($string) === $this->normalize($this->format)) {
315+
$fix = $phpcsFile->addFixableError($error, $next, 'InvalidFormat', $data);
316+
317+
if ($fix === true) {
318+
$phpcsFile->fixer->beginChangeset();
319+
for ($i = $next; $i < $eos; ++$i) {
320+
$phpcsFile->fixer->replaceToken($i, '');
321+
}
247322

248-
$phpcsFile->fixer->replaceToken($eos, $this->format);
249-
$phpcsFile->fixer->endChangeset();
323+
$phpcsFile->fixer->replaceToken($eos, $this->format);
324+
$phpcsFile->fixer->endChangeset();
325+
}
326+
} else {
327+
$phpcsFile->addError($error, $next, 'InvalidFormatNotFixable', $data);
250328
}
251-
}
329+
}//end if
330+
331+
$this->checkOtherDeclarations($phpcsFile, $next);
252332

253333
return (count($tokens) + 1);
254334
}//end if
255335
}//end if
256336

257-
$error = 'Missing declaration of strict types at the beginning of the file';
337+
$this->checkOtherDeclarations($phpcsFile, $next);
338+
339+
$error = 'Missing strict type declaration at the beginning of the file';
258340
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotFound');
259341

260342
if ($fix === true) {
@@ -287,4 +369,67 @@ public function process(File $phpcsFile, $stackPtr)
287369
}//end process()
288370

289371

372+
/**
373+
* Normalize given string by removing all white characters
374+
* and changed to lower case.
375+
*
376+
* @param string $string String to be normalized.
377+
*
378+
* @return string
379+
*/
380+
private function normalize($string)
381+
{
382+
return strtolower(preg_replace('/\s/', '', $string));
383+
384+
}//end normalize()
385+
386+
387+
/**
388+
* Process other strict_type declaration in the file and remove them.
389+
* The declaration has to be the very first statement in the script.
390+
*
391+
* @param File $phpcsFile The file being scanned.
392+
* @param int $declare The position of the first declaration.
393+
*
394+
* @return void
395+
*/
396+
private function checkOtherDeclarations(File $phpcsFile, $declare=0)
397+
{
398+
$tokens = $phpcsFile->getTokens();
399+
400+
while (($declare = $phpcsFile->findNext(T_DECLARE, ($declare + 1))) !== false) {
401+
$string = $phpcsFile->findNext(
402+
T_STRING,
403+
($tokens[$declare]['parenthesis_opener'] + 1),
404+
$tokens[$declare]['parenthesis_closer']
405+
);
406+
407+
if ($string !== false
408+
&& stripos($tokens[$string]['content'], 'strict_types') !== false
409+
) {
410+
$error = 'Strict type declaration must be the very first statement in the script';
411+
$fix = $phpcsFile->addFixableError($error, $declare, 'NotFirstStatement');
412+
413+
if ($fix === true) {
414+
$end = $phpcsFile->findNext(
415+
(Tokens::$emptyTokens + [T_SEMICOLON]),
416+
($tokens[$declare]['parenthesis_closer'] + 1),
417+
null,
418+
true
419+
);
420+
421+
if ($end === false) {
422+
$end = $phpcsFile->numTokens;
423+
}
424+
425+
for ($i = $declare; $i < $end; ++$i) {
426+
$phpcsFile->fixer->replaceToken($i, '');
427+
}
428+
}
429+
}//end if
430+
}//end while
431+
432+
}//end checkOtherDeclarations()
433+
434+
290435
}//end class
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
declare(ticks=1) {}
3+
4+
declare(strict_types=1);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
declare(ticks=1) {}
6+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<html>
2+
<head>
3+
<title>
4+
<?php
5+
declare(strict_types=1);
6+
echo 'Title';
7+
?>
8+
</title>
9+
</head>
10+
11+
<?php echo $content; ?>
12+
</html>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php declare(strict_types=1); ?>
2+
<html>
3+
<head>
4+
<title>
5+
<?php
6+
echo 'Title';
7+
?>
8+
</title>
9+
</head>
10+
11+
<?php echo $content; ?>
12+
</html>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
// Comment
3+
declare(strict_types=1); ?>
4+
<html>
5+
<?php echo $content; ?>
6+
</html>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
// Comment
3+
4+
declare(strict_types=1);
5+
?>
6+
<html>
7+
<?php echo $content; ?>
8+
</html>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
declare(strict_types=1) /* comment */;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
declare(strict_types=1) /* comment */;
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
phpcs:set Generic.PHP.DeclareStrictTypes linesBefore 0
2-
phpcs:set Generic.PHP.DeclareStrictTypes linesAfter 2
3-
phpcs:set Generic.PHP.DeclareStrictTypes format declare(strict_types = 1);
4-
<?php
5-
declare(strict_types=1);
6-
echo 'hey';
1+
<?php /* Must be first. */ declare(strict_types=1); /* Comment after. */ ?>

0 commit comments

Comments
 (0)