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; |
12
|
|
|
|
13
|
|
|
use PHP_CodeSniffer_Exception as PHPCS_Exception; |
14
|
|
|
use PHP_CodeSniffer_File as File; |
15
|
|
|
use PHP_CodeSniffer_Tokens as Tokens; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* PHPCS cross-version compatibility helper class. |
19
|
|
|
* |
20
|
|
|
* A number of PHPCS classes were split up into several classes in PHPCS 3.x |
21
|
|
|
* Those classes cannot be aliased as they don't represent the same object. |
22
|
|
|
* This class provides helper methods for functions which were contained in |
23
|
|
|
* one of these classes and which are used within the PHPCompatibility library. |
24
|
|
|
* |
25
|
|
|
* Additionally, this class contains some duplicates of PHPCS native methods. |
26
|
|
|
* These methods have received bug fixes or improved functionality between the |
27
|
|
|
* lowest supported PHPCS version and the latest PHPCS stable version and |
28
|
|
|
* to provide the same results cross-version, PHPCompatibility needs to use |
29
|
|
|
* the up-to-date versions of these methods. |
30
|
|
|
* |
31
|
|
|
* @since 8.0.0 |
32
|
|
|
* @since 8.2.0 The duplicate PHPCS methods have been moved from the `Sniff` |
33
|
|
|
* base class to this class. |
34
|
|
|
*/ |
35
|
|
|
class PHPCSHelper |
36
|
|
|
{ |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Get the PHPCS version number. |
40
|
|
|
* |
41
|
|
|
* @since 8.0.0 |
42
|
|
|
* |
43
|
|
|
* @return string |
44
|
|
|
*/ |
45
|
|
|
public static function getVersion() |
46
|
|
|
{ |
47
|
|
|
if (\defined('\PHP_CodeSniffer\Config::VERSION')) { |
48
|
|
|
// PHPCS 3.x. |
49
|
|
|
return \PHP_CodeSniffer\Config::VERSION; |
50
|
|
|
} else { |
51
|
|
|
// PHPCS 2.x. |
52
|
|
|
return \PHP_CodeSniffer::VERSION; |
53
|
|
|
} |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Pass config data to PHPCS. |
59
|
|
|
* |
60
|
|
|
* PHPCS cross-version compatibility helper. |
61
|
|
|
* |
62
|
|
|
* @since 8.0.0 |
63
|
|
|
* |
64
|
|
|
* @param string $key The name of the config value. |
65
|
|
|
* @param string|null $value The value to set. If null, the config entry |
66
|
|
|
* is deleted, reverting it to the default value. |
67
|
|
|
* @param boolean $temp Set this config data temporarily for this script run. |
68
|
|
|
* This will not write the config data to the config file. |
69
|
|
|
* |
70
|
|
|
* @return void |
71
|
|
|
*/ |
72
|
|
|
public static function setConfigData($key, $value, $temp = false) |
73
|
|
|
{ |
74
|
|
|
if (method_exists('\PHP_CodeSniffer\Config', 'setConfigData')) { |
75
|
|
|
// PHPCS 3.x. |
76
|
|
|
\PHP_CodeSniffer\Config::setConfigData($key, $value, $temp); |
77
|
|
|
} else { |
78
|
|
|
// PHPCS 2.x. |
79
|
|
|
\PHP_CodeSniffer::setConfigData($key, $value, $temp); |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Get the value of a single PHPCS config key. |
86
|
|
|
* |
87
|
|
|
* @since 8.0.0 |
88
|
|
|
* |
89
|
|
|
* @param string $key The name of the config value. |
90
|
|
|
* |
91
|
|
|
* @return string|null |
92
|
|
|
*/ |
93
|
|
|
public static function getConfigData($key) |
94
|
|
|
{ |
95
|
|
|
if (method_exists('\PHP_CodeSniffer\Config', 'getConfigData')) { |
96
|
|
|
// PHPCS 3.x. |
97
|
|
|
return \PHP_CodeSniffer\Config::getConfigData($key); |
98
|
|
|
} else { |
99
|
|
|
// PHPCS 2.x. |
100
|
|
|
return \PHP_CodeSniffer::getConfigData($key); |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Get the value of a single PHPCS config key. |
107
|
|
|
* |
108
|
|
|
* This config key can be set in the `CodeSniffer.conf` file, on the |
109
|
|
|
* command-line or in a ruleset. |
110
|
|
|
* |
111
|
|
|
* @since 8.2.0 |
112
|
|
|
* |
113
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. |
114
|
|
|
* @param string $key The name of the config value. |
115
|
|
|
* |
116
|
|
|
* @return string|null |
117
|
|
|
*/ |
118
|
|
|
public static function getCommandLineData(File $phpcsFile, $key) |
119
|
|
|
{ |
120
|
|
|
if (class_exists('\PHP_CodeSniffer\Config')) { |
121
|
|
|
// PHPCS 3.x. |
122
|
|
|
$config = $phpcsFile->config; |
123
|
|
|
if (isset($config->{$key})) { |
124
|
|
|
return $config->{$key}; |
125
|
|
|
} |
126
|
|
|
} else { |
127
|
|
|
// PHPCS 2.x. |
128
|
|
|
$config = $phpcsFile->phpcs->cli->getCommandLineValues(); |
129
|
|
|
if (isset($config[$key])) { |
130
|
|
|
return $config[$key]; |
131
|
|
|
} |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
return null; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Returns the position of the first non-whitespace token in a statement. |
140
|
|
|
* |
141
|
|
|
* {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File` |
142
|
|
|
* class and introduced in PHPCS 2.1.0 and improved in PHPCS 2.7.1. |
143
|
|
|
* |
144
|
|
|
* Once the minimum supported PHPCS version for this standard goes beyond |
145
|
|
|
* that, this method can be removed and calls to it replaced with |
146
|
|
|
* `$phpcsFile->findStartOfStatement($start, $ignore)` calls. |
147
|
|
|
* |
148
|
|
|
* Last synced with PHPCS version: PHPCS 3.3.2 at commit 6ad28354c04b364c3c71a34e4a18b629cc3b231e} |
149
|
|
|
* |
150
|
|
|
* @since 9.1.0 |
151
|
|
|
* |
152
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. |
153
|
|
|
* @param int $start The position to start searching from in the token stack. |
154
|
|
|
* @param int|array $ignore Token types that should not be considered stop points. |
155
|
|
|
* |
156
|
|
|
* @return int |
157
|
|
|
*/ |
158
|
|
|
public static function findStartOfStatement(File $phpcsFile, $start, $ignore = null) |
159
|
|
|
{ |
160
|
|
|
if (version_compare(self::getVersion(), '2.7.1', '>=') === true) { |
161
|
|
|
return $phpcsFile->findStartOfStatement($start, $ignore); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
$tokens = $phpcsFile->getTokens(); |
165
|
|
|
$endTokens = Tokens::$blockOpeners; |
166
|
|
|
|
167
|
|
|
$endTokens[\T_COLON] = true; |
168
|
|
|
$endTokens[\T_COMMA] = true; |
169
|
|
|
$endTokens[\T_DOUBLE_ARROW] = true; |
170
|
|
|
$endTokens[\T_SEMICOLON] = true; |
171
|
|
|
$endTokens[\T_OPEN_TAG] = true; |
172
|
|
|
$endTokens[\T_CLOSE_TAG] = true; |
173
|
|
|
$endTokens[\T_OPEN_SHORT_ARRAY] = true; |
174
|
|
|
|
175
|
|
View Code Duplication |
if ($ignore !== null) { |
|
|
|
|
176
|
|
|
$ignore = (array) $ignore; |
177
|
|
|
foreach ($ignore as $code) { |
178
|
|
|
if (isset($endTokens[$code]) === true) { |
179
|
|
|
unset($endTokens[$code]); |
180
|
|
|
} |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
$lastNotEmpty = $start; |
185
|
|
|
|
186
|
|
|
for ($i = $start; $i >= 0; $i--) { |
187
|
|
|
if (isset($endTokens[$tokens[$i]['code']]) === true) { |
188
|
|
|
// Found the end of the previous statement. |
189
|
|
|
return $lastNotEmpty; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
if (isset($tokens[$i]['scope_opener']) === true |
193
|
|
|
&& $i === $tokens[$i]['scope_closer'] |
194
|
|
|
) { |
195
|
|
|
// Found the end of the previous scope block. |
196
|
|
|
return $lastNotEmpty; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
// Skip nested statements. |
200
|
|
View Code Duplication |
if (isset($tokens[$i]['bracket_opener']) === true |
|
|
|
|
201
|
|
|
&& $i === $tokens[$i]['bracket_closer'] |
202
|
|
|
) { |
203
|
|
|
$i = $tokens[$i]['bracket_opener']; |
204
|
|
|
} elseif (isset($tokens[$i]['parenthesis_opener']) === true |
205
|
|
|
&& $i === $tokens[$i]['parenthesis_closer'] |
206
|
|
|
) { |
207
|
|
|
$i = $tokens[$i]['parenthesis_opener']; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
View Code Duplication |
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false) { |
|
|
|
|
211
|
|
|
$lastNotEmpty = $i; |
212
|
|
|
} |
213
|
|
|
}//end for |
214
|
|
|
|
215
|
|
|
return 0; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* Returns the position of the last non-whitespace token in a statement. |
221
|
|
|
* |
222
|
|
|
* {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File` |
223
|
|
|
* class and introduced in PHPCS 2.1.0 and improved in PHPCS 2.7.1 and 3.3.0. |
224
|
|
|
* |
225
|
|
|
* Once the minimum supported PHPCS version for this standard goes beyond |
226
|
|
|
* that, this method can be removed and calls to it replaced with |
227
|
|
|
* `$phpcsFile->findEndOfStatement($start, $ignore)` calls. |
228
|
|
|
* |
229
|
|
|
* Last synced with PHPCS version: PHPCS 3.3.0-alpha at commit f5d899dcb5c534a1c3cca34668624517856ba823} |
230
|
|
|
* |
231
|
|
|
* @since 8.2.0 |
232
|
|
|
* |
233
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. |
234
|
|
|
* @param int $start The position to start searching from in the token stack. |
235
|
|
|
* @param int|array $ignore Token types that should not be considered stop points. |
236
|
|
|
* |
237
|
|
|
* @return int |
238
|
|
|
*/ |
239
|
|
|
public static function findEndOfStatement(File $phpcsFile, $start, $ignore = null) |
240
|
|
|
{ |
241
|
|
|
if (version_compare(self::getVersion(), '3.3.0', '>=') === true) { |
242
|
|
|
return $phpcsFile->findEndOfStatement($start, $ignore); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
$tokens = $phpcsFile->getTokens(); |
246
|
|
|
$endTokens = array( |
247
|
|
|
\T_COLON => true, |
248
|
|
|
\T_COMMA => true, |
249
|
|
|
\T_DOUBLE_ARROW => true, |
250
|
|
|
\T_SEMICOLON => true, |
251
|
|
|
\T_CLOSE_PARENTHESIS => true, |
252
|
|
|
\T_CLOSE_SQUARE_BRACKET => true, |
253
|
|
|
\T_CLOSE_CURLY_BRACKET => true, |
254
|
|
|
\T_CLOSE_SHORT_ARRAY => true, |
255
|
|
|
\T_OPEN_TAG => true, |
256
|
|
|
\T_CLOSE_TAG => true, |
257
|
|
|
); |
258
|
|
|
|
259
|
|
View Code Duplication |
if ($ignore !== null) { |
|
|
|
|
260
|
|
|
$ignore = (array) $ignore; |
261
|
|
|
foreach ($ignore as $code) { |
262
|
|
|
if (isset($endTokens[$code]) === true) { |
263
|
|
|
unset($endTokens[$code]); |
264
|
|
|
} |
265
|
|
|
} |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
$lastNotEmpty = $start; |
269
|
|
|
|
270
|
|
|
for ($i = $start; $i < $phpcsFile->numTokens; $i++) { |
271
|
|
|
if ($i !== $start && isset($endTokens[$tokens[$i]['code']]) === true) { |
272
|
|
|
// Found the end of the statement. |
273
|
|
|
if ($tokens[$i]['code'] === \T_CLOSE_PARENTHESIS |
274
|
|
|
|| $tokens[$i]['code'] === \T_CLOSE_SQUARE_BRACKET |
275
|
|
|
|| $tokens[$i]['code'] === \T_CLOSE_CURLY_BRACKET |
276
|
|
|
|| $tokens[$i]['code'] === \T_CLOSE_SHORT_ARRAY |
277
|
|
|
|| $tokens[$i]['code'] === \T_OPEN_TAG |
278
|
|
|
|| $tokens[$i]['code'] === \T_CLOSE_TAG |
279
|
|
|
) { |
280
|
|
|
return $lastNotEmpty; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
return $i; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
// Skip nested statements. |
287
|
|
|
if (isset($tokens[$i]['scope_closer']) === true |
288
|
|
|
&& ($i === $tokens[$i]['scope_opener'] |
289
|
|
|
|| $i === $tokens[$i]['scope_condition']) |
290
|
|
|
) { |
291
|
|
|
if ($i === $start && isset(Tokens::$scopeOpeners[$tokens[$i]['code']]) === true) { |
292
|
|
|
return $tokens[$i]['scope_closer']; |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
$i = $tokens[$i]['scope_closer']; |
296
|
|
View Code Duplication |
} elseif (isset($tokens[$i]['bracket_closer']) === true |
|
|
|
|
297
|
|
|
&& $i === $tokens[$i]['bracket_opener'] |
298
|
|
|
) { |
299
|
|
|
$i = $tokens[$i]['bracket_closer']; |
300
|
|
|
} elseif (isset($tokens[$i]['parenthesis_closer']) === true |
301
|
|
|
&& $i === $tokens[$i]['parenthesis_opener'] |
302
|
|
|
) { |
303
|
|
|
$i = $tokens[$i]['parenthesis_closer']; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
View Code Duplication |
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false) { |
|
|
|
|
307
|
|
|
$lastNotEmpty = $i; |
308
|
|
|
} |
309
|
|
|
}//end for |
310
|
|
|
|
311
|
|
|
return ($phpcsFile->numTokens - 1); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
|
315
|
|
|
/** |
316
|
|
|
* Returns the name of the class that the specified class extends |
317
|
|
|
* (works for classes, anonymous classes and interfaces). |
318
|
|
|
* |
319
|
|
|
* Returns FALSE on error or if there is no extended class name. |
320
|
|
|
* |
321
|
|
|
* {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File` |
322
|
|
|
* class, but with some improvements which have been introduced in |
323
|
|
|
* PHPCS 2.8.0. |
324
|
|
|
* {@link https://github.com/squizlabs/PHP_CodeSniffer/commit/0011d448119d4c568e3ac1f825ae78815bf2cc34}. |
325
|
|
|
* |
326
|
|
|
* Once the minimum supported PHPCS version for this standard goes beyond |
327
|
|
|
* that, this method can be removed and calls to it replaced with |
328
|
|
|
* `$phpcsFile->findExtendedClassName($stackPtr)` calls. |
329
|
|
|
* |
330
|
|
|
* Last synced with PHPCS version: PHPCS 3.1.0-alpha at commit a9efcc9b0703f3f9f4a900623d4e97128a6aafc6} |
331
|
|
|
* |
332
|
|
|
* @since 7.1.4 |
333
|
|
|
* @since 8.2.0 Moved from the `Sniff` class to this class. |
334
|
|
|
* |
335
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. |
336
|
|
|
* @param int $stackPtr The position of the class token in the stack. |
337
|
|
|
* |
338
|
|
|
* @return string|false |
339
|
|
|
*/ |
340
|
|
|
public static function findExtendedClassName(File $phpcsFile, $stackPtr) |
341
|
|
|
{ |
342
|
|
|
if (version_compare(self::getVersion(), '3.1.0', '>=') === true) { |
343
|
|
|
return $phpcsFile->findExtendedClassName($stackPtr); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
$tokens = $phpcsFile->getTokens(); |
347
|
|
|
|
348
|
|
|
// Check for the existence of the token. |
349
|
|
|
if (isset($tokens[$stackPtr]) === false) { |
350
|
|
|
return false; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
View Code Duplication |
if ($tokens[$stackPtr]['code'] !== \T_CLASS |
|
|
|
|
354
|
|
|
&& $tokens[$stackPtr]['type'] !== 'T_ANON_CLASS' |
355
|
|
|
&& $tokens[$stackPtr]['type'] !== 'T_INTERFACE' |
356
|
|
|
) { |
357
|
|
|
return false; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
if (isset($tokens[$stackPtr]['scope_closer']) === false) { |
361
|
|
|
return false; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
$classCloserIndex = $tokens[$stackPtr]['scope_closer']; |
365
|
|
|
$extendsIndex = $phpcsFile->findNext(\T_EXTENDS, $stackPtr, $classCloserIndex); |
366
|
|
|
if ($extendsIndex === false) { |
367
|
|
|
return false; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
$find = array( |
371
|
|
|
\T_NS_SEPARATOR, |
372
|
|
|
\T_STRING, |
373
|
|
|
\T_WHITESPACE, |
374
|
|
|
); |
375
|
|
|
|
376
|
|
|
$end = $phpcsFile->findNext($find, ($extendsIndex + 1), $classCloserIndex, true); |
377
|
|
|
$name = $phpcsFile->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1)); |
378
|
|
|
$name = trim($name); |
379
|
|
|
|
380
|
|
|
if ($name === '') { |
381
|
|
|
return false; |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
return $name; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* Returns the name(s) of the interface(s) that the specified class implements. |
390
|
|
|
* |
391
|
|
|
* Returns FALSE on error or if there are no implemented interface names. |
392
|
|
|
* |
393
|
|
|
* {@internal Duplicate of same method as introduced in PHPCS 2.7. |
394
|
|
|
* This method also includes an improvement we use which was only introduced |
395
|
|
|
* in PHPCS 2.8.0, so only defer to upstream for higher versions. |
396
|
|
|
* Once the minimum supported PHPCS version for this sniff library goes beyond |
397
|
|
|
* that, this method can be removed and calls to it replaced with |
398
|
|
|
* `$phpcsFile->findImplementedInterfaceNames($stackPtr)` calls.} |
399
|
|
|
* |
400
|
|
|
* @since 7.0.3 |
401
|
|
|
* @since 8.2.0 Moved from the `Sniff` class to this class. |
402
|
|
|
* |
403
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. |
404
|
|
|
* @param int $stackPtr The position of the class token. |
405
|
|
|
* |
406
|
|
|
* @return array|false |
407
|
|
|
*/ |
408
|
|
|
public static function findImplementedInterfaceNames(File $phpcsFile, $stackPtr) |
409
|
|
|
{ |
410
|
|
|
if (version_compare(self::getVersion(), '2.7.1', '>') === true) { |
411
|
|
|
return $phpcsFile->findImplementedInterfaceNames($stackPtr); |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
$tokens = $phpcsFile->getTokens(); |
415
|
|
|
|
416
|
|
|
// Check for the existence of the token. |
417
|
|
|
if (isset($tokens[$stackPtr]) === false) { |
418
|
|
|
return false; |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
if ($tokens[$stackPtr]['code'] !== \T_CLASS |
422
|
|
|
&& $tokens[$stackPtr]['type'] !== 'T_ANON_CLASS' |
423
|
|
|
) { |
424
|
|
|
return false; |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
if (isset($tokens[$stackPtr]['scope_closer']) === false) { |
428
|
|
|
return false; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
$classOpenerIndex = $tokens[$stackPtr]['scope_opener']; |
432
|
|
|
$implementsIndex = $phpcsFile->findNext(\T_IMPLEMENTS, $stackPtr, $classOpenerIndex); |
433
|
|
|
if ($implementsIndex === false) { |
434
|
|
|
return false; |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
$find = array( |
438
|
|
|
\T_NS_SEPARATOR, |
439
|
|
|
\T_STRING, |
440
|
|
|
\T_WHITESPACE, |
441
|
|
|
\T_COMMA, |
442
|
|
|
); |
443
|
|
|
|
444
|
|
|
$end = $phpcsFile->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true); |
445
|
|
|
$name = $phpcsFile->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1)); |
446
|
|
|
$name = trim($name); |
447
|
|
|
|
448
|
|
|
if ($name === '') { |
449
|
|
|
return false; |
450
|
|
|
} else { |
451
|
|
|
$names = explode(',', $name); |
452
|
|
|
$names = array_map('trim', $names); |
453
|
|
|
return $names; |
454
|
|
|
} |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
|
458
|
|
|
/** |
459
|
|
|
* Returns the method parameters for the specified function token. |
460
|
|
|
* |
461
|
|
|
* Each parameter is in the following format: |
462
|
|
|
* |
463
|
|
|
* <code> |
464
|
|
|
* 0 => array( |
465
|
|
|
* 'name' => '$var', // The variable name. |
466
|
|
|
* 'token' => integer, // The stack pointer to the variable name. |
467
|
|
|
* 'content' => string, // The full content of the variable definition. |
468
|
|
|
* 'pass_by_reference' => boolean, // Is the variable passed by reference? |
469
|
|
|
* 'variable_length' => boolean, // Is the param of variable length through use of `...` ? |
470
|
|
|
* 'type_hint' => string, // The type hint for the variable. |
471
|
|
|
* 'type_hint_token' => integer, // The stack pointer to the type hint |
472
|
|
|
* // or false if there is no type hint. |
473
|
|
|
* 'nullable_type' => boolean, // Is the variable using a nullable type? |
474
|
|
|
* ) |
475
|
|
|
* </code> |
476
|
|
|
* |
477
|
|
|
* Parameters with default values have an additional array index of |
478
|
|
|
* 'default' with the value of the default as a string. |
479
|
|
|
* |
480
|
|
|
* {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File` |
481
|
|
|
* class. |
482
|
|
|
* |
483
|
|
|
* Last synced with PHPCS version: PHPCS 3.3.0-alpha at commit 53a28408d345044c0360c2c1b4a2aaebf4a3b8c9} |
484
|
|
|
* |
485
|
|
|
* @since 7.0.3 |
486
|
|
|
* @since 8.2.0 Moved from the `Sniff` class to this class. |
487
|
|
|
* |
488
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. |
489
|
|
|
* @param int $stackPtr The position in the stack of the |
490
|
|
|
* function token to acquire the |
491
|
|
|
* parameters for. |
492
|
|
|
* |
493
|
|
|
* @return array|false |
494
|
|
|
* @throws \PHP_CodeSniffer_Exception If the specified $stackPtr is not of |
495
|
|
|
* type T_FUNCTION or T_CLOSURE. |
496
|
|
|
*/ |
497
|
|
|
public static function getMethodParameters(File $phpcsFile, $stackPtr) |
498
|
|
|
{ |
499
|
|
|
if (version_compare(self::getVersion(), '3.3.0', '>=') === true) { |
500
|
|
|
return $phpcsFile->getMethodParameters($stackPtr); |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
$tokens = $phpcsFile->getTokens(); |
504
|
|
|
|
505
|
|
|
// Check for the existence of the token. |
506
|
|
|
if (isset($tokens[$stackPtr]) === false) { |
507
|
|
|
return false; |
508
|
|
|
} |
509
|
|
|
|
510
|
|
View Code Duplication |
if ($tokens[$stackPtr]['code'] !== \T_FUNCTION |
|
|
|
|
511
|
|
|
&& $tokens[$stackPtr]['code'] !== \T_CLOSURE |
512
|
|
|
) { |
513
|
|
|
throw new PHPCS_Exception('$stackPtr must be of type T_FUNCTION or T_CLOSURE'); |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
$opener = $tokens[$stackPtr]['parenthesis_opener']; |
517
|
|
|
$closer = $tokens[$stackPtr]['parenthesis_closer']; |
518
|
|
|
|
519
|
|
|
$vars = array(); |
520
|
|
|
$currVar = null; |
521
|
|
|
$paramStart = ($opener + 1); |
522
|
|
|
$defaultStart = null; |
523
|
|
|
$paramCount = 0; |
524
|
|
|
$passByReference = false; |
525
|
|
|
$variableLength = false; |
526
|
|
|
$typeHint = ''; |
527
|
|
|
$typeHintToken = false; |
528
|
|
|
$nullableType = false; |
529
|
|
|
|
530
|
|
|
for ($i = $paramStart; $i <= $closer; $i++) { |
531
|
|
|
// Check to see if this token has a parenthesis or bracket opener. If it does |
532
|
|
|
// it's likely to be an array which might have arguments in it. This |
533
|
|
|
// could cause problems in our parsing below, so lets just skip to the |
534
|
|
|
// end of it. |
535
|
|
|
if (isset($tokens[$i]['parenthesis_opener']) === true) { |
536
|
|
|
// Don't do this if it's the close parenthesis for the method. |
537
|
|
|
if ($i !== $tokens[$i]['parenthesis_closer']) { |
538
|
|
|
$i = ($tokens[$i]['parenthesis_closer'] + 1); |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
if (isset($tokens[$i]['bracket_opener']) === true) { |
543
|
|
|
// Don't do this if it's the close parenthesis for the method. |
544
|
|
|
if ($i !== $tokens[$i]['bracket_closer']) { |
545
|
|
|
$i = ($tokens[$i]['bracket_closer'] + 1); |
546
|
|
|
} |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
switch ($tokens[$i]['type']) { |
550
|
|
|
case 'T_BITWISE_AND': |
551
|
|
|
if ($defaultStart === null) { |
552
|
|
|
$passByReference = true; |
553
|
|
|
} |
554
|
|
|
break; |
555
|
|
|
case 'T_VARIABLE': |
556
|
|
|
$currVar = $i; |
557
|
|
|
break; |
558
|
|
|
case 'T_ELLIPSIS': |
559
|
|
|
$variableLength = true; |
560
|
|
|
break; |
561
|
|
|
case 'T_ARRAY_HINT': // Pre-PHPCS 3.3.0. |
562
|
|
|
case 'T_CALLABLE': |
563
|
|
|
if ($typeHintToken === false) { |
564
|
|
|
$typeHintToken = $i; |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
$typeHint .= $tokens[$i]['content']; |
568
|
|
|
break; |
569
|
|
|
case 'T_SELF': |
570
|
|
|
case 'T_PARENT': |
571
|
|
View Code Duplication |
case 'T_STATIC': |
|
|
|
|
572
|
|
|
// Self and parent are valid, static invalid, but was probably intended as type hint. |
573
|
|
|
if (isset($defaultStart) === false) { |
574
|
|
|
if ($typeHintToken === false) { |
575
|
|
|
$typeHintToken = $i; |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
$typeHint .= $tokens[$i]['content']; |
579
|
|
|
} |
580
|
|
|
break; |
581
|
|
|
case 'T_STRING': |
582
|
|
|
// This is a string, so it may be a type hint, but it could |
583
|
|
|
// also be a constant used as a default value. |
584
|
|
|
$prevComma = false; |
585
|
|
View Code Duplication |
for ($t = $i; $t >= $opener; $t--) { |
|
|
|
|
586
|
|
|
if ($tokens[$t]['code'] === \T_COMMA) { |
587
|
|
|
$prevComma = $t; |
588
|
|
|
break; |
589
|
|
|
} |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
if ($prevComma !== false) { |
593
|
|
|
$nextEquals = false; |
594
|
|
View Code Duplication |
for ($t = $prevComma; $t < $i; $t++) { |
|
|
|
|
595
|
|
|
if ($tokens[$t]['code'] === \T_EQUAL) { |
596
|
|
|
$nextEquals = $t; |
597
|
|
|
break; |
598
|
|
|
} |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
if ($nextEquals !== false) { |
602
|
|
|
break; |
603
|
|
|
} |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
if ($defaultStart === null) { |
607
|
|
|
if ($typeHintToken === false) { |
608
|
|
|
$typeHintToken = $i; |
609
|
|
|
} |
610
|
|
|
|
611
|
|
|
$typeHint .= $tokens[$i]['content']; |
612
|
|
|
} |
613
|
|
|
break; |
614
|
|
View Code Duplication |
case 'T_NS_SEPARATOR': |
|
|
|
|
615
|
|
|
// Part of a type hint or default value. |
616
|
|
|
if ($defaultStart === null) { |
617
|
|
|
if ($typeHintToken === false) { |
618
|
|
|
$typeHintToken = $i; |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
$typeHint .= $tokens[$i]['content']; |
622
|
|
|
} |
623
|
|
|
break; |
624
|
|
|
case 'T_NULLABLE': |
625
|
|
|
case 'T_INLINE_THEN': // Pre-PHPCS 2.8.0. |
626
|
|
|
if ($defaultStart === null) { |
627
|
|
|
$nullableType = true; |
628
|
|
|
$typeHint .= $tokens[$i]['content']; |
629
|
|
|
} |
630
|
|
|
break; |
631
|
|
|
case 'T_CLOSE_PARENTHESIS': |
632
|
|
|
case 'T_COMMA': |
633
|
|
|
// If it's null, then there must be no parameters for this |
634
|
|
|
// method. |
635
|
|
|
if ($currVar === null) { |
636
|
|
|
break; |
637
|
|
|
} |
638
|
|
|
|
639
|
|
|
$vars[$paramCount] = array(); |
640
|
|
|
$vars[$paramCount]['token'] = $currVar; |
641
|
|
|
$vars[$paramCount]['name'] = $tokens[$currVar]['content']; |
642
|
|
|
$vars[$paramCount]['content'] = trim($phpcsFile->getTokensAsString($paramStart, ($i - $paramStart))); |
643
|
|
|
|
644
|
|
|
if ($defaultStart !== null) { |
645
|
|
|
$vars[$paramCount]['default'] = trim( |
646
|
|
|
$phpcsFile->getTokensAsString( |
647
|
|
|
$defaultStart, |
648
|
|
|
($i - $defaultStart) |
649
|
|
|
) |
650
|
|
|
); |
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
$vars[$paramCount]['pass_by_reference'] = $passByReference; |
654
|
|
|
$vars[$paramCount]['variable_length'] = $variableLength; |
655
|
|
|
$vars[$paramCount]['type_hint'] = $typeHint; |
656
|
|
|
$vars[$paramCount]['type_hint_token'] = $typeHintToken; |
657
|
|
|
$vars[$paramCount]['nullable_type'] = $nullableType; |
658
|
|
|
|
659
|
|
|
// Reset the vars, as we are about to process the next parameter. |
660
|
|
|
$defaultStart = null; |
661
|
|
|
$paramStart = ($i + 1); |
662
|
|
|
$passByReference = false; |
663
|
|
|
$variableLength = false; |
664
|
|
|
$typeHint = ''; |
665
|
|
|
$typeHintToken = false; |
666
|
|
|
$nullableType = false; |
667
|
|
|
|
668
|
|
|
$paramCount++; |
669
|
|
|
break; |
670
|
|
|
case 'T_EQUAL': |
671
|
|
|
$defaultStart = ($i + 1); |
672
|
|
|
break; |
673
|
|
|
}//end switch |
674
|
|
|
}//end for |
675
|
|
|
|
676
|
|
|
return $vars; |
677
|
|
|
} |
678
|
|
|
} |
679
|
|
|
|
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.