3
3
* Checks that strict types are declared in the PHP file.
4
4
*
5
5
* @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)
7
7
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8
8
*/
9
9
@@ -78,13 +78,58 @@ public function process(File $phpcsFile, $stackPtr)
78
78
79
79
$ tokens = $ phpcsFile ->getTokens ();
80
80
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
+
81
113
$ next = $ phpcsFile ->findNext (Tokens::$ emptyTokens , ($ stackPtr + 1 ), null , true );
82
114
83
115
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
+ }
86
132
87
- if (stripos ($ string , 'strict_types ' ) !== false ) {
88
133
$ prev = $ phpcsFile ->findPrevious (T_WHITESPACE , ($ next - 1 ), null , true );
89
134
$ after = $ phpcsFile ->findNext (T_WHITESPACE , ($ eos + 1 ), null , true );
90
135
@@ -93,7 +138,7 @@ public function process(File $phpcsFile, $stackPtr)
93
138
&& $ tokens [$ after ]['code ' ] === T_CLOSE_TAG
94
139
) {
95
140
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. ' ;
97
142
$ fix = $ phpcsFile ->addFixableError ($ error , $ prev , 'OpenTag ' );
98
143
99
144
if ($ fix === true ) {
@@ -110,7 +155,7 @@ public function process(File $phpcsFile, $stackPtr)
110
155
}//end if
111
156
112
157
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. ' ;
114
159
$ fix = $ phpcsFile ->addFixableError ($ error , $ prev , 'OpenTagSpace ' );
115
160
116
161
if ($ fix === true ) {
@@ -125,7 +170,7 @@ public function process(File $phpcsFile, $stackPtr)
125
170
}
126
171
127
172
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. ' ;
129
174
$ fix = $ phpcsFile ->addFixableError ($ error , $ after , 'CloseTag ' );
130
175
131
176
if ($ fix === true ) {
@@ -160,16 +205,22 @@ public function process(File $phpcsFile, $stackPtr)
160
205
$ after = false ;
161
206
}//end if
162
207
163
- // Check how many blank lines is before declare statement.
208
+ // Check how many blank lines there are before declare statement.
164
209
if ($ prev !== false ) {
165
210
$ linesBefore = ($ tokens [$ next ]['line ' ] - $ tokens [$ prev ]['line ' ] - 1 );
166
211
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 );
173
224
174
225
if ($ fix === true ) {
175
226
$ phpcsFile ->fixer ->beginChangeset ();
@@ -182,15 +233,22 @@ public function process(File $phpcsFile, $stackPtr)
182
233
}
183
234
}
184
235
} 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
+
185
243
// Add new blank line(s).
186
244
while ($ linesBefore < $ this ->linesBefore ) {
187
245
$ phpcsFile ->fixer ->addNewlineBefore ($ next );
188
246
++$ linesBefore ;
189
247
}
190
- }
248
+ }//end if
191
249
192
250
$ phpcsFile ->fixer ->endChangeset ();
193
- }
251
+ }//end if
194
252
}//end if
195
253
}//end if
196
254
@@ -202,12 +260,18 @@ public function process(File $phpcsFile, $stackPtr)
202
260
203
261
$ linesAfter = ($ tokens [$ after ]['line ' ] - $ tokens [$ eos ]['line ' ] - 1 );
204
262
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 );
211
275
212
276
if ($ fix === true ) {
213
277
$ phpcsFile ->fixer ->beginChangeset ();
@@ -219,50 +283,68 @@ public function process(File $phpcsFile, $stackPtr)
219
283
}
220
284
}
221
285
} 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.
222
294
while ($ linesAfter < $ this ->linesAfter ) {
223
295
$ phpcsFile ->fixer ->addNewline ($ eos );
224
296
++$ linesAfter ;
225
297
}
226
- }
298
+ }//end if
227
299
228
300
$ phpcsFile ->fixer ->endChangeset ();
229
- }
301
+ }//end if
230
302
}//end if
231
303
}//end if
232
304
233
305
// Check if declare statement match provided format.
306
+ $ string = $ phpcsFile ->getTokensAsString ($ next , ($ eos - $ next + 1 ));
234
307
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" ' ;
236
309
$ data = [
237
310
$ this ->format ,
238
311
$ string ,
239
312
];
240
- $ fix = $ phpcsFile ->addFixableError ($ error , $ next , 'InvalidFormat ' , $ data );
241
313
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
+ }
247
322
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 );
250
328
}
251
- }
329
+ }//end if
330
+
331
+ $ this ->checkOtherDeclarations ($ phpcsFile , $ next );
252
332
253
333
return (count ($ tokens ) + 1 );
254
334
}//end if
255
335
}//end if
256
336
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 ' ;
258
340
$ fix = $ phpcsFile ->addFixableError ($ error , $ stackPtr , 'NotFound ' );
259
341
260
342
if ($ fix === true ) {
261
343
$ after = $ stackPtr ;
262
344
$ first = $ phpcsFile ->findNext (T_WHITESPACE , ($ stackPtr + 1 ), null , true );
263
345
if ($ first !== null && $ tokens [$ first ]['code ' ] === T_DOC_COMMENT_OPEN_TAG ) {
264
346
foreach ($ tokens [$ first ]['comment_tags ' ] as $ tag ) {
265
- if (in_array (strtolower ($ tokens [$ tag ]['content ' ]), $ this ->omitCommentWithTags , true )) {
347
+ if (in_array (strtolower ($ tokens [$ tag ]['content ' ]), $ this ->omitCommentWithTags , true ) === true ) {
266
348
$ after = $ tokens [$ first ]['comment_closer ' ];
267
349
break ;
268
350
}
@@ -287,4 +369,67 @@ public function process(File $phpcsFile, $stackPtr)
287
369
}//end process()
288
370
289
371
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
+
290
435
}//end class
0 commit comments