1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* CodeIgniter_Sniffs_NamingConventions_ValidVariableNameSniff. |
4
|
|
|
* |
5
|
|
|
* PHP version 5 |
6
|
|
|
* |
7
|
|
|
* @category PHP |
8
|
|
|
* @package PHP_CodeSniffer |
9
|
|
|
* @author Thomas Ernest <[email protected]> |
10
|
|
|
* @copyright 2010 Thomas Ernest |
11
|
|
|
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License |
12
|
|
|
* @link http://pear.php.net/package/PHP_CodeSniffer |
13
|
|
|
*/ |
14
|
|
|
/** |
15
|
|
|
* CodeIgniter_Sniffs_NamingConventions_ValidVariableNameSniff. |
16
|
|
|
* |
17
|
|
|
* Ensures that variable names contain only lowercase letters, |
18
|
|
|
* use underscore separators. |
19
|
|
|
* Ensures that class attribute names are prefixed with an underscore, |
20
|
|
|
* only when they are private. |
21
|
|
|
* Ensure that variable names are longer than 3 chars except those declared |
22
|
|
|
* in for loops. |
23
|
|
|
* |
24
|
|
|
* @todo Try to avoid overly long and verbose names in using property rule and |
25
|
|
|
* configuration variable to set limits. Have a look at |
26
|
|
|
* CodeIgniter_Sniffs_NamingConventions_ValidMethodNameSniff. |
27
|
|
|
* @todo Use a property rule or a configuration variable to allow users to set |
28
|
|
|
* minimum variable name length. Have a look at |
29
|
|
|
* CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff and application root. |
30
|
|
|
* |
31
|
|
|
* @category PHP |
32
|
|
|
* @package PHP_CodeSniffer |
33
|
|
|
* @author Thomas Ernest <[email protected]> |
34
|
|
|
* @copyright 2010 Thomas Ernest |
35
|
|
|
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License |
36
|
|
|
* @link http://pear.php.net/package/PHP_CodeSniffer |
37
|
|
|
*/ |
38
|
|
|
namespace CodeIgniter\Sniffs\NamingConventions; |
39
|
|
|
|
40
|
|
|
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff; |
41
|
|
|
use PHP_CodeSniffer\Files\File; |
42
|
|
|
|
43
|
|
|
class ValidVariableNameSniff extends AbstractVariableSniff |
44
|
|
|
{ |
45
|
|
|
|
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Processes class member variables. |
49
|
|
|
* |
50
|
|
|
* @param File $phpcsFile The file being scanned. |
51
|
|
|
* @param int $stackPtr The position of the current token |
52
|
|
|
* in the stack passed in $tokens. |
53
|
|
|
* |
54
|
|
|
* @return void |
55
|
|
|
*/ |
56
|
|
|
protected function processMemberVar(File $phpcsFile, $stackPtr) |
57
|
|
|
{ |
58
|
|
|
// get variable name and properties |
59
|
|
|
$tokens = $phpcsFile->getTokens(); |
60
|
|
|
$varTk = $tokens[$stackPtr]; |
61
|
|
|
$varName = substr($varTk['content'], 1); |
62
|
|
|
$varProps = $phpcsFile->getMemberProperties($stackPtr); |
63
|
|
|
// check(s) |
64
|
|
|
if ( ! $this->checkLowerCase($phpcsFile, $stackPtr, $varName) ) { |
65
|
|
|
return; |
66
|
|
|
} |
67
|
|
|
if ( ! $this->checkVisibilityPrefix($phpcsFile, $stackPtr, $varName, $varProps)) { |
68
|
|
|
return; |
69
|
|
|
} |
70
|
|
|
if ( ! $this->checkLength($phpcsFile, $stackPtr, $varName)) { |
71
|
|
|
return; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
}//end processMemberVar() |
75
|
|
|
|
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Processes normal variables. |
79
|
|
|
* |
80
|
|
|
* @param File $phpcsFile The file where this token was found. |
81
|
|
|
* @param int $stackPtr The position where the token was found. |
82
|
|
|
* |
83
|
|
|
* @return void |
84
|
|
|
*/ |
85
|
|
|
protected function processVariable(File $phpcsFile, $stackPtr) |
86
|
|
|
{ |
87
|
|
|
// get variable name |
88
|
|
|
$tokens = $phpcsFile->getTokens(); |
89
|
|
|
$varTk = $tokens[$stackPtr]; |
90
|
|
|
$varName = substr($varTk['content'], 1); |
91
|
|
|
// skip the current object variable, i.e. $this |
92
|
|
|
if (0 === strcmp($varName, 'this')) { |
93
|
|
|
return; |
94
|
|
|
} |
95
|
|
|
// check(s) |
96
|
|
|
if ( ! $this->checkLowerCase($phpcsFile, $stackPtr, $varName)) { |
97
|
|
|
return; |
98
|
|
|
} |
99
|
|
|
if ( ! $this->checkLength($phpcsFile, $stackPtr, $varName)) { |
100
|
|
|
return; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
}//end processVariable() |
104
|
|
|
|
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Processes variables in double quoted strings. |
108
|
|
|
* |
109
|
|
|
* @param File $phpcsFile The file where this token was found. |
110
|
|
|
* @param int $stackPtr The position where the token was found. |
111
|
|
|
* |
112
|
|
|
* @return void |
113
|
|
|
*/ |
114
|
|
|
protected function processVariableInString(File $phpcsFile, $stackPtr) |
115
|
|
|
{ |
116
|
|
|
$tokens = $phpcsFile->getTokens(); |
117
|
|
|
$stringTk = $tokens[$stackPtr]; |
118
|
|
|
$stringString = $stringTk['content']; |
119
|
|
|
$varAt = self::_getVariablePosition($stringString, 0); |
120
|
|
|
while (false !== $varAt) { |
121
|
|
|
// get variable name |
122
|
|
|
$matches = array(); |
123
|
|
|
preg_match('/^\$\{?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}?/', substr($stringString, $varAt), $matches); |
|
|
|
|
124
|
|
|
$varName = $matches[1]; |
125
|
|
|
// check(s) |
126
|
|
|
if ( ! $this->checkLowerCase($phpcsFile, $stackPtr, $varName)) { |
127
|
|
|
return; |
128
|
|
|
} |
129
|
|
|
if ( ! $this->checkLength($phpcsFile, $stackPtr, $varName)) { |
130
|
|
|
return; |
131
|
|
|
} |
132
|
|
|
// prepare checking next variable |
133
|
|
|
$varAt = self::_getVariablePosition($stringString, $varAt + 1); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
}//end processVariableInString() |
137
|
|
|
|
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Checks that the variable name is all in lower case, else it add an error |
141
|
|
|
* to $phpcsFile. Returns true if variable name is all in lower case, false |
142
|
|
|
* otherwise. |
143
|
|
|
* |
144
|
|
|
* @param File $phpcsFile The current file being processed. |
145
|
|
|
* @param int $stackPtr The position of the current token |
146
|
|
|
* in the stack passed in $tokens. |
147
|
|
|
* @param string $varName The name of the variable to |
148
|
|
|
* procced without $, { nor }. |
149
|
|
|
* |
150
|
|
|
* @return bool true if variable name is all in lower case, false otherwise. |
151
|
|
|
*/ |
152
|
|
|
protected function checkLowerCase(File $phpcsFile, $stackPtr, $varName) |
153
|
|
|
{ |
154
|
|
|
$isInLowerCase = true; |
155
|
|
|
if (0 !== strcmp($varName, strtolower($varName))) { |
156
|
|
|
// get the expected variable name |
157
|
|
|
$varNameWithUnderscores = preg_replace('/([A-Z])/', '_${1}', $varName); |
158
|
|
|
$expectedVarName = strtolower(ltrim($varNameWithUnderscores, '_')); |
159
|
|
|
// adapts the error message to the error case |
160
|
|
|
if (strlen($varNameWithUnderscores) > strlen($varName)) { |
161
|
|
|
$error = 'Variables should not use CamelCasing or start with a Capital.'; |
162
|
|
|
} else { |
163
|
|
|
$error = 'Variables should be entirely lowercased.'; |
164
|
|
|
} |
165
|
|
|
$error = $error . 'Please consider "' . $expectedVarName |
166
|
|
|
. '" instead of "' . $varName . '".'; |
167
|
|
|
// adds the error and changes return value |
168
|
|
|
$phpcsFile->addError($error, $stackPtr); |
|
|
|
|
169
|
|
|
$isInLowerCase = false; |
170
|
|
|
} |
171
|
|
|
return $isInLowerCase; |
172
|
|
|
}//end checkLowerCase() |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Checks that an underscore is used at the beginning of a variable only if |
176
|
|
|
* it is about a private variable. If it isn't a private variable, then it |
177
|
|
|
* must not be prefixed with an underscore. Returns true if $varName is |
178
|
|
|
* properly prefixed according to the variable visibility provided in |
179
|
|
|
* $varProps, false otherwise. |
180
|
|
|
* |
181
|
|
|
* @param File $phpcsFile The current file being processed. |
182
|
|
|
* @param int $stackPtr The position of the current token |
183
|
|
|
* in the stack passed in $tokens. |
184
|
|
|
* @param string $varName The name of the variable to |
185
|
|
|
* procced without $, { nor }. |
186
|
|
|
* @param array $varProps Member variable properties like |
187
|
|
|
* its visibility. |
188
|
|
|
* |
189
|
|
|
* @return bool true if variable name is prefixed with an underscore only |
190
|
|
|
* when it is about a private variable, false otherwise. |
191
|
|
|
*/ |
192
|
|
|
protected function checkVisibilityPrefix(File $phpcsFile, $stackPtr, $varName, $varProps) |
193
|
|
|
{ |
194
|
|
|
$isVisibilityPrefixRight = true; |
195
|
|
|
$scope = $varProps['scope']; |
196
|
|
|
// If it's a private variable, it must have an underscore on the front. |
197
|
|
|
if ($scope === 'private' && $varName{0} !== '_') { |
198
|
|
|
$error = "Private variable name \"$varName\" must be prefixed with an underscore"; |
199
|
|
|
$phpcsFile->addError($error, $stackPtr); |
|
|
|
|
200
|
|
|
$isVisibilityPrefixRight = false; |
201
|
|
View Code Duplication |
} else if ($scope !== 'private' && $varName{0} === '_') { |
|
|
|
|
202
|
|
|
// If it's not a private variable, |
203
|
|
|
// then it must not start with an underscore. |
204
|
|
|
if (isset ($scopeSpecified) && true === $scopeSpecified) { |
|
|
|
|
205
|
|
|
$error = "Public variable name \"$varName\" must not be prefixed with an underscore"; |
206
|
|
|
} else { |
207
|
|
|
$error = ucfirst($scope) . " variable name \"$varName\" must not be prefixed with an underscore"; |
208
|
|
|
} |
209
|
|
|
$phpcsFile->addError($error, $stackPtr); |
|
|
|
|
210
|
|
|
$isVisibilityPrefixRight = false; |
211
|
|
|
} |
212
|
|
|
return $isVisibilityPrefixRight; |
213
|
|
|
}//end checkVisibilityPrefix() |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Checks that variable name length is not too short. Returns true, if it |
217
|
|
|
* meets minimum length requirement, false otherwise. |
218
|
|
|
* |
219
|
|
|
* A variable name is too short if it is shorter than the minimal |
220
|
|
|
* length and it isn't in the list of allowed short names nor declared in a |
221
|
|
|
* for loop (in which it would be nested). |
222
|
|
|
* The minimal length is defined in the function. It is 3 chars now. |
223
|
|
|
* The list of allowed short names is defined in the function. |
224
|
|
|
* It is case-sensitive. It contains only 'ci' now. |
225
|
|
|
* |
226
|
|
|
* @param File $phpcsFile The current file being processed. |
227
|
|
|
* @param int $stackPtr The position of the current token |
228
|
|
|
* in the stack passed in $tokens. |
229
|
|
|
* @param string $varName The name of the variable to |
230
|
|
|
* procced without $, { nor }. |
231
|
|
|
* |
232
|
|
|
* @return bool false if variable name $varName is shorter than the minimal |
233
|
|
|
* length and it isn't in the list of allowed short names nor declared in a |
234
|
|
|
* for loop (in which it would be nested), otherwise true. |
235
|
|
|
*/ |
236
|
|
|
protected function checkLength(File $phpcsFile, $stackPtr, $varName) |
237
|
|
|
{ |
238
|
|
|
$minLength = 3; |
239
|
|
|
$allowedShortName = array('ci'); |
240
|
|
|
|
241
|
|
|
$isLengthRight = true; |
242
|
|
|
// cleans variable name |
243
|
|
|
$varName = ltrim($varName, '_'); |
244
|
|
|
if (strlen($varName) <= $minLength) { |
245
|
|
|
// skips adding an error, if it is a specific variable name |
246
|
|
|
if (in_array($varName, $allowedShortName)) { |
247
|
|
|
return $isLengthRight; |
248
|
|
|
} |
249
|
|
|
// skips adding an error, if the variable is in a for loop |
250
|
|
|
if (false !== self::_isInForLoop($phpcsFile, $stackPtr, $varName)) { |
251
|
|
|
return $isLengthRight; |
252
|
|
|
} |
253
|
|
|
// adds the error message finally |
254
|
|
|
$error = 'Very short' |
255
|
|
|
. ( |
256
|
|
|
$minLength > 0 ? |
257
|
|
|
' (i.e. less than ' . ($minLength + 1) . ' chars)' |
258
|
|
|
: '' |
259
|
|
|
) |
260
|
|
|
. ', non-word variables like "' . $varName |
261
|
|
|
. '" should only be used as iterators in for() loops.'; |
262
|
|
|
$phpcsFile->addError($error, $stackPtr); |
|
|
|
|
263
|
|
|
$isLengthRight = false; |
264
|
|
|
} |
265
|
|
|
return $isLengthRight; |
266
|
|
|
}//end checkLength() |
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* Returns the position of closest previous T_FOR, if token associated with |
270
|
|
|
* $stackPtr in $phpcsFile is in a for loop, otherwise false. |
271
|
|
|
* |
272
|
|
|
* @param File $phpcsFile The current file being processed. |
273
|
|
|
* @param int $stackPtr The position of the current token |
274
|
|
|
* in the stack passed in $tokens. |
275
|
|
|
* @param string $varName The name of the variable to |
276
|
|
|
* procced without $, { nor }. |
277
|
|
|
* |
278
|
|
|
* @return int|bool Position of T_FOR if token associated with $stackPtr in |
279
|
|
|
* $phpcsFile is in the head of a for loop, otherwise false. |
280
|
|
|
*/ |
281
|
|
|
private static function _isInForLoop(File $phpcsFile, $stackPtr, $varName) |
282
|
|
|
{ |
283
|
|
|
$keepLookingFromPtr = $stackPtr; |
284
|
|
|
while (false !== $keepLookingFromPtr) { |
285
|
|
|
// looks if it is in (head or body) of a for loop |
286
|
|
|
$forPtr = self::_isInForLoopHead($phpcsFile, $keepLookingFromPtr); |
|
|
|
|
287
|
|
|
if (false === $forPtr) { |
288
|
|
|
$forPtr = self::_isInForLoopBody($phpcsFile, $keepLookingFromPtr); |
|
|
|
|
289
|
|
|
} |
290
|
|
|
// checks if it is declared in here and prepares next step |
291
|
|
|
if (false !== $forPtr) { |
292
|
|
|
if (false !== self::_isDeclaredInForLoop($phpcsFile, $forPtr, $varName)) { |
|
|
|
|
293
|
|
|
return $forPtr; |
294
|
|
|
} |
295
|
|
|
$keepLookingFromPtr = $forPtr; |
296
|
|
|
} else { |
297
|
|
|
$keepLookingFromPtr = false; |
298
|
|
|
} |
299
|
|
|
} |
300
|
|
|
return false; |
301
|
|
|
}//end _isInForLoop() |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Returns the position of closest previous T_FOR, if token associated with |
305
|
|
|
* $stackPtr in $phpcsFile is in the head of a for loop, otherwise false. |
306
|
|
|
* The head is the code placed between parenthesis next to the key word |
307
|
|
|
* 'for' : for (<loop_head>) {<loop_body>}. |
308
|
|
|
* |
309
|
|
|
* @param File $phpcsFile The current file being processed. |
310
|
|
|
* @param int $stackPtr The position of the current token |
311
|
|
|
* in the stack passed in $tokens. |
312
|
|
|
* |
313
|
|
|
* @return int|bool Position of T_FOR if token associated with $stackPtr in |
314
|
|
|
* $phpcsFile is in the head of a for loop, otherwise false. |
315
|
|
|
*/ |
316
|
|
|
private static function _isInForLoopHead(File $phpcsFile, $stackPtr) |
317
|
|
|
{ |
318
|
|
|
$isInForLoop = false; |
319
|
|
|
$tokens = $phpcsFile->getTokens(); |
320
|
|
|
$currentTk = $tokens[$stackPtr]; |
321
|
|
|
if (array_key_exists('nested_parenthesis', $currentTk)) { |
322
|
|
|
$nestedParenthesis = $currentTk['nested_parenthesis']; |
323
|
|
|
foreach ( $nestedParenthesis as $openParPtr => $closeParPtr) { |
324
|
|
|
$nonWhitspacePtr = $phpcsFile->findPrevious( |
325
|
|
|
array(T_WHITESPACE), |
326
|
|
|
$openParPtr - 1, |
327
|
|
|
null, |
328
|
|
|
true, |
329
|
|
|
null, |
330
|
|
|
true |
331
|
|
|
); |
332
|
|
|
if (false !== $nonWhitspacePtr) { |
333
|
|
|
$isFor = T_FOR === $tokens[$nonWhitspacePtr]['code']; |
334
|
|
|
if ($isFor) { |
335
|
|
|
$isInForLoop = $nonWhitspacePtr; |
336
|
|
|
break; |
337
|
|
|
} |
338
|
|
|
} |
339
|
|
|
} |
340
|
|
|
} |
341
|
|
|
return $isInForLoop; |
342
|
|
|
}//end _isInForLoopHead() |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Returns the position of closest previous T_FOR, if token associated with |
346
|
|
|
* $stackPtr in $phpcsFile is in the body of a for loop, otherwise false. |
347
|
|
|
* The body are the instructions placed after parenthesis of a 'for' |
348
|
|
|
* declaration, enclosed with curly brackets usually. |
349
|
|
|
* 'for' : for (<loop_head>) {<loop_body>}. |
350
|
|
|
* |
351
|
|
|
* @param File $phpcsFile The current file being processed. |
352
|
|
|
* @param int $stackPtr The position of the current token |
353
|
|
|
* in the stack passed in $tokens. |
354
|
|
|
* |
355
|
|
|
* @return int|bool Position of T_FOR if token associated with $stackPtr in |
356
|
|
|
* $phpcsFile is in the body of a for loop, otherwise false. |
357
|
|
|
*/ |
358
|
|
|
private static function _isInForLoopBody(File $phpcsFile, $stackPtr) |
359
|
|
|
{ |
360
|
|
|
$isInForLoop = false; |
|
|
|
|
361
|
|
|
$tokens = $phpcsFile->getTokens(); |
362
|
|
|
// get englobing hierarchy |
363
|
|
|
$parentPtrAndCode = $tokens[$stackPtr]['conditions']; |
364
|
|
|
krsort($parentPtrAndCode); |
365
|
|
|
|
366
|
|
|
// looks for a for loop having a body not enclosed with curly brackets, |
367
|
|
|
// which involves that its body contains only one instruction. |
368
|
|
|
if (is_array($parentPtrAndCode) && ! empty($parentPtrAndCode)) { |
369
|
|
|
$parentCode = reset($parentPtrAndCode); |
370
|
|
|
$parentPtr = key($parentPtrAndCode); |
371
|
|
|
$openBracketPtr = $tokens[$parentPtr]['scope_opener']; |
372
|
|
|
} else { |
373
|
|
|
$parentCode = 0; |
374
|
|
|
$parentPtr = 0; |
375
|
|
|
$openBracketPtr = 0; |
376
|
|
|
} |
377
|
|
|
$openResearchScopePtr = $stackPtr; |
378
|
|
|
// recursive search, since a for statement may englobe other inline |
379
|
|
|
// control statement or may be near to function calls, etc... |
380
|
|
|
while (false !== $openResearchScopePtr) { |
381
|
|
|
$closeParPtr = $phpcsFile->findPrevious( |
382
|
|
|
array(T_CLOSE_PARENTHESIS), |
383
|
|
|
$openResearchScopePtr, |
384
|
|
|
null, |
385
|
|
|
false, |
386
|
|
|
null, |
387
|
|
|
true |
388
|
|
|
); |
389
|
|
|
// is there a closing parenthesis with a control statement before |
390
|
|
|
// the previous instruction ? |
391
|
|
|
if (false !== $closeParPtr) { |
392
|
|
|
// is there no opening curly bracket specific to |
393
|
|
|
// set of instructions, between the closing parenthesis |
394
|
|
|
// and the current token ? |
395
|
|
|
if ($openBracketPtr < $closeParPtr) { |
396
|
|
|
// starts the search from the token before the closing |
397
|
|
|
// parenthesis, if it isn't a for statement |
398
|
|
|
$openResearchScopePtr = $closeParPtr - 1; |
399
|
|
|
// is this parenthesis associated with a for statement ? |
400
|
|
|
$closeParenthesisTk = $tokens[$closeParPtr]; |
401
|
|
|
if (array_key_exists('parenthesis_owner', $closeParenthesisTk)) { |
402
|
|
|
$mayBeForPtr = $closeParenthesisTk['parenthesis_owner']; |
403
|
|
|
$mayBeForTk = $tokens[$mayBeForPtr]; |
404
|
|
|
if (T_FOR === $mayBeForTk['code']) { |
405
|
|
|
return $mayBeForPtr; |
406
|
|
|
} |
407
|
|
|
} |
408
|
|
|
} else { |
409
|
|
|
// if it is about a for loop, don't go further |
410
|
|
|
// and detect it after one more loop execution, do it now |
411
|
|
|
if (T_FOR === $parentCode) { |
412
|
|
|
return $parentPtr; |
413
|
|
|
} |
414
|
|
|
// starts the search from the token before the one |
415
|
|
|
// englobing the current statement |
416
|
|
|
$openResearchScopePtr = $parentPtr - 1; |
417
|
|
|
// re-initialize variables about the englobing structure |
418
|
|
|
if (is_array($parentPtrAndCode)) { |
419
|
|
|
$parentCode = next($parentPtrAndCode); |
420
|
|
|
$parentPtr = key($parentPtrAndCode); |
421
|
|
|
$openBracketPtr = $tokens[$parentPtr]['scope_opener']; |
422
|
|
|
} |
423
|
|
|
} |
424
|
|
|
} else { |
425
|
|
|
$openResearchScopePtr = false; |
426
|
|
|
} |
427
|
|
|
} |
428
|
|
|
// looks for a for loop having a body enclosed with curly brackets |
429
|
|
|
foreach ($parentPtrAndCode as $parentPtr => $parentCode) { |
430
|
|
|
if (T_FOR === $parentCode) { |
431
|
|
|
return $parentPtr; |
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
return false; |
435
|
|
|
}//end _isInForLoopBody() |
436
|
|
|
|
437
|
|
|
/** |
438
|
|
|
* Returns true if a variable declared in the head of the for loop pointed |
439
|
|
|
* by $forPtr in file $phpcsFile has the name $varName. |
440
|
|
|
* |
441
|
|
|
* @param File $phpcsFile The current file being processed. |
442
|
|
|
* @param int $forPtr The position of the 'for' token |
443
|
|
|
* in the stack passed in $tokens. |
444
|
|
|
* @param string $varName The name of the variable to |
445
|
|
|
* procced without $, { nor }. |
446
|
|
|
* |
447
|
|
|
* @return int|bool true if a variable declared in the head of the for loop |
448
|
|
|
* pointed by $forPtr in file $phpcsFile has the name $varName. |
449
|
|
|
*/ |
450
|
|
|
private static function _isDeclaredInForLoop(File $phpcsFile, $forPtr, $varName) |
451
|
|
|
{ |
452
|
|
|
$isDeclaredInFor = false; |
453
|
|
|
$tokens = $phpcsFile->getTokens(); |
454
|
|
|
$forVarPtrs = self::_getVarDeclaredInFor($phpcsFile, $forPtr); |
455
|
|
|
foreach ($forVarPtrs as $forVarPtr) { |
456
|
|
|
$forVarTk = $tokens[$forVarPtr]; |
457
|
|
|
// get variable name |
458
|
|
|
$matches = array(); |
459
|
|
|
preg_match('/^\$\{?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}?/', $forVarTk['content'], $matches); |
460
|
|
|
$forVarName = $matches[1]; |
461
|
|
|
if (0 === strcmp($forVarName, $varName)) { |
462
|
|
|
$isDeclaredInFor = $forVarPtr; |
463
|
|
|
break; |
464
|
|
|
} |
465
|
|
|
} |
466
|
|
|
return $isDeclaredInFor; |
467
|
|
|
}//end _isDeclaredInForLoop() |
468
|
|
|
|
469
|
|
|
/** |
470
|
|
|
* Returns list of pointers to variables declared in for loop associated to |
471
|
|
|
* $forPtr in file $phpcsFile. |
472
|
|
|
* |
473
|
|
|
* All pointers in the result list are pointing to token with code |
474
|
|
|
* T_VARIABLE. An exception is raised, if $forPtr doesn't point a token with |
475
|
|
|
* code T_FOR. |
476
|
|
|
* |
477
|
|
|
* @param File $phpcsFile The current file being processed. |
478
|
|
|
* @param int $forPtr The position of the current token |
479
|
|
|
* in the stack passed in $tokens. |
480
|
|
|
* |
481
|
|
|
* @return array List of pointers to variables declared in for loop $forPtr. |
482
|
|
|
*/ |
483
|
|
|
private static function _getVarDeclaredInFor(File $phpcsFile, $forPtr) |
484
|
|
|
{ |
485
|
|
|
$tokens = $phpcsFile->getTokens(); |
486
|
|
|
$forTk = $tokens[$forPtr]; |
487
|
|
|
if (T_FOR !== $forTk['code']) { |
488
|
|
|
throw new PHP_CodeSniffer_Exception('$forPtr must be of type T_FOR'); |
489
|
|
|
} |
490
|
|
|
$openParPtr = $forTk['parenthesis_opener']; |
491
|
|
|
$openParenthesisTk = $tokens[$openParPtr]; |
|
|
|
|
492
|
|
|
$endOfDeclPtr = $phpcsFile->findNext(array(T_SEMICOLON), $openParPtr); |
493
|
|
|
$forVarPtrs = array(); |
494
|
|
|
$varPtr = $phpcsFile->findNext( |
495
|
|
|
array(T_VARIABLE), |
496
|
|
|
$openParPtr + 1, |
497
|
|
|
$endOfDeclPtr |
|
|
|
|
498
|
|
|
); |
499
|
|
|
while (false !== $varPtr) { |
500
|
|
|
$forVarPtrs [] = $varPtr; |
501
|
|
|
$varPtr = $phpcsFile->findNext( |
502
|
|
|
array(T_VARIABLE), |
503
|
|
|
$varPtr + 1, |
504
|
|
|
$endOfDeclPtr |
|
|
|
|
505
|
|
|
); |
506
|
|
|
} |
507
|
|
|
return $forVarPtrs; |
508
|
|
|
}//end _getVarDeclaredInFor() |
509
|
|
|
|
510
|
|
|
/** |
511
|
|
|
* Returns the position of first occurrence of a PHP variable starting with |
512
|
|
|
* $ in $haystack from $offset. |
513
|
|
|
* |
514
|
|
|
* @param string $haystack The string to search in. |
515
|
|
|
* @param int $offset The optional offset parameter allows you to |
516
|
|
|
* specify which character in haystack to start |
517
|
|
|
* searching. The returned position is still |
518
|
|
|
* relative to the beginning of haystack. |
519
|
|
|
* |
520
|
|
|
* @return mixed The position as an integer |
521
|
|
|
* or the boolean false, if no variable is found. |
522
|
|
|
*/ |
523
|
|
|
private static function _getVariablePosition($haystack, $offset = 0) |
524
|
|
|
{ |
525
|
|
|
$var_starts_at = strpos($haystack, '$', $offset); |
526
|
|
|
$is_a_var = false; |
527
|
|
|
while (false !== $var_starts_at && ! $is_a_var) { |
528
|
|
|
// makes sure that $ is used for a variable and not as a symbol, |
529
|
|
|
// if $ is protected with the escape char, then it is a symbol. |
530
|
|
|
if (0 !== strcmp($haystack[$var_starts_at - 1], '\\')) { |
531
|
|
|
if (0 === strcmp($haystack[$var_starts_at + 1], '{')) { |
532
|
|
|
// there is an opening brace in the right place |
533
|
|
|
// so it looks for the closing brace in the right place |
534
|
|
|
$hsChunk2 = substr($haystack, $var_starts_at + 2); |
535
|
|
|
if (1 === preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\}/', $hsChunk2)) { |
536
|
|
|
$is_a_var = true; |
537
|
|
|
} |
538
|
|
|
} else { |
539
|
|
|
$hsChunk1 = substr($haystack, $var_starts_at + 1); |
540
|
|
|
if (1 === preg_match('/^[a-zA-Z_\x7f-\xff]/', $hsChunk1)) { |
541
|
|
|
// $ is used for a variable and not as a symbol, |
542
|
|
|
// since what follows $ matchs the definition of |
543
|
|
|
// a variable label for PHP. |
544
|
|
|
$is_a_var = true; |
545
|
|
|
} |
546
|
|
|
} |
547
|
|
|
} |
548
|
|
|
// update $var_starts_at for the next variable |
549
|
|
|
// only if no variable was found, since it is returned otherwise. |
550
|
|
|
if ( ! $is_a_var) { |
551
|
|
|
$var_starts_at = strpos($haystack, '$', $var_starts_at + 1); |
552
|
|
|
} |
553
|
|
|
} |
554
|
|
|
if ($is_a_var) { |
555
|
|
|
return $var_starts_at; |
556
|
|
|
} else { |
557
|
|
|
return false; |
558
|
|
|
} |
559
|
|
|
}//end _getVariablePosition() |
560
|
|
|
}//end class |
561
|
|
|
|
562
|
|
|
?> |
|
|
|
|
563
|
|
|
|
Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.