1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* PHPCompatibility, an external standard for PHP_CodeSniffer. |
4
|
|
|
* |
5
|
|
|
* @package PHPCompatibility |
6
|
|
|
* @copyright 2012-2019 PHPCompatibility Contributors |
7
|
|
|
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3 |
8
|
|
|
* @link https://github.com/PHPCompatibility/PHPCompatibility |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace PHPCompatibility\Sniffs\InitialValue; |
12
|
|
|
|
13
|
|
|
use PHPCompatibility\Sniff; |
14
|
|
|
use PHPCompatibility\PHPCSHelper; |
15
|
|
|
use PHP_CodeSniffer_File as File; |
16
|
|
|
use PHP_CodeSniffer_Tokens as Tokens; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Detect constant scalar expressions being used to set an initial value. |
20
|
|
|
* |
21
|
|
|
* Since PHP 5.6, it is now possible to provide a scalar expression involving |
22
|
|
|
* numeric and string literals and/or constants in contexts where PHP previously |
23
|
|
|
* expected a static value, such as constant and property declarations and |
24
|
|
|
* default values for function parameters. |
25
|
|
|
* |
26
|
|
|
* PHP version 5.6 |
27
|
|
|
* |
28
|
|
|
* @link https://www.php.net/manual/en/migration56.new-features.php#migration56.new-features.const-scalar-exprs |
29
|
|
|
* @link https://wiki.php.net/rfc/const_scalar_exprs |
30
|
|
|
* |
31
|
|
|
* @since 8.2.0 |
32
|
|
|
*/ |
33
|
|
|
class NewConstantScalarExpressionsSniff extends Sniff |
34
|
|
|
{ |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Error message. |
38
|
|
|
* |
39
|
|
|
* @since 8.2.0 |
40
|
|
|
* |
41
|
|
|
* @var string |
42
|
|
|
*/ |
43
|
|
|
const ERROR_PHRASE = 'Constant scalar expressions are not allowed %s in PHP 5.5 or earlier.'; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Partial error phrases to be used in combination with the error message constant. |
47
|
|
|
* |
48
|
|
|
* @since 8.2.0 |
49
|
|
|
* |
50
|
|
|
* @var array |
51
|
|
|
*/ |
52
|
|
|
protected $errorPhrases = array( |
53
|
|
|
'const' => 'when defining constants using the const keyword', |
54
|
|
|
'property' => 'in property declarations', |
55
|
|
|
'staticvar' => 'in static variable declarations', |
56
|
|
|
'default' => 'in default function arguments', |
57
|
|
|
); |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Tokens which were allowed to be used in these declarations prior to PHP 5.6. |
61
|
|
|
* |
62
|
|
|
* This list will be enriched in the setProperties() method. |
63
|
|
|
* |
64
|
|
|
* @since 8.2.0 |
65
|
|
|
* |
66
|
|
|
* @var array |
67
|
|
|
*/ |
68
|
|
|
protected $safeOperands = array( |
69
|
|
|
\T_LNUMBER => \T_LNUMBER, |
70
|
|
|
\T_DNUMBER => \T_DNUMBER, |
71
|
|
|
\T_CONSTANT_ENCAPSED_STRING => \T_CONSTANT_ENCAPSED_STRING, |
72
|
|
|
\T_TRUE => \T_TRUE, |
73
|
|
|
\T_FALSE => \T_FALSE, |
74
|
|
|
\T_NULL => \T_NULL, |
75
|
|
|
|
76
|
|
|
\T_LINE => \T_LINE, |
77
|
|
|
\T_FILE => \T_FILE, |
78
|
|
|
\T_DIR => \T_DIR, |
79
|
|
|
\T_FUNC_C => \T_FUNC_C, |
80
|
|
|
\T_CLASS_C => \T_CLASS_C, |
81
|
|
|
\T_TRAIT_C => \T_TRAIT_C, |
82
|
|
|
\T_METHOD_C => \T_METHOD_C, |
83
|
|
|
\T_NS_C => \T_NS_C, |
84
|
|
|
|
85
|
|
|
// Special cases: |
86
|
|
|
\T_NS_SEPARATOR => \T_NS_SEPARATOR, |
87
|
|
|
/* |
88
|
|
|
* This can be neigh anything, but for any usage except constants, |
89
|
|
|
* the T_STRING will be combined with non-allowed tokens, so we should be good. |
90
|
|
|
*/ |
91
|
|
|
\T_STRING => \T_STRING, |
92
|
|
|
); |
93
|
|
|
|
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Returns an array of tokens this test wants to listen for. |
97
|
|
|
* |
98
|
|
|
* @since 8.2.0 |
99
|
|
|
* |
100
|
|
|
* @return array |
101
|
|
|
*/ |
102
|
|
|
public function register() |
103
|
|
|
{ |
104
|
|
|
// Set the properties up only once. |
105
|
|
|
$this->setProperties(); |
106
|
|
|
|
107
|
|
|
return array( |
108
|
|
|
\T_CONST, |
109
|
|
|
\T_VARIABLE, |
110
|
|
|
\T_FUNCTION, |
111
|
|
|
\T_CLOSURE, |
112
|
|
|
\T_STATIC, |
113
|
|
|
); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Make some adjustments to the $safeOperands property. |
119
|
|
|
* |
120
|
|
|
* @since 8.2.0 |
121
|
|
|
* |
122
|
|
|
* @return void |
123
|
|
|
*/ |
124
|
|
|
public function setProperties() |
125
|
|
|
{ |
126
|
|
|
$this->safeOperands += Tokens::$heredocTokens; |
127
|
|
|
$this->safeOperands += Tokens::$emptyTokens; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Do a version check to determine if this sniff needs to run at all. |
133
|
|
|
* |
134
|
|
|
* @since 8.2.0 |
135
|
|
|
* |
136
|
|
|
* @return bool |
137
|
|
|
*/ |
138
|
|
|
protected function bowOutEarly() |
139
|
|
|
{ |
140
|
|
|
return ($this->supportsBelow('5.5') !== true); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Processes this test, when one of its tokens is encountered. |
146
|
|
|
* |
147
|
|
|
* @since 8.2.0 |
148
|
|
|
* |
149
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. |
150
|
|
|
* @param int $stackPtr The position of the current token in the |
151
|
|
|
* stack passed in $tokens. |
152
|
|
|
* |
153
|
|
|
* @return void|int Null or integer stack pointer to skip forward. |
154
|
|
|
*/ |
155
|
|
|
public function process(File $phpcsFile, $stackPtr) |
156
|
|
|
{ |
157
|
|
|
if ($this->bowOutEarly() === true) { |
158
|
|
|
return; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
$tokens = $phpcsFile->getTokens(); |
162
|
|
|
|
163
|
|
|
switch ($tokens[$stackPtr]['type']) { |
164
|
|
|
case 'T_FUNCTION': |
165
|
|
|
case 'T_CLOSURE': |
166
|
|
|
$params = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr); |
167
|
|
|
if (empty($params)) { |
168
|
|
|
// No parameters. |
169
|
|
|
return; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
$funcToken = $tokens[$stackPtr]; |
173
|
|
|
|
174
|
|
|
if (isset($funcToken['parenthesis_owner'], $funcToken['parenthesis_opener'], $funcToken['parenthesis_closer']) === false |
175
|
|
|
|| $funcToken['parenthesis_owner'] !== $stackPtr |
176
|
|
|
|| isset($tokens[$funcToken['parenthesis_opener']], $tokens[$funcToken['parenthesis_closer']]) === false |
177
|
|
|
) { |
178
|
|
|
// Hmm.. something is going wrong as these should all be available & valid. |
179
|
|
|
return; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
$opener = $funcToken['parenthesis_opener']; |
183
|
|
|
$closer = $funcToken['parenthesis_closer']; |
184
|
|
|
|
185
|
|
|
// Which nesting level is the one we are interested in ? |
186
|
|
|
$nestedParenthesisCount = 1; |
187
|
|
View Code Duplication |
if (isset($tokens[$opener]['nested_parenthesis'])) { |
|
|
|
|
188
|
|
|
$nestedParenthesisCount += \count($tokens[$opener]['nested_parenthesis']); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
foreach ($params as $param) { |
192
|
|
|
if (isset($param['default']) === false) { |
193
|
|
|
continue; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
$end = $param['token']; |
197
|
|
|
while (($end = $phpcsFile->findNext(array(\T_COMMA, \T_CLOSE_PARENTHESIS), ($end + 1), ($closer + 1))) !== false) { |
198
|
|
|
$maybeSkipTo = $this->isRealEndOfDeclaration($tokens, $end, $nestedParenthesisCount); |
199
|
|
|
if ($maybeSkipTo !== true) { |
200
|
|
|
$end = $maybeSkipTo; |
201
|
|
|
continue; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
// Ignore closing parenthesis/bracket if not 'ours'. |
205
|
|
|
if ($tokens[$end]['code'] === \T_CLOSE_PARENTHESIS && $end !== $closer) { |
206
|
|
|
continue; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
// Ok, we've found the end of the param default value declaration. |
210
|
|
|
break; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
if ($this->isValidAssignment($phpcsFile, $param['token'], $end) === false) { |
214
|
|
|
$this->throwError($phpcsFile, $param['token'], 'default', $param['content']); |
215
|
|
|
} |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/* |
219
|
|
|
* No need for the sniff to be triggered by the T_VARIABLEs in the function |
220
|
|
|
* definition as we've already examined them above, so let's skip over them. |
221
|
|
|
*/ |
222
|
|
|
return $closer; |
223
|
|
|
|
224
|
|
|
case 'T_VARIABLE': |
225
|
|
|
case 'T_STATIC': |
226
|
|
|
case 'T_CONST': |
227
|
|
|
$type = 'const'; |
228
|
|
|
|
229
|
|
|
// Filter out non-property declarations. |
230
|
|
|
if ($tokens[$stackPtr]['code'] === \T_VARIABLE) { |
231
|
|
|
if ($this->isClassProperty($phpcsFile, $stackPtr) === false) { |
232
|
|
|
return; |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
$type = 'property'; |
236
|
|
|
|
237
|
|
|
// Move back one token to have the same starting point as the others. |
238
|
|
|
$stackPtr = ($stackPtr - 1); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
// Filter out late static binding and class properties. |
242
|
|
|
if ($tokens[$stackPtr]['code'] === \T_STATIC) { |
243
|
|
|
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true); |
244
|
|
|
if ($next === false || $tokens[$next]['code'] !== \T_VARIABLE) { |
245
|
|
|
// Late static binding. |
246
|
|
|
return; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
if ($this->isClassProperty($phpcsFile, $next) === true) { |
250
|
|
|
// Class properties are examined based on the T_VARIABLE token. |
251
|
|
|
return; |
252
|
|
|
} |
253
|
|
|
unset($next); |
254
|
|
|
|
255
|
|
|
$type = 'staticvar'; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
$endOfStatement = $phpcsFile->findNext(array(\T_SEMICOLON, \T_CLOSE_TAG), ($stackPtr + 1)); |
259
|
|
|
if ($endOfStatement === false) { |
260
|
|
|
// No semi-colon - live coding. |
261
|
|
|
return; |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
$targetNestingLevel = 0; |
265
|
|
View Code Duplication |
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) { |
|
|
|
|
266
|
|
|
$targetNestingLevel = \count($tokens[$stackPtr]['nested_parenthesis']); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
// Examine each variable/constant in multi-declarations. |
270
|
|
|
$start = $stackPtr; |
271
|
|
|
$end = $stackPtr; |
272
|
|
|
while (($end = $phpcsFile->findNext(array(\T_COMMA, \T_SEMICOLON, \T_OPEN_SHORT_ARRAY, \T_CLOSE_TAG), ($end + 1), ($endOfStatement + 1))) !== false) { |
273
|
|
|
|
274
|
|
|
$maybeSkipTo = $this->isRealEndOfDeclaration($tokens, $end, $targetNestingLevel); |
275
|
|
|
if ($maybeSkipTo !== true) { |
276
|
|
|
$end = $maybeSkipTo; |
277
|
|
|
continue; |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
$start = $phpcsFile->findNext(Tokens::$emptyTokens, ($start + 1), $end, true); |
281
|
|
|
if ($start === false |
282
|
|
|
|| ($tokens[$stackPtr]['code'] === \T_CONST && $tokens[$start]['code'] !== \T_STRING) |
283
|
|
|
|| ($tokens[$stackPtr]['code'] !== \T_CONST && $tokens[$start]['code'] !== \T_VARIABLE) |
284
|
|
|
) { |
285
|
|
|
// Shouldn't be possible. |
286
|
|
|
continue; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
if ($this->isValidAssignment($phpcsFile, $start, $end) === false) { |
290
|
|
|
// Create the "found" snippet. |
291
|
|
|
$content = ''; |
292
|
|
|
$tokenCount = ($end - $start); |
293
|
|
|
if ($tokenCount < 20) { |
294
|
|
|
// Prevent large arrays from being added to the error message. |
295
|
|
|
$content = $phpcsFile->getTokensAsString($start, ($tokenCount + 1)); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
$this->throwError($phpcsFile, $start, $type, $content); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
$start = $end; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
// Skip to the end of the statement to prevent duplicate messages for multi-declarations. |
305
|
|
|
return $endOfStatement; |
306
|
|
|
} |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Is a value declared and is the value declared valid pre-PHP 5.6 ? |
312
|
|
|
* |
313
|
|
|
* @since 8.2.0 |
314
|
|
|
* |
315
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. |
316
|
|
|
* @param int $stackPtr The position of the current token in the |
317
|
|
|
* stack passed in $tokens. |
318
|
|
|
* @param int $end The end of the value definition. |
319
|
|
|
* This will normally be a comma or semi-colon. |
320
|
|
|
* |
321
|
|
|
* @return bool |
322
|
|
|
*/ |
323
|
|
|
protected function isValidAssignment(File $phpcsFile, $stackPtr, $end) |
324
|
|
|
{ |
325
|
|
|
$tokens = $phpcsFile->getTokens(); |
326
|
|
|
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), $end, true); |
327
|
|
View Code Duplication |
if ($next === false || $tokens[$next]['code'] !== \T_EQUAL) { |
|
|
|
|
328
|
|
|
// No value assigned. |
329
|
|
|
return true; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
return $this->isStaticValue($phpcsFile, $tokens, ($next + 1), ($end - 1)); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Is a value declared and is the value declared constant as accepted in PHP 5.5 and lower ? |
338
|
|
|
* |
339
|
|
|
* @since 8.2.0 |
340
|
|
|
* |
341
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. |
342
|
|
|
* @param array $tokens The token stack of the current file. |
343
|
|
|
* @param int $start The stackPtr from which to start examining. |
344
|
|
|
* @param int $end The end of the value definition (inclusive), |
345
|
|
|
* i.e. this token will be examined as part of |
346
|
|
|
* the snippet. |
347
|
|
|
* @param int $nestedArrays Optional. Array nesting level when examining |
348
|
|
|
* the content of an array. |
349
|
|
|
* |
350
|
|
|
* @return bool |
351
|
|
|
*/ |
352
|
|
|
protected function isStaticValue(File $phpcsFile, $tokens, $start, $end, $nestedArrays = 0) |
353
|
|
|
{ |
354
|
|
|
$nextNonSimple = $phpcsFile->findNext($this->safeOperands, $start, ($end + 1), true); |
355
|
|
|
if ($nextNonSimple === false) { |
356
|
|
|
return true; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
/* |
360
|
|
|
* OK, so we have at least one token which needs extra examination. |
361
|
|
|
*/ |
362
|
|
|
switch ($tokens[$nextNonSimple]['code']) { |
363
|
|
|
case \T_MINUS: |
364
|
|
|
case \T_PLUS: |
365
|
|
|
if ($this->isNumber($phpcsFile, $start, $end, true) !== false) { |
366
|
|
|
// Int or float with sign. |
367
|
|
|
return true; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
return false; |
371
|
|
|
|
372
|
|
|
case \T_NAMESPACE: |
373
|
|
|
case \T_PARENT: |
374
|
|
|
case \T_SELF: |
375
|
|
|
case \T_DOUBLE_COLON: |
376
|
|
|
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonSimple + 1), ($end + 1), true); |
377
|
|
|
|
378
|
|
|
if ($tokens[$nextNonSimple]['code'] === \T_NAMESPACE) { |
379
|
|
|
// Allow only `namespace\...`. |
380
|
|
|
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_NS_SEPARATOR) { |
381
|
|
|
return false; |
382
|
|
|
} |
383
|
|
|
} elseif ($tokens[$nextNonSimple]['code'] === \T_PARENT |
384
|
|
|
|| $tokens[$nextNonSimple]['code'] === \T_SELF |
385
|
|
|
) { |
386
|
|
|
// Allow only `parent::` and `self::`. |
387
|
|
View Code Duplication |
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_DOUBLE_COLON) { |
|
|
|
|
388
|
|
|
return false; |
389
|
|
|
} |
390
|
|
|
} elseif ($tokens[$nextNonSimple]['code'] === \T_DOUBLE_COLON) { |
391
|
|
|
// Allow only `T_STRING::T_STRING`. |
392
|
|
|
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_STRING) { |
393
|
|
|
return false; |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($nextNonSimple - 1), null, true); |
397
|
|
|
// No need to worry about parent/self, that's handled above and |
398
|
|
|
// the double colon is skipped over in that case. |
399
|
|
|
if ($prevNonEmpty === false || $tokens[$prevNonEmpty]['code'] !== \T_STRING) { |
400
|
|
|
return false; |
401
|
|
|
} |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
// Examine what comes after the namespace/parent/self/double colon, if anything. |
405
|
|
|
return $this->isStaticValue($phpcsFile, $tokens, ($nextNonEmpty + 1), $end, $nestedArrays); |
406
|
|
|
|
407
|
|
|
case \T_ARRAY: |
408
|
|
|
case \T_OPEN_SHORT_ARRAY: |
409
|
|
|
++$nestedArrays; |
410
|
|
|
|
411
|
|
|
$arrayItems = $this->getFunctionCallParameters($phpcsFile, $nextNonSimple); |
412
|
|
|
if (empty($arrayItems) === false) { |
413
|
|
|
foreach ($arrayItems as $item) { |
414
|
|
|
// Check for a double arrow, but only if it's for this array item, not for a nested array. |
415
|
|
|
$doubleArrow = false; |
416
|
|
|
|
417
|
|
|
$maybeDoubleArrow = $phpcsFile->findNext( |
418
|
|
|
array(\T_DOUBLE_ARROW, \T_ARRAY, \T_OPEN_SHORT_ARRAY), |
419
|
|
|
$item['start'], |
420
|
|
|
($item['end'] + 1) |
421
|
|
|
); |
422
|
|
|
if ($maybeDoubleArrow !== false && $tokens[$maybeDoubleArrow]['code'] === \T_DOUBLE_ARROW) { |
423
|
|
|
// Double arrow is for this nesting level. |
424
|
|
|
$doubleArrow = $maybeDoubleArrow; |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
if ($doubleArrow === false) { |
428
|
|
View Code Duplication |
if ($this->isStaticValue($phpcsFile, $tokens, $item['start'], $item['end'], $nestedArrays) === false) { |
|
|
|
|
429
|
|
|
return false; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
} else { |
433
|
|
|
// Examine array key. |
434
|
|
View Code Duplication |
if ($this->isStaticValue($phpcsFile, $tokens, $item['start'], ($doubleArrow - 1), $nestedArrays) === false) { |
|
|
|
|
435
|
|
|
return false; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
// Examine array value. |
439
|
|
View Code Duplication |
if ($this->isStaticValue($phpcsFile, $tokens, ($doubleArrow + 1), $item['end'], $nestedArrays) === false) { |
|
|
|
|
440
|
|
|
return false; |
441
|
|
|
} |
442
|
|
|
} |
443
|
|
|
} |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
--$nestedArrays; |
447
|
|
|
|
448
|
|
|
/* |
449
|
|
|
* Find the end of the array. |
450
|
|
|
* We already know we will have a valid closer as otherwise we wouldn't have been |
451
|
|
|
* able to get the array items. |
452
|
|
|
*/ |
453
|
|
|
$closer = ($nextNonSimple + 1); |
454
|
|
|
if ($tokens[$nextNonSimple]['code'] === \T_OPEN_SHORT_ARRAY |
455
|
|
|
&& isset($tokens[$nextNonSimple]['bracket_closer']) === true |
456
|
|
|
) { |
457
|
|
|
$closer = $tokens[$nextNonSimple]['bracket_closer']; |
458
|
|
|
} else { |
459
|
|
|
$maybeOpener = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonSimple + 1), ($end + 1), true); |
460
|
|
|
if ($tokens[$maybeOpener]['code'] === \T_OPEN_PARENTHESIS) { |
461
|
|
|
$opener = $maybeOpener; |
462
|
|
|
if (isset($tokens[$opener]['parenthesis_closer']) === true) { |
463
|
|
|
$closer = $tokens[$opener]['parenthesis_closer']; |
464
|
|
|
} |
465
|
|
|
} |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
if ($closer === $end) { |
469
|
|
|
return true; |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
// Examine what comes after the array, if anything. |
473
|
|
|
return $this->isStaticValue($phpcsFile, $tokens, ($closer + 1), $end, $nestedArrays); |
474
|
|
|
|
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
// Ok, so this unsafe token was not one of the exceptions, i.e. this is a PHP 5.6+ syntax. |
478
|
|
|
return false; |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* Throw an error if a scalar expression is found. |
484
|
|
|
* |
485
|
|
|
* @since 8.2.0 |
486
|
|
|
* |
487
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. |
488
|
|
|
* @param int $stackPtr The position of the token to link the error to. |
489
|
|
|
* @param string $type Type of usage found. |
490
|
|
|
* @param string $content Optional. The value for the declaration as found. |
491
|
|
|
* |
492
|
|
|
* @return void |
493
|
|
|
*/ |
494
|
|
|
protected function throwError(File $phpcsFile, $stackPtr, $type, $content = '') |
495
|
|
|
{ |
496
|
|
|
$error = static::ERROR_PHRASE; |
497
|
|
|
$phrase = ''; |
498
|
|
|
$errorCode = 'Found'; |
499
|
|
|
|
500
|
|
|
if (isset($this->errorPhrases[$type]) === true) { |
501
|
|
|
$errorCode = $this->stringToErrorCode($type) . 'Found'; |
502
|
|
|
$phrase = $this->errorPhrases[$type]; |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
$data = array($phrase); |
506
|
|
|
|
507
|
|
|
if (empty($content) === false) { |
508
|
|
|
$error .= ' Found: %s'; |
509
|
|
|
$data[] = $content; |
510
|
|
|
} |
511
|
|
|
|
512
|
|
|
$phpcsFile->addError($error, $stackPtr, $errorCode, $data); |
513
|
|
|
} |
514
|
|
|
|
515
|
|
|
|
516
|
|
|
/** |
517
|
|
|
* Helper function to find the end of multi variable/constant declarations. |
518
|
|
|
* |
519
|
|
|
* Checks whether a certain part of a declaration needs to be skipped over or |
520
|
|
|
* if it is the real end of the declaration. |
521
|
|
|
* |
522
|
|
|
* @since 8.2.0 |
523
|
|
|
* |
524
|
|
|
* @param array $tokens Token stack of the current file. |
525
|
|
|
* @param int $endPtr The token to examine as a candidate end pointer. |
526
|
|
|
* @param int $targetLevel Target nesting level. |
527
|
|
|
* |
528
|
|
|
* @return bool|int True if this is the real end. Int stackPtr to skip to if not. |
529
|
|
|
*/ |
530
|
|
|
private function isRealEndOfDeclaration($tokens, $endPtr, $targetLevel) |
531
|
|
|
{ |
532
|
|
|
// Ignore anything within short array definition brackets for now. |
533
|
|
|
if ($tokens[$endPtr]['code'] === \T_OPEN_SHORT_ARRAY |
534
|
|
|
&& (isset($tokens[$endPtr]['bracket_opener']) |
535
|
|
|
&& $tokens[$endPtr]['bracket_opener'] === $endPtr) |
536
|
|
|
&& isset($tokens[$endPtr]['bracket_closer']) |
537
|
|
|
) { |
538
|
|
|
// Skip forward to the end of the short array definition. |
539
|
|
|
return $tokens[$endPtr]['bracket_closer']; |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
// Skip past comma's at a lower nesting level. |
543
|
|
|
if ($tokens[$endPtr]['code'] === \T_COMMA) { |
544
|
|
|
// Check if a comma is at the nesting level we're targetting. |
545
|
|
|
$nestingLevel = 0; |
546
|
|
View Code Duplication |
if (isset($tokens[$endPtr]['nested_parenthesis']) === true) { |
|
|
|
|
547
|
|
|
$nestingLevel = \count($tokens[$endPtr]['nested_parenthesis']); |
548
|
|
|
} |
549
|
|
|
if ($nestingLevel > $targetLevel) { |
550
|
|
|
return $endPtr; |
551
|
|
|
} |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
return true; |
555
|
|
|
} |
556
|
|
|
} |
557
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.