Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@0
|
3 * This file is part of the VariableAnalysis addon for PHP_CodeSniffer.
|
Chris@0
|
4 *
|
Chris@0
|
5 * @category PHP
|
Chris@0
|
6 * @package PHP_CodeSniffer
|
Chris@0
|
7 * @author Sam Graham <php-codesniffer-variableanalysis BLAHBLAH illusori.co.uk>
|
Chris@0
|
8 * @copyright 2011-2012 Sam Graham <php-codesniffer-variableanalysis BLAHBLAH illusori.co.uk>
|
Chris@0
|
9 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
Chris@0
|
10 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
11 */
|
Chris@0
|
12
|
Chris@0
|
13 /**
|
Chris@0
|
14 * Holds details of a scope.
|
Chris@0
|
15 *
|
Chris@0
|
16 * @category PHP
|
Chris@0
|
17 * @package PHP_CodeSniffer
|
Chris@0
|
18 * @author Sam Graham <php-codesniffer-variableanalysis BLAHBLAH illusori.co.uk>
|
Chris@0
|
19 * @copyright 2011-2012 Sam Graham <php-codesniffer-plugins BLAHBLAH illusori.co.uk>
|
Chris@0
|
20 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
21 */
|
Chris@0
|
22 class ScopeInfo
|
Chris@0
|
23 {
|
Chris@0
|
24 public $owner;
|
Chris@0
|
25 public $opener;
|
Chris@0
|
26 public $closer;
|
Chris@0
|
27 public $variables = array();
|
Chris@0
|
28
|
Chris@0
|
29
|
Chris@0
|
30 /**
|
Chris@0
|
31 * Constructor.
|
Chris@0
|
32 *
|
Chris@0
|
33 * @param int $currScope
|
Chris@0
|
34 */
|
Chris@0
|
35 function __construct($currScope)
|
Chris@0
|
36 {
|
Chris@0
|
37 // TODO: extract opener/closer.
|
Chris@0
|
38 $this->owner = $currScope;
|
Chris@0
|
39
|
Chris@0
|
40 }//end __construct()
|
Chris@0
|
41
|
Chris@0
|
42
|
Chris@0
|
43 }//end class
|
Chris@0
|
44
|
Chris@0
|
45
|
Chris@0
|
46 /**
|
Chris@0
|
47 * Holds details of a variable within a scope.
|
Chris@0
|
48 *
|
Chris@0
|
49 * @category PHP
|
Chris@0
|
50 * @package PHP_CodeSniffer
|
Chris@0
|
51 * @author Sam Graham <php-codesniffer-variableanalysis BLAHBLAH illusori.co.uk>
|
Chris@0
|
52 * @copyright 2011 Sam Graham <php-codesniffer-variableanalysis BLAHBLAH illusori.co.uk>
|
Chris@0
|
53 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
54 */
|
Chris@0
|
55 class VariableInfo
|
Chris@0
|
56 {
|
Chris@0
|
57 public $name;
|
Chris@0
|
58 /**
|
Chris@0
|
59 * What scope the variable has: local, param, static, global, bound
|
Chris@0
|
60 */
|
Chris@0
|
61 public $scopeType;
|
Chris@0
|
62 public $typeHint;
|
Chris@0
|
63 public $passByReference = false;
|
Chris@0
|
64 public $firstDeclared;
|
Chris@0
|
65 public $firstInitialized;
|
Chris@0
|
66 public $firstRead;
|
Chris@0
|
67 public $ignoreUnused = false;
|
Chris@0
|
68 public $lastAssignment;
|
Chris@0
|
69
|
Chris@0
|
70 static $scopeTypeDescriptions = array(
|
Chris@0
|
71 'local' => 'variable',
|
Chris@0
|
72 'param' => 'function parameter',
|
Chris@0
|
73 'static' => 'static variable',
|
Chris@0
|
74 'global' => 'global variable',
|
Chris@0
|
75 'bound' => 'bound variable',
|
Chris@0
|
76 );
|
Chris@0
|
77
|
Chris@0
|
78
|
Chris@0
|
79 /**
|
Chris@0
|
80 * Constructor.
|
Chris@0
|
81 *
|
Chris@0
|
82 * @param string $varName
|
Chris@0
|
83 */
|
Chris@0
|
84 function __construct($varName)
|
Chris@0
|
85 {
|
Chris@0
|
86 $this->name = $varName;
|
Chris@0
|
87
|
Chris@0
|
88 }//end __construct()
|
Chris@0
|
89
|
Chris@0
|
90
|
Chris@0
|
91 }//end class
|
Chris@0
|
92
|
Chris@0
|
93
|
Chris@0
|
94 /**
|
Chris@0
|
95 * Checks the for undefined function variables.
|
Chris@0
|
96 *
|
Chris@0
|
97 * This sniff checks that all function variables
|
Chris@0
|
98 * are defined in the function body.
|
Chris@0
|
99 *
|
Chris@0
|
100 * @category PHP
|
Chris@0
|
101 * @package PHP_CodeSniffer
|
Chris@0
|
102 * @author Sam Graham <php-codesniffer-variableanalysis BLAHBLAH illusori.co.uk>
|
Chris@0
|
103 * @copyright 2011 Sam Graham <php-codesniffer-variableanalysis BLAHBLAH illusori.co.uk>
|
Chris@0
|
104 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
105 */
|
Chris@0
|
106 class DrupalPractice_Sniffs_CodeAnalysis_VariableAnalysisSniff implements PHP_CodeSniffer_Sniff
|
Chris@0
|
107 {
|
Chris@0
|
108 /**
|
Chris@0
|
109 * The current phpcsFile being checked.
|
Chris@0
|
110 *
|
Chris@0
|
111 * @var phpcsFile
|
Chris@0
|
112 */
|
Chris@0
|
113 protected $currentFile = null;
|
Chris@0
|
114
|
Chris@0
|
115 /**
|
Chris@0
|
116 * A list of scopes encountered so far and the variables within them.
|
Chris@0
|
117 */
|
Chris@0
|
118 private $_scopes = array();
|
Chris@0
|
119
|
Chris@0
|
120 /**
|
Chris@0
|
121 * A regexp for matching variable names in double-quoted strings.
|
Chris@0
|
122 */
|
Chris@0
|
123 private $_double_quoted_variable_regexp = '|(?<!\\\\)(?:\\\\{2})*\${?([a-zA-Z0-9_]+)}?|';
|
Chris@0
|
124
|
Chris@0
|
125 /**
|
Chris@0
|
126 * Array of known pass-by-reference functions and the argument(s) which are passed
|
Chris@0
|
127 * by reference, the arguments are numbered starting from 1 and an elipsis '...'
|
Chris@0
|
128 * means all argument numbers after the previous should be considered pass-by-reference.
|
Chris@0
|
129 */
|
Chris@0
|
130 private $_passByRefFunctions = array(
|
Chris@0
|
131 '__soapCall' => array(5),
|
Chris@0
|
132 'addFunction' => array(3),
|
Chris@0
|
133 'addTask' => array(3),
|
Chris@0
|
134 'addTaskBackground' => array(3),
|
Chris@0
|
135 'addTaskHigh' => array(3),
|
Chris@0
|
136 'addTaskHighBackground' => array(3),
|
Chris@0
|
137 'addTaskLow' => array(3),
|
Chris@0
|
138 'addTaskLowBackground' => array(3),
|
Chris@0
|
139 'addTaskStatus' => array(2),
|
Chris@0
|
140 'apc_dec' => array(3),
|
Chris@0
|
141 'apc_fetch' => array(2),
|
Chris@0
|
142 'apc_inc' => array(3),
|
Chris@0
|
143 'areConfusable' => array(3),
|
Chris@0
|
144 'array_multisort' => array(1),
|
Chris@0
|
145 'array_pop' => array(1),
|
Chris@0
|
146 'array_push' => array(1),
|
Chris@0
|
147 'array_replace' => array(1),
|
Chris@0
|
148 'array_replace_recursive' => array(
|
Chris@0
|
149 1,
|
Chris@0
|
150 2,
|
Chris@0
|
151 3,
|
Chris@0
|
152 '...',
|
Chris@0
|
153 ),
|
Chris@0
|
154 'array_shift' => array(1),
|
Chris@0
|
155 'array_splice' => array(1),
|
Chris@0
|
156 'array_unshift' => array(1),
|
Chris@0
|
157 'array_walk' => array(1),
|
Chris@0
|
158 'array_walk_recursive' => array(1),
|
Chris@0
|
159 'arsort' => array(1),
|
Chris@0
|
160 'asort' => array(1),
|
Chris@0
|
161 'asort' => array(1),
|
Chris@0
|
162 'bindColumn' => array(2),
|
Chris@0
|
163 'bindParam' => array(2),
|
Chris@0
|
164 'bind_param' => array(
|
Chris@0
|
165 2,
|
Chris@0
|
166 3,
|
Chris@0
|
167 '...',
|
Chris@0
|
168 ),
|
Chris@0
|
169 'bind_result' => array(
|
Chris@0
|
170 1,
|
Chris@0
|
171 2,
|
Chris@0
|
172 '...',
|
Chris@0
|
173 ),
|
Chris@0
|
174 'call_user_method' => array(2),
|
Chris@0
|
175 'call_user_method_array' => array(2),
|
Chris@0
|
176 'curl_multi_exec' => array(2),
|
Chris@0
|
177 'curl_multi_info_read' => array(2),
|
Chris@0
|
178 'current' => array(1),
|
Chris@0
|
179 'dbplus_curr' => array(2),
|
Chris@0
|
180 'dbplus_first' => array(2),
|
Chris@0
|
181 'dbplus_info' => array(3),
|
Chris@0
|
182 'dbplus_last' => array(2),
|
Chris@0
|
183 'dbplus_next' => array(2),
|
Chris@0
|
184 'dbplus_prev' => array(2),
|
Chris@0
|
185 'dbplus_tremove' => array(3),
|
Chris@0
|
186 'dns_get_record' => array(
|
Chris@0
|
187 3,
|
Chris@0
|
188 4,
|
Chris@0
|
189 ),
|
Chris@0
|
190 'domxml_open_file' => array(3),
|
Chris@0
|
191 'domxml_open_mem' => array(3),
|
Chris@0
|
192 'each' => array(1),
|
Chris@0
|
193 'enchant_dict_quick_check' => array(3),
|
Chris@0
|
194 'end' => array(1),
|
Chris@0
|
195 'ereg' => array(3),
|
Chris@0
|
196 'eregi' => array(3),
|
Chris@0
|
197 'exec' => array(
|
Chris@0
|
198 2,
|
Chris@0
|
199 3,
|
Chris@0
|
200 ),
|
Chris@0
|
201 'exif_thumbnail' => array(
|
Chris@0
|
202 1,
|
Chris@0
|
203 2,
|
Chris@0
|
204 3,
|
Chris@0
|
205 ),
|
Chris@0
|
206 'expect_expectl' => array(3),
|
Chris@0
|
207 'extract' => array(1),
|
Chris@0
|
208 'filter' => array(3),
|
Chris@0
|
209 'flock' => array(
|
Chris@0
|
210 2,
|
Chris@0
|
211 3,
|
Chris@0
|
212 ),
|
Chris@0
|
213 'fscanf' => array(
|
Chris@0
|
214 2,
|
Chris@0
|
215 3,
|
Chris@0
|
216 '...',
|
Chris@0
|
217 ),
|
Chris@0
|
218 'fsockopen' => array(
|
Chris@0
|
219 3,
|
Chris@0
|
220 4,
|
Chris@0
|
221 ),
|
Chris@0
|
222 'ftp_alloc' => array(3),
|
Chris@0
|
223 'get' => array(
|
Chris@0
|
224 2,
|
Chris@0
|
225 3,
|
Chris@0
|
226 ),
|
Chris@0
|
227 'getByKey' => array(4),
|
Chris@0
|
228 'getMulti' => array(2),
|
Chris@0
|
229 'getMultiByKey' => array(3),
|
Chris@0
|
230 'getimagesize' => array(2),
|
Chris@0
|
231 'getmxrr' => array(
|
Chris@0
|
232 2,
|
Chris@0
|
233 3,
|
Chris@0
|
234 ),
|
Chris@0
|
235 'gnupg_decryptverify' => array(3),
|
Chris@0
|
236 'gnupg_verify' => array(4),
|
Chris@0
|
237 'grapheme_extract' => array(5),
|
Chris@0
|
238 'headers_sent' => array(
|
Chris@0
|
239 1,
|
Chris@0
|
240 2,
|
Chris@0
|
241 ),
|
Chris@0
|
242 'http_build_url' => array(4),
|
Chris@0
|
243 'http_get' => array(3),
|
Chris@0
|
244 'http_head' => array(3),
|
Chris@0
|
245 'http_negotiate_charset' => array(2),
|
Chris@0
|
246 'http_negotiate_content_type' => array(2),
|
Chris@0
|
247 'http_negotiate_language' => array(2),
|
Chris@0
|
248 'http_post_data' => array(4),
|
Chris@0
|
249 'http_post_fields' => array(5),
|
Chris@0
|
250 'http_put_data' => array(4),
|
Chris@0
|
251 'http_put_file' => array(4),
|
Chris@0
|
252 'http_put_stream' => array(4),
|
Chris@0
|
253 'http_request' => array(5),
|
Chris@0
|
254 'isSuspicious' => array(2),
|
Chris@0
|
255 'is_callable' => array(3),
|
Chris@0
|
256 'key' => array(1),
|
Chris@0
|
257 'krsort' => array(1),
|
Chris@0
|
258 'ksort' => array(1),
|
Chris@0
|
259 'ldap_get_option' => array(3),
|
Chris@0
|
260 'ldap_parse_reference' => array(3),
|
Chris@0
|
261 'ldap_parse_result' => array(
|
Chris@0
|
262 3,
|
Chris@0
|
263 4,
|
Chris@0
|
264 5,
|
Chris@0
|
265 6,
|
Chris@0
|
266 ),
|
Chris@0
|
267 'localtime' => array(2),
|
Chris@0
|
268 'm_completeauthorizations' => array(2),
|
Chris@0
|
269 'maxdb_stmt_bind_param' => array(
|
Chris@0
|
270 3,
|
Chris@0
|
271 4,
|
Chris@0
|
272 '...',
|
Chris@0
|
273 ),
|
Chris@0
|
274 'maxdb_stmt_bind_result' => array(
|
Chris@0
|
275 2,
|
Chris@0
|
276 3,
|
Chris@0
|
277 '...',
|
Chris@0
|
278 ),
|
Chris@0
|
279 'mb_convert_variables' => array(
|
Chris@0
|
280 3,
|
Chris@0
|
281 4,
|
Chris@0
|
282 '...',
|
Chris@0
|
283 ),
|
Chris@0
|
284 'mb_parse_str' => array(2),
|
Chris@0
|
285 'mqseries_back' => array(
|
Chris@0
|
286 2,
|
Chris@0
|
287 3,
|
Chris@0
|
288 ),
|
Chris@0
|
289 'mqseries_begin' => array(
|
Chris@0
|
290 3,
|
Chris@0
|
291 4,
|
Chris@0
|
292 ),
|
Chris@0
|
293 'mqseries_close' => array(
|
Chris@0
|
294 4,
|
Chris@0
|
295 5,
|
Chris@0
|
296 ),
|
Chris@0
|
297 'mqseries_cmit' => array(
|
Chris@0
|
298 2,
|
Chris@0
|
299 3,
|
Chris@0
|
300 ),
|
Chris@0
|
301 'mqseries_conn' => array(
|
Chris@0
|
302 2,
|
Chris@0
|
303 3,
|
Chris@0
|
304 4,
|
Chris@0
|
305 ),
|
Chris@0
|
306 'mqseries_connx' => array(
|
Chris@0
|
307 2,
|
Chris@0
|
308 3,
|
Chris@0
|
309 4,
|
Chris@0
|
310 5,
|
Chris@0
|
311 ),
|
Chris@0
|
312 'mqseries_disc' => array(
|
Chris@0
|
313 2,
|
Chris@0
|
314 3,
|
Chris@0
|
315 ),
|
Chris@0
|
316 'mqseries_get' => array(
|
Chris@0
|
317 3,
|
Chris@0
|
318 4,
|
Chris@0
|
319 5,
|
Chris@0
|
320 6,
|
Chris@0
|
321 7,
|
Chris@0
|
322 8,
|
Chris@0
|
323 9,
|
Chris@0
|
324 ),
|
Chris@0
|
325 'mqseries_inq' => array(
|
Chris@0
|
326 6,
|
Chris@0
|
327 8,
|
Chris@0
|
328 9,
|
Chris@0
|
329 10,
|
Chris@0
|
330 ),
|
Chris@0
|
331 'mqseries_open' => array(
|
Chris@0
|
332 2,
|
Chris@0
|
333 4,
|
Chris@0
|
334 5,
|
Chris@0
|
335 6,
|
Chris@0
|
336 ),
|
Chris@0
|
337 'mqseries_put' => array(
|
Chris@0
|
338 3,
|
Chris@0
|
339 4,
|
Chris@0
|
340 6,
|
Chris@0
|
341 7,
|
Chris@0
|
342 ),
|
Chris@0
|
343 'mqseries_put1' => array(
|
Chris@0
|
344 2,
|
Chris@0
|
345 3,
|
Chris@0
|
346 4,
|
Chris@0
|
347 6,
|
Chris@0
|
348 7,
|
Chris@0
|
349 ),
|
Chris@0
|
350 'mqseries_set' => array(
|
Chris@0
|
351 9,
|
Chris@0
|
352 10,
|
Chris@0
|
353 ),
|
Chris@0
|
354 'msg_receive' => array(
|
Chris@0
|
355 3,
|
Chris@0
|
356 5,
|
Chris@0
|
357 8,
|
Chris@0
|
358 ),
|
Chris@0
|
359 'msg_send' => array(6),
|
Chris@0
|
360 'mssql_bind' => array(3),
|
Chris@0
|
361 'natcasesort' => array(1),
|
Chris@0
|
362 'natsort' => array(1),
|
Chris@0
|
363 'ncurses_color_content' => array(
|
Chris@0
|
364 2,
|
Chris@0
|
365 3,
|
Chris@0
|
366 4,
|
Chris@0
|
367 ),
|
Chris@0
|
368 'ncurses_getmaxyx' => array(
|
Chris@0
|
369 2,
|
Chris@0
|
370 3,
|
Chris@0
|
371 ),
|
Chris@0
|
372 'ncurses_getmouse' => array(1),
|
Chris@0
|
373 'ncurses_getyx' => array(
|
Chris@0
|
374 2,
|
Chris@0
|
375 3,
|
Chris@0
|
376 ),
|
Chris@0
|
377 'ncurses_instr' => array(1),
|
Chris@0
|
378 'ncurses_mouse_trafo' => array(
|
Chris@0
|
379 1,
|
Chris@0
|
380 2,
|
Chris@0
|
381 ),
|
Chris@0
|
382 'ncurses_mousemask' => array(2),
|
Chris@0
|
383 'ncurses_pair_content' => array(
|
Chris@0
|
384 2,
|
Chris@0
|
385 3,
|
Chris@0
|
386 ),
|
Chris@0
|
387 'ncurses_wmouse_trafo' => array(
|
Chris@0
|
388 2,
|
Chris@0
|
389 3,
|
Chris@0
|
390 ),
|
Chris@0
|
391 'newt_button_bar' => array(1),
|
Chris@0
|
392 'newt_form_run' => array(2),
|
Chris@0
|
393 'newt_get_screen_size' => array(
|
Chris@0
|
394 1,
|
Chris@0
|
395 2,
|
Chris@0
|
396 ),
|
Chris@0
|
397 'newt_grid_get_size' => array(
|
Chris@0
|
398 2,
|
Chris@0
|
399 3,
|
Chris@0
|
400 ),
|
Chris@0
|
401 'newt_reflow_text' => array(
|
Chris@0
|
402 5,
|
Chris@0
|
403 6,
|
Chris@0
|
404 ),
|
Chris@0
|
405 'newt_win_entries' => array(7),
|
Chris@0
|
406 'newt_win_menu' => array(8),
|
Chris@0
|
407 'next' => array(1),
|
Chris@0
|
408 'oci_bind_array_by_name' => array(3),
|
Chris@0
|
409 'oci_bind_by_name' => array(3),
|
Chris@0
|
410 'oci_define_by_name' => array(3),
|
Chris@0
|
411 'oci_fetch_all' => array(2),
|
Chris@0
|
412 'ocifetchinto' => array(2),
|
Chris@0
|
413 'odbc_fetch_into' => array(2),
|
Chris@0
|
414 'openssl_csr_export' => array(2),
|
Chris@0
|
415 'openssl_csr_new' => array(2),
|
Chris@0
|
416 'openssl_open' => array(2),
|
Chris@0
|
417 'openssl_pkcs12_export' => array(2),
|
Chris@0
|
418 'openssl_pkcs12_read' => array(2),
|
Chris@0
|
419 'openssl_pkey_export' => array(2),
|
Chris@0
|
420 'openssl_private_decrypt' => array(2),
|
Chris@0
|
421 'openssl_private_encrypt' => array(2),
|
Chris@0
|
422 'openssl_public_decrypt' => array(2),
|
Chris@0
|
423 'openssl_public_encrypt' => array(2),
|
Chris@0
|
424 'openssl_random_pseudo_bytes' => array(2),
|
Chris@0
|
425 'openssl_seal' => array(
|
Chris@0
|
426 2,
|
Chris@0
|
427 3,
|
Chris@0
|
428 ),
|
Chris@0
|
429 'openssl_sign' => array(2),
|
Chris@0
|
430 'openssl_x509_export' => array(2),
|
Chris@0
|
431 'ovrimos_fetch_into' => array(2),
|
Chris@0
|
432 'parse' => array(
|
Chris@0
|
433 2,
|
Chris@0
|
434 3,
|
Chris@0
|
435 ),
|
Chris@0
|
436 'parseCurrency' => array(
|
Chris@0
|
437 2,
|
Chris@0
|
438 3,
|
Chris@0
|
439 ),
|
Chris@0
|
440 'parse_str' => array(2),
|
Chris@0
|
441 'parsekit_compile_file' => array(2),
|
Chris@0
|
442 'parsekit_compile_string' => array(2),
|
Chris@0
|
443 'passthru' => array(2),
|
Chris@0
|
444 'pcntl_sigprocmask' => array(3),
|
Chris@0
|
445 'pcntl_sigtimedwait' => array(2),
|
Chris@0
|
446 'pcntl_sigwaitinfo' => array(2),
|
Chris@0
|
447 'pcntl_wait' => array(1),
|
Chris@0
|
448 'pcntl_waitpid' => array(2),
|
Chris@0
|
449 'pfsockopen' => array(
|
Chris@0
|
450 3,
|
Chris@0
|
451 4,
|
Chris@0
|
452 ),
|
Chris@0
|
453 'php_check_syntax' => array(2),
|
Chris@0
|
454 'poll' => array(
|
Chris@0
|
455 1,
|
Chris@0
|
456 2,
|
Chris@0
|
457 3,
|
Chris@0
|
458 ),
|
Chris@0
|
459 'preg_filter' => array(5),
|
Chris@0
|
460 'preg_match' => array(3),
|
Chris@0
|
461 'preg_match_all' => array(3),
|
Chris@0
|
462 'preg_replace' => array(5),
|
Chris@0
|
463 'preg_replace_callback' => array(5),
|
Chris@0
|
464 'prev' => array(1),
|
Chris@0
|
465 'proc_open' => array(3),
|
Chris@0
|
466 'query' => array(3),
|
Chris@0
|
467 'queryExec' => array(2),
|
Chris@0
|
468 'reset' => array(1),
|
Chris@0
|
469 'rsort' => array(1),
|
Chris@0
|
470 'settype' => array(1),
|
Chris@0
|
471 'shuffle' => array(1),
|
Chris@0
|
472 'similar_text' => array(3),
|
Chris@0
|
473 'socket_create_pair' => array(4),
|
Chris@0
|
474 'socket_getpeername' => array(
|
Chris@0
|
475 2,
|
Chris@0
|
476 3,
|
Chris@0
|
477 ),
|
Chris@0
|
478 'socket_getsockname' => array(
|
Chris@0
|
479 2,
|
Chris@0
|
480 3,
|
Chris@0
|
481 ),
|
Chris@0
|
482 'socket_recv' => array(2),
|
Chris@0
|
483 'socket_recvfrom' => array(
|
Chris@0
|
484 2,
|
Chris@0
|
485 5,
|
Chris@0
|
486 6,
|
Chris@0
|
487 ),
|
Chris@0
|
488 'socket_select' => array(
|
Chris@0
|
489 1,
|
Chris@0
|
490 2,
|
Chris@0
|
491 3,
|
Chris@0
|
492 ),
|
Chris@0
|
493 'sort' => array(1),
|
Chris@0
|
494 'sortWithSortKeys' => array(1),
|
Chris@0
|
495 'sqlite_exec' => array(3),
|
Chris@0
|
496 'sqlite_factory' => array(3),
|
Chris@0
|
497 'sqlite_open' => array(3),
|
Chris@0
|
498 'sqlite_popen' => array(3),
|
Chris@0
|
499 'sqlite_query' => array(4),
|
Chris@0
|
500 'sqlite_query' => array(4),
|
Chris@0
|
501 'sqlite_unbuffered_query' => array(4),
|
Chris@0
|
502 'sscanf' => array(
|
Chris@0
|
503 3,
|
Chris@0
|
504 '...',
|
Chris@0
|
505 ),
|
Chris@0
|
506 'str_ireplace' => array(4),
|
Chris@0
|
507 'str_replace' => array(4),
|
Chris@0
|
508 'stream_open' => array(4),
|
Chris@0
|
509 'stream_select' => array(
|
Chris@0
|
510 1,
|
Chris@0
|
511 2,
|
Chris@0
|
512 3,
|
Chris@0
|
513 ),
|
Chris@0
|
514 'stream_socket_accept' => array(3),
|
Chris@0
|
515 'stream_socket_client' => array(
|
Chris@0
|
516 2,
|
Chris@0
|
517 3,
|
Chris@0
|
518 ),
|
Chris@0
|
519 'stream_socket_recvfrom' => array(4),
|
Chris@0
|
520 'stream_socket_server' => array(
|
Chris@0
|
521 2,
|
Chris@0
|
522 3,
|
Chris@0
|
523 ),
|
Chris@0
|
524 'system' => array(2),
|
Chris@0
|
525 'uasort' => array(1),
|
Chris@0
|
526 'uksort' => array(1),
|
Chris@0
|
527 'unbufferedQuery' => array(3),
|
Chris@0
|
528 'usort' => array(1),
|
Chris@0
|
529 'wincache_ucache_dec' => array(3),
|
Chris@0
|
530 'wincache_ucache_get' => array(2),
|
Chris@0
|
531 'wincache_ucache_inc' => array(3),
|
Chris@0
|
532 'xdiff_string_merge3' => array(4),
|
Chris@0
|
533 'xdiff_string_patch' => array(4),
|
Chris@0
|
534 'xml_parse_into_struct' => array(
|
Chris@0
|
535 3,
|
Chris@0
|
536 4,
|
Chris@0
|
537 ),
|
Chris@0
|
538 'xml_set_object' => array(2),
|
Chris@0
|
539 'xmlrpc_decode_request' => array(2),
|
Chris@0
|
540 'xmlrpc_set_type' => array(1),
|
Chris@0
|
541 'xslt_set_object' => array(2),
|
Chris@0
|
542 'yaml_parse' => array(3),
|
Chris@0
|
543 'yaml_parse_file' => array(3),
|
Chris@0
|
544 'yaml_parse_url' => array(3),
|
Chris@0
|
545 'yaz_ccl_parse' => array(3),
|
Chris@0
|
546 'yaz_hits' => array(2),
|
Chris@0
|
547 'yaz_scan_result' => array(2),
|
Chris@0
|
548 'yaz_wait' => array(1),
|
Chris@0
|
549 );
|
Chris@0
|
550
|
Chris@0
|
551 /**
|
Chris@0
|
552 * Allows an install to extend the list of known pass-by-reference functions
|
Chris@0
|
553 * by defining generic.codeanalysis.variableanalysis.sitePassByRefFunctions.
|
Chris@0
|
554 */
|
Chris@0
|
555 public $sitePassByRefFunctions = null;
|
Chris@0
|
556
|
Chris@0
|
557 /**
|
Chris@0
|
558 * Allows exceptions in a catch block to be unused without provoking unused-var warning.
|
Chris@0
|
559 * Set generic.codeanalysis.variableanalysis.allowUnusedCaughtExceptions to a true value.
|
Chris@0
|
560 */
|
Chris@0
|
561 public $allowUnusedCaughtExceptions = true;
|
Chris@0
|
562
|
Chris@0
|
563 /**
|
Chris@0
|
564 * Allow function parameters to be unused without provoking unused-var warning.
|
Chris@0
|
565 * Set generic.codeanalysis.variableanalysis.allowUnusedFunctionParameters to a true value.
|
Chris@0
|
566 */
|
Chris@0
|
567 public $allowUnusedFunctionParameters = true;
|
Chris@0
|
568
|
Chris@0
|
569 /**
|
Chris@0
|
570 * A list of names of placeholder variables that you want to ignore from
|
Chris@0
|
571 * unused variable warnings, ie things like $junk.
|
Chris@0
|
572 */
|
Chris@0
|
573 public $validUnusedVariableNames = null;
|
Chris@0
|
574
|
Chris@0
|
575
|
Chris@0
|
576 /**
|
Chris@0
|
577 * Returns an array of tokens this test wants to listen for.
|
Chris@0
|
578 *
|
Chris@0
|
579 * @return array
|
Chris@0
|
580 */
|
Chris@0
|
581 public function register()
|
Chris@0
|
582 {
|
Chris@0
|
583 // Magic to modfy $_passByRefFunctions with any site-specific settings.
|
Chris@0
|
584 if (empty($this->sitePassByRefFunctions) === false) {
|
Chris@0
|
585 foreach (preg_split('/\s+/', trim($this->sitePassByRefFunctions)) as $line) {
|
Chris@0
|
586 list ($function, $args) = explode(':', $line);
|
Chris@0
|
587 $this->_passByRefFunctions[$function] = explode(',', $args);
|
Chris@0
|
588 }
|
Chris@0
|
589 }
|
Chris@0
|
590
|
Chris@0
|
591 if (empty($this->validUnusedVariableNames) === false) {
|
Chris@0
|
592 $this->validUnusedVariableNames = preg_split('/\s+/', trim($this->validUnusedVariableNames));
|
Chris@0
|
593 }
|
Chris@0
|
594
|
Chris@0
|
595 return array(
|
Chris@0
|
596 T_VARIABLE,
|
Chris@0
|
597 T_DOUBLE_QUOTED_STRING,
|
Chris@0
|
598 T_HEREDOC,
|
Chris@0
|
599 T_CLOSE_CURLY_BRACKET,
|
Chris@0
|
600 T_STRING,
|
Chris@0
|
601 );
|
Chris@0
|
602
|
Chris@0
|
603 }//end register()
|
Chris@0
|
604
|
Chris@0
|
605
|
Chris@0
|
606 /**
|
Chris@0
|
607 * Processes this test, when one of its tokens is encountered.
|
Chris@0
|
608 *
|
Chris@0
|
609 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
Chris@0
|
610 * @param int $stackPtr The position of the current token
|
Chris@0
|
611 * in the stack passed in $tokens.
|
Chris@0
|
612 *
|
Chris@0
|
613 * @return void
|
Chris@0
|
614 */
|
Chris@0
|
615 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
|
Chris@0
|
616 {
|
Chris@0
|
617 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
618 $token = $tokens[$stackPtr];
|
Chris@0
|
619
|
Chris@0
|
620 // Debug code.
|
Chris@0
|
621 // if ($token['content'] == '$param') {
|
Chris@0
|
622 // echo "Found token on line {$token['line']}.\n" . print_r($token, true);
|
Chris@0
|
623 // }
|
Chris@0
|
624 // End: Debug code.
|
Chris@0
|
625 if ($this->currentFile !== $phpcsFile) {
|
Chris@0
|
626 $this->currentFile = $phpcsFile;
|
Chris@0
|
627 }
|
Chris@0
|
628
|
Chris@0
|
629 if ($token['code'] === T_VARIABLE) {
|
Chris@0
|
630 return $this->processVariable($phpcsFile, $stackPtr);
|
Chris@0
|
631 }
|
Chris@0
|
632
|
Chris@0
|
633 if (($token['code'] === T_DOUBLE_QUOTED_STRING)
|
Chris@0
|
634 || ($token['code'] === T_HEREDOC)
|
Chris@0
|
635 ) {
|
Chris@0
|
636 return $this->processVariableInString($phpcsFile, $stackPtr);
|
Chris@0
|
637 }
|
Chris@0
|
638
|
Chris@0
|
639 if (($token['code'] === T_STRING) && ($token['content'] === 'compact')) {
|
Chris@0
|
640 return $this->processCompact($phpcsFile, $stackPtr);
|
Chris@0
|
641 }
|
Chris@0
|
642
|
Chris@0
|
643 if (($token['code'] === T_CLOSE_CURLY_BRACKET)
|
Chris@0
|
644 && isset($token['scope_condition']) === true
|
Chris@0
|
645 ) {
|
Chris@0
|
646 return $this->processScopeClose($phpcsFile, $token['scope_condition']);
|
Chris@0
|
647 }
|
Chris@0
|
648
|
Chris@0
|
649 }//end process()
|
Chris@0
|
650
|
Chris@0
|
651
|
Chris@0
|
652 /**
|
Chris@0
|
653 * Remove special characters from the variable name.
|
Chris@0
|
654 *
|
Chris@0
|
655 * @param string $varName
|
Chris@0
|
656 *
|
Chris@0
|
657 * @return string
|
Chris@0
|
658 */
|
Chris@0
|
659 function normalizeVarName($varName)
|
Chris@0
|
660 {
|
Chris@0
|
661 $varName = preg_replace('/[{}$]/', '', $varName);
|
Chris@0
|
662 return $varName;
|
Chris@0
|
663
|
Chris@0
|
664 }//end normalizeVarName()
|
Chris@0
|
665
|
Chris@0
|
666
|
Chris@0
|
667 /**
|
Chris@0
|
668 * Generate a scope key based on the current file.
|
Chris@0
|
669 *
|
Chris@0
|
670 * @param string $currScope
|
Chris@0
|
671 *
|
Chris@0
|
672 * @return string
|
Chris@0
|
673 */
|
Chris@0
|
674 function scopeKey($currScope)
|
Chris@0
|
675 {
|
Chris@0
|
676 if ($currScope === false) {
|
Chris@0
|
677 $currScope = 'file';
|
Chris@0
|
678 }
|
Chris@0
|
679
|
Chris@0
|
680 if (isset($this->currentFile) === true) {
|
Chris@0
|
681 return ($this->currentFile->getFilename()).':'.$currScope;
|
Chris@0
|
682 } else {
|
Chris@0
|
683 return ('unknown file').':'.$currScope;
|
Chris@0
|
684 }
|
Chris@0
|
685
|
Chris@0
|
686 }//end scopeKey()
|
Chris@0
|
687
|
Chris@0
|
688
|
Chris@0
|
689 /**
|
Chris@0
|
690 * Warning: this is an autovivifying get.
|
Chris@0
|
691 *
|
Chris@0
|
692 * @param string|false $currScope
|
Chris@0
|
693 * @param bool $autoCreate
|
Chris@0
|
694 *
|
Chris@0
|
695 * @return ScopeInfo
|
Chris@0
|
696 */
|
Chris@0
|
697 function getScopeInfo($currScope, $autoCreate = true)
|
Chris@0
|
698 {
|
Chris@0
|
699 $scopeKey = $this->scopeKey($currScope);
|
Chris@0
|
700 if (isset($this->_scopes[$scopeKey]) === false) {
|
Chris@0
|
701 if ($autoCreate === false) {
|
Chris@0
|
702 return null;
|
Chris@0
|
703 }
|
Chris@0
|
704
|
Chris@0
|
705 $this->_scopes[$scopeKey] = new ScopeInfo($currScope);
|
Chris@0
|
706 }
|
Chris@0
|
707
|
Chris@0
|
708 return $this->_scopes[$scopeKey];
|
Chris@0
|
709
|
Chris@0
|
710 }//end getScopeInfo()
|
Chris@0
|
711
|
Chris@0
|
712
|
Chris@0
|
713 /**
|
Chris@0
|
714 * Get variable information for a given variable name.
|
Chris@0
|
715 *
|
Chris@0
|
716 * @param string $varName
|
Chris@0
|
717 * Name of the variable.
|
Chris@0
|
718 * @param int $currScope
|
Chris@0
|
719 * Token stack pointer of the current scope.
|
Chris@0
|
720 * @param bool $autoCreate
|
Chris@0
|
721 * TRUE if the variable should be auto created.
|
Chris@0
|
722 *
|
Chris@0
|
723 * @return VariableInfo|null
|
Chris@0
|
724 * Information about the variable.
|
Chris@0
|
725 */
|
Chris@0
|
726 function getVariableInfo($varName, $currScope, $autoCreate = true)
|
Chris@0
|
727 {
|
Chris@0
|
728 $scopeInfo = $this->getScopeInfo($currScope, $autoCreate);
|
Chris@0
|
729 if (isset($scopeInfo->variables[$varName]) === false) {
|
Chris@0
|
730 if ($autoCreate === false) {
|
Chris@0
|
731 return null;
|
Chris@0
|
732 }
|
Chris@0
|
733
|
Chris@0
|
734 $scopeInfo->variables[$varName] = new VariableInfo($varName);
|
Chris@0
|
735 if (is_array($this->validUnusedVariableNames) === true
|
Chris@0
|
736 && in_array($varName, $this->validUnusedVariableNames) === true
|
Chris@0
|
737 ) {
|
Chris@0
|
738 $scopeInfo->variables[$varName]->ignoreUnused = true;
|
Chris@0
|
739 }
|
Chris@0
|
740 }
|
Chris@0
|
741
|
Chris@0
|
742 return $scopeInfo->variables[$varName];
|
Chris@0
|
743
|
Chris@0
|
744 }//end getVariableInfo()
|
Chris@0
|
745
|
Chris@0
|
746
|
Chris@0
|
747 /**
|
Chris@0
|
748 * Mark the given variable as being assigned.
|
Chris@0
|
749 *
|
Chris@0
|
750 * @param string $varName
|
Chris@0
|
751 * @param int $stackPtr
|
Chris@0
|
752 * @param string $currScope
|
Chris@0
|
753 *
|
Chris@0
|
754 * @return void
|
Chris@0
|
755 */
|
Chris@0
|
756 function markVariableAssignment($varName, $stackPtr, $currScope)
|
Chris@0
|
757 {
|
Chris@0
|
758 $varInfo = $this->getVariableInfo($varName, $currScope);
|
Chris@0
|
759 if (isset($varInfo->scopeType) === false) {
|
Chris@0
|
760 $varInfo->scopeType = 'local';
|
Chris@0
|
761 }
|
Chris@0
|
762
|
Chris@0
|
763 if (isset($varInfo->firstInitialized) === true && ($varInfo->firstInitialized <= $stackPtr)) {
|
Chris@0
|
764 $varInfo->lastAssignment = $stackPtr;
|
Chris@0
|
765 return;
|
Chris@0
|
766 }
|
Chris@0
|
767
|
Chris@0
|
768 $varInfo->firstInitialized = $stackPtr;
|
Chris@0
|
769
|
Chris@0
|
770 }//end markVariableAssignment()
|
Chris@0
|
771
|
Chris@0
|
772
|
Chris@0
|
773 /**
|
Chris@0
|
774 * Mark the given variable as being declared.
|
Chris@0
|
775 *
|
Chris@0
|
776 * @param string $varName
|
Chris@0
|
777 * @param string $scopeType
|
Chris@0
|
778 * @param string $typeHint
|
Chris@0
|
779 * @param int $stackPtr
|
Chris@0
|
780 * @param string $currScope
|
Chris@0
|
781 * @param bool $permitMatchingRedeclaration
|
Chris@0
|
782 *
|
Chris@0
|
783 * @return void
|
Chris@0
|
784 */
|
Chris@0
|
785 function markVariableDeclaration($varName, $scopeType, $typeHint, $stackPtr, $currScope, $permitMatchingRedeclaration = false)
|
Chris@0
|
786 {
|
Chris@0
|
787 $varInfo = $this->getVariableInfo($varName, $currScope);
|
Chris@0
|
788 if (isset($varInfo->scopeType) === true) {
|
Chris@0
|
789 if (($permitMatchingRedeclaration === false)
|
Chris@0
|
790 || ($varInfo->scopeType !== $scopeType)
|
Chris@0
|
791 ) {
|
Chris@0
|
792 // Issue redeclaration/reuse warning
|
Chris@0
|
793 // Note: we check off scopeType not firstDeclared, this is so that
|
Chris@0
|
794 // we catch declarations that come after implicit declarations like
|
Chris@0
|
795 // use of a variable as a local.
|
Chris@0
|
796 $this->currentFile->addWarning(
|
Chris@0
|
797 "Redeclaration of %s %s as %s.",
|
Chris@0
|
798 $stackPtr,
|
Chris@0
|
799 'VariableRedeclaration',
|
Chris@0
|
800 array(
|
Chris@0
|
801 VariableInfo::$scopeTypeDescriptions[$varInfo->scopeType],
|
Chris@0
|
802 "\${$varName}",
|
Chris@0
|
803 VariableInfo::$scopeTypeDescriptions[$scopeType],
|
Chris@0
|
804 )
|
Chris@0
|
805 );
|
Chris@0
|
806 }
|
Chris@0
|
807 }
|
Chris@0
|
808
|
Chris@0
|
809 $varInfo->scopeType = $scopeType;
|
Chris@0
|
810 if (isset($typeHint) === true) {
|
Chris@0
|
811 $varInfo->typeHint = $typeHint;
|
Chris@0
|
812 }
|
Chris@0
|
813
|
Chris@0
|
814 if (isset($varInfo->firstDeclared) === true && ($varInfo->firstDeclared <= $stackPtr)) {
|
Chris@0
|
815 return;
|
Chris@0
|
816 }
|
Chris@0
|
817
|
Chris@0
|
818 // When a global variable is declared it also means we can consider it as
|
Chris@0
|
819 // being initialized.
|
Chris@0
|
820 if ($scopeType === 'global') {
|
Chris@0
|
821 $varInfo->firstInitialized = $stackPtr;
|
Chris@0
|
822 }
|
Chris@0
|
823
|
Chris@0
|
824 $varInfo->firstDeclared = $stackPtr;
|
Chris@0
|
825
|
Chris@0
|
826 }//end markVariableDeclaration()
|
Chris@0
|
827
|
Chris@0
|
828
|
Chris@0
|
829 /**
|
Chris@0
|
830 * Mark the given variable as being read.
|
Chris@0
|
831 *
|
Chris@0
|
832 * @param string $varName
|
Chris@0
|
833 * @param int $stackPtr
|
Chris@0
|
834 * @param string $currScope
|
Chris@0
|
835 *
|
Chris@0
|
836 * @return void
|
Chris@0
|
837 */
|
Chris@0
|
838 function markVariableRead($varName, $stackPtr, $currScope)
|
Chris@0
|
839 {
|
Chris@0
|
840 $varInfo = $this->getVariableInfo($varName, $currScope);
|
Chris@0
|
841 if (isset($varInfo->firstRead) === true && ($varInfo->firstRead <= $stackPtr)) {
|
Chris@0
|
842 return;
|
Chris@0
|
843 }
|
Chris@0
|
844
|
Chris@0
|
845 $varInfo->firstRead = $stackPtr;
|
Chris@0
|
846
|
Chris@0
|
847 }//end markVariableRead()
|
Chris@0
|
848
|
Chris@0
|
849
|
Chris@0
|
850 /**
|
Chris@0
|
851 * Checks if a variable has been initialized.
|
Chris@0
|
852 *
|
Chris@0
|
853 * @param string $varName
|
Chris@0
|
854 * @param int $stackPtr
|
Chris@0
|
855 * @param string $currScope
|
Chris@0
|
856 *
|
Chris@0
|
857 * @return bool
|
Chris@0
|
858 */
|
Chris@0
|
859 function isVariableInitialized($varName, $stackPtr, $currScope)
|
Chris@0
|
860 {
|
Chris@0
|
861 $varInfo = $this->getVariableInfo($varName, $currScope);
|
Chris@0
|
862 if (isset($varInfo->firstInitialized) === true && $varInfo->firstInitialized <= $stackPtr) {
|
Chris@0
|
863 return true;
|
Chris@0
|
864 }
|
Chris@0
|
865
|
Chris@0
|
866 return false;
|
Chris@0
|
867
|
Chris@0
|
868 }//end isVariableInitialized()
|
Chris@0
|
869
|
Chris@0
|
870
|
Chris@0
|
871 /**
|
Chris@0
|
872 * Checks if the given variable is undefined.
|
Chris@0
|
873 *
|
Chris@0
|
874 * @param string $varName
|
Chris@0
|
875 * @param int $stackPtr
|
Chris@0
|
876 * @param string $currScope
|
Chris@0
|
877 *
|
Chris@0
|
878 * @return bool
|
Chris@0
|
879 */
|
Chris@0
|
880 function isVariableUndefined($varName, $stackPtr, $currScope)
|
Chris@0
|
881 {
|
Chris@0
|
882 $varInfo = $this->getVariableInfo($varName, $currScope, false);
|
Chris@0
|
883 if (isset($varInfo->firstDeclared) === true && $varInfo->firstDeclared <= $stackPtr) {
|
Chris@0
|
884 // TODO: do we want to check scopeType here?
|
Chris@0
|
885 return false;
|
Chris@0
|
886 }
|
Chris@0
|
887
|
Chris@0
|
888 if (isset($varInfo->firstInitialized) === true && $varInfo->firstInitialized <= $stackPtr) {
|
Chris@0
|
889 return false;
|
Chris@0
|
890 }
|
Chris@0
|
891
|
Chris@0
|
892 return true;
|
Chris@0
|
893
|
Chris@0
|
894 }//end isVariableUndefined()
|
Chris@0
|
895
|
Chris@0
|
896
|
Chris@0
|
897 /**
|
Chris@0
|
898 * Marks a variable as read and throws a PHPCS warning if it is undefined.
|
Chris@0
|
899 *
|
Chris@0
|
900 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
901 * @param string $varName
|
Chris@0
|
902 * @param int $stackPtr
|
Chris@0
|
903 * @param string $currScope
|
Chris@0
|
904 *
|
Chris@0
|
905 * @return bool
|
Chris@0
|
906 */
|
Chris@0
|
907 function markVariableReadAndWarnIfUndefined(PHP_CodeSniffer_File $phpcsFile, $varName, $stackPtr, $currScope)
|
Chris@0
|
908 {
|
Chris@0
|
909 $this->markVariableRead($varName, $stackPtr, $currScope);
|
Chris@0
|
910
|
Chris@0
|
911 if ($this->isVariableUndefined($varName, $stackPtr, $currScope) === true) {
|
Chris@0
|
912 // We haven't been defined by this point.
|
Chris@0
|
913 $phpcsFile->addWarning(
|
Chris@0
|
914 "Variable %s is undefined.",
|
Chris@0
|
915 $stackPtr,
|
Chris@0
|
916 'UndefinedVariable',
|
Chris@0
|
917 array("\${$varName}")
|
Chris@0
|
918 );
|
Chris@0
|
919 }
|
Chris@0
|
920
|
Chris@0
|
921 return true;
|
Chris@0
|
922
|
Chris@0
|
923 }//end markVariableReadAndWarnIfUndefined()
|
Chris@0
|
924
|
Chris@0
|
925
|
Chris@0
|
926 /**
|
Chris@0
|
927 * Returns the function declaration pointer.
|
Chris@0
|
928 *
|
Chris@0
|
929 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
930 * @param int $stackPtr
|
Chris@0
|
931 *
|
Chris@0
|
932 * @return int|false
|
Chris@0
|
933 */
|
Chris@0
|
934 function findFunctionPrototype(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
|
Chris@0
|
935 {
|
Chris@0
|
936 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
937
|
Chris@0
|
938 if (($openPtr = $this->findContainingBrackets($phpcsFile, $stackPtr)) === false) {
|
Chris@0
|
939 return false;
|
Chris@0
|
940 }
|
Chris@0
|
941
|
Chris@0
|
942 // Function names are T_STRING, and return-by-reference is T_BITWISE_AND,
|
Chris@0
|
943 // so we look backwards from the opening bracket for the first thing that
|
Chris@0
|
944 // isn't a function name, reference sigil or whitespace and check if
|
Chris@0
|
945 // it's a function keyword.
|
Chris@0
|
946 $functionPtr = $phpcsFile->findPrevious(
|
Chris@0
|
947 array(
|
Chris@0
|
948 T_STRING,
|
Chris@0
|
949 T_WHITESPACE,
|
Chris@0
|
950 T_BITWISE_AND,
|
Chris@0
|
951 ),
|
Chris@0
|
952 ($openPtr - 1),
|
Chris@0
|
953 null,
|
Chris@0
|
954 true,
|
Chris@0
|
955 null,
|
Chris@0
|
956 true
|
Chris@0
|
957 );
|
Chris@0
|
958 if (($functionPtr !== false)
|
Chris@0
|
959 && ($tokens[$functionPtr]['code'] === T_FUNCTION)
|
Chris@0
|
960 ) {
|
Chris@0
|
961 return $functionPtr;
|
Chris@0
|
962 }
|
Chris@0
|
963
|
Chris@0
|
964 return false;
|
Chris@0
|
965
|
Chris@0
|
966 }//end findFunctionPrototype()
|
Chris@0
|
967
|
Chris@0
|
968
|
Chris@0
|
969 /**
|
Chris@0
|
970 * Find the scope the given pointer is in.
|
Chris@0
|
971 *
|
Chris@0
|
972 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
973 * @param int $stackPtr
|
Chris@0
|
974 *
|
Chris@0
|
975 * @return int|false
|
Chris@0
|
976 */
|
Chris@0
|
977 function findVariableScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
|
Chris@0
|
978 {
|
Chris@0
|
979 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
980 $token = $tokens[$stackPtr];
|
Chris@0
|
981
|
Chris@0
|
982 $in_class = false;
|
Chris@0
|
983 if (empty($token['conditions']) === false) {
|
Chris@0
|
984 foreach (array_reverse($token['conditions'], true) as $scopePtr => $scopeCode) {
|
Chris@0
|
985 if (($scopeCode === T_FUNCTION) || ($scopeCode === T_CLOSURE)) {
|
Chris@0
|
986 return $scopePtr;
|
Chris@0
|
987 }
|
Chris@0
|
988
|
Chris@0
|
989 if (($scopeCode === T_CLASS) || ($scopeCode === T_INTERFACE) || ($scopeCode === T_TRAIT)) {
|
Chris@0
|
990 $in_class = true;
|
Chris@0
|
991 }
|
Chris@0
|
992 }
|
Chris@0
|
993 }
|
Chris@0
|
994
|
Chris@0
|
995 if (($scopePtr = $this->findFunctionPrototype($phpcsFile, $stackPtr)) !== false) {
|
Chris@0
|
996 return $scopePtr;
|
Chris@0
|
997 }
|
Chris@0
|
998
|
Chris@0
|
999 if ($in_class === true) {
|
Chris@0
|
1000 // Member var of a class, we don't care.
|
Chris@0
|
1001 return false;
|
Chris@0
|
1002 }
|
Chris@0
|
1003
|
Chris@0
|
1004 // File scope, hmm, lets use first token of file?
|
Chris@0
|
1005 return 0;
|
Chris@0
|
1006
|
Chris@0
|
1007 }//end findVariableScope()
|
Chris@0
|
1008
|
Chris@0
|
1009
|
Chris@0
|
1010 /**
|
Chris@0
|
1011 * Checks if the next token is an assignment.
|
Chris@0
|
1012 *
|
Chris@0
|
1013 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1014 * @param int $stackPtr
|
Chris@0
|
1015 *
|
Chris@0
|
1016 * @return bool
|
Chris@0
|
1017 */
|
Chris@0
|
1018 function isNextThingAnAssign(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
|
Chris@0
|
1019 {
|
Chris@0
|
1020 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1021
|
Chris@0
|
1022 // Is the next non-whitespace an assignment?
|
Chris@0
|
1023 $nextPtr = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true, null, true);
|
Chris@0
|
1024 if ($nextPtr !== false) {
|
Chris@0
|
1025 if ($tokens[$nextPtr]['code'] === T_EQUAL) {
|
Chris@0
|
1026 return $nextPtr;
|
Chris@0
|
1027 }
|
Chris@0
|
1028
|
Chris@0
|
1029 // Special handling for initializing arrays on the fly, which is
|
Chris@0
|
1030 // also an assignment.
|
Chris@0
|
1031 if ($tokens[$nextPtr]['code'] === T_OPEN_SQUARE_BRACKET) {
|
Chris@0
|
1032 return $this->isNextThingAnAssign($phpcsFile, $tokens[$nextPtr]['bracket_closer']);
|
Chris@0
|
1033 }
|
Chris@0
|
1034 }
|
Chris@0
|
1035
|
Chris@0
|
1036 return false;
|
Chris@0
|
1037
|
Chris@0
|
1038 }//end isNextThingAnAssign()
|
Chris@0
|
1039
|
Chris@0
|
1040
|
Chris@0
|
1041 /**
|
Chris@0
|
1042 * Find the end of the assignment.
|
Chris@0
|
1043 *
|
Chris@0
|
1044 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1045 * @param int $stackPtr
|
Chris@0
|
1046 *
|
Chris@0
|
1047 * @return int
|
Chris@0
|
1048 */
|
Chris@0
|
1049 function findWhereAssignExecuted(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
|
Chris@0
|
1050 {
|
Chris@0
|
1051 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1052
|
Chris@0
|
1053 // Write should be recorded at the next statement to ensure we treat
|
Chris@0
|
1054 // the assign as happening after the RHS execution.
|
Chris@0
|
1055 // eg: $var = $var + 1; -> RHS could still be undef.
|
Chris@0
|
1056 // However, if we're within a bracketed expression,
|
Chris@0
|
1057 // eg: echo (($var = 12) && ($var == 12));
|
Chris@0
|
1058 // we take place at the closing bracket, if that's first.
|
Chris@0
|
1059 $semicolonPtr = $phpcsFile->findNext(T_SEMICOLON, ($stackPtr + 1), null, false, null, true);
|
Chris@0
|
1060 $closePtr = false;
|
Chris@0
|
1061 if (($openPtr = $this->findContainingBrackets($phpcsFile, $stackPtr)) !== false) {
|
Chris@0
|
1062 if (isset($tokens[$openPtr]['parenthesis_closer']) === true) {
|
Chris@0
|
1063 $closePtr = $tokens[$openPtr]['parenthesis_closer'];
|
Chris@0
|
1064 }
|
Chris@0
|
1065 }
|
Chris@0
|
1066
|
Chris@0
|
1067 if ($semicolonPtr === false) {
|
Chris@0
|
1068 if ($closePtr === false) {
|
Chris@0
|
1069 // TODO: panic.
|
Chris@0
|
1070 return $stackPtr;
|
Chris@0
|
1071 }
|
Chris@0
|
1072
|
Chris@0
|
1073 return $closePtr;
|
Chris@0
|
1074 }
|
Chris@0
|
1075
|
Chris@0
|
1076 if ($closePtr !== false && $closePtr < $semicolonPtr) {
|
Chris@0
|
1077 return $closePtr;
|
Chris@0
|
1078 }
|
Chris@0
|
1079
|
Chris@0
|
1080 return $semicolonPtr;
|
Chris@0
|
1081
|
Chris@0
|
1082 }//end findWhereAssignExecuted()
|
Chris@0
|
1083
|
Chris@0
|
1084
|
Chris@0
|
1085 /**
|
Chris@0
|
1086 * Find the parenthesis if the pointer is in some.
|
Chris@0
|
1087 *
|
Chris@0
|
1088 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1089 * @param int $stackPtr
|
Chris@0
|
1090 *
|
Chris@0
|
1091 * @return int|false
|
Chris@0
|
1092 */
|
Chris@0
|
1093 function findContainingBrackets(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
|
Chris@0
|
1094 {
|
Chris@0
|
1095 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1096
|
Chris@0
|
1097 if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
|
Chris@0
|
1098 $openPtrs = array_keys($tokens[$stackPtr]['nested_parenthesis']);
|
Chris@0
|
1099 return end($openPtrs);
|
Chris@0
|
1100 }
|
Chris@0
|
1101
|
Chris@0
|
1102 return false;
|
Chris@0
|
1103
|
Chris@0
|
1104 }//end findContainingBrackets()
|
Chris@0
|
1105
|
Chris@0
|
1106
|
Chris@0
|
1107 /**
|
Chris@0
|
1108 * Checks if the given pointer is in a function call.
|
Chris@0
|
1109 *
|
Chris@0
|
1110 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1111 * @param int $stackPtr
|
Chris@0
|
1112 *
|
Chris@0
|
1113 * @return int|false
|
Chris@0
|
1114 */
|
Chris@0
|
1115 function findFunctionCall(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
|
Chris@0
|
1116 {
|
Chris@0
|
1117 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1118
|
Chris@0
|
1119 if (($openPtr = $this->findContainingBrackets($phpcsFile, $stackPtr)) !== false) {
|
Chris@0
|
1120 // First non-whitespace thing and see if it's a T_STRING function name.
|
Chris@0
|
1121 $functionPtr = $phpcsFile->findPrevious(
|
Chris@0
|
1122 T_WHITESPACE,
|
Chris@0
|
1123 ($openPtr - 1),
|
Chris@0
|
1124 null,
|
Chris@0
|
1125 true,
|
Chris@0
|
1126 null,
|
Chris@0
|
1127 true
|
Chris@0
|
1128 );
|
Chris@0
|
1129 if ($tokens[$functionPtr]['code'] === T_STRING) {
|
Chris@0
|
1130 return $functionPtr;
|
Chris@0
|
1131 }
|
Chris@0
|
1132 }
|
Chris@0
|
1133
|
Chris@0
|
1134 return false;
|
Chris@0
|
1135
|
Chris@0
|
1136 }//end findFunctionCall()
|
Chris@0
|
1137
|
Chris@0
|
1138
|
Chris@0
|
1139 /**
|
Chris@0
|
1140 * Get the arguments of a function call.
|
Chris@0
|
1141 *
|
Chris@0
|
1142 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1143 * @param int $stackPtr
|
Chris@0
|
1144 *
|
Chris@0
|
1145 * @return array|false
|
Chris@0
|
1146 */
|
Chris@0
|
1147 function findFunctionCallArguments(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
|
Chris@0
|
1148 {
|
Chris@0
|
1149 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1150
|
Chris@0
|
1151 // Slight hack: also allow this to find args for array constructor.
|
Chris@0
|
1152 // TODO: probably should refactor into three functions: arg-finding and bracket-finding.
|
Chris@0
|
1153 if (($tokens[$stackPtr]['code'] !== T_STRING) && ($tokens[$stackPtr]['code'] !== T_ARRAY)) {
|
Chris@0
|
1154 // Assume $stackPtr is something within the brackets, find our function call.
|
Chris@0
|
1155 if (($stackPtr = $this->findFunctionCall($phpcsFile, $stackPtr)) === false) {
|
Chris@0
|
1156 return false;
|
Chris@0
|
1157 }
|
Chris@0
|
1158 }
|
Chris@0
|
1159
|
Chris@0
|
1160 // $stackPtr is the function name, find our brackets after it.
|
Chris@0
|
1161 $openPtr = $phpcsFile->findNext(
|
Chris@0
|
1162 T_WHITESPACE,
|
Chris@0
|
1163 ($stackPtr + 1),
|
Chris@0
|
1164 null,
|
Chris@0
|
1165 true,
|
Chris@0
|
1166 null,
|
Chris@0
|
1167 true
|
Chris@0
|
1168 );
|
Chris@0
|
1169 if (($openPtr === false) || ($tokens[$openPtr]['code'] !== T_OPEN_PARENTHESIS)) {
|
Chris@0
|
1170 return false;
|
Chris@0
|
1171 }
|
Chris@0
|
1172
|
Chris@0
|
1173 if (isset($tokens[$openPtr]['parenthesis_closer']) === false) {
|
Chris@0
|
1174 return false;
|
Chris@0
|
1175 }
|
Chris@0
|
1176
|
Chris@0
|
1177 $closePtr = $tokens[$openPtr]['parenthesis_closer'];
|
Chris@0
|
1178
|
Chris@0
|
1179 $argPtrs = array();
|
Chris@0
|
1180 $lastPtr = $openPtr;
|
Chris@0
|
1181 $lastArgComma = $openPtr;
|
Chris@0
|
1182 while (($nextPtr = $phpcsFile->findNext(T_COMMA, ($lastPtr + 1), $closePtr)) !== false) {
|
Chris@0
|
1183 if ($this->findContainingBrackets($phpcsFile, $nextPtr) === $openPtr) {
|
Chris@0
|
1184 // Comma is at our level of brackets, it's an argument delimiter.
|
Chris@0
|
1185 array_push($argPtrs, range(($lastArgComma + 1), ($nextPtr - 1)));
|
Chris@0
|
1186 $lastArgComma = $nextPtr;
|
Chris@0
|
1187 }
|
Chris@0
|
1188
|
Chris@0
|
1189 $lastPtr = $nextPtr;
|
Chris@0
|
1190 }
|
Chris@0
|
1191
|
Chris@0
|
1192 array_push($argPtrs, range(($lastArgComma + 1), ($closePtr - 1)));
|
Chris@0
|
1193
|
Chris@0
|
1194 return $argPtrs;
|
Chris@0
|
1195
|
Chris@0
|
1196 }//end findFunctionCallArguments()
|
Chris@0
|
1197
|
Chris@0
|
1198
|
Chris@0
|
1199 /**
|
Chris@0
|
1200 * Checks the function prototype.
|
Chris@0
|
1201 *
|
Chris@0
|
1202 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1203 * @param int $stackPtr
|
Chris@0
|
1204 * @param string $varName
|
Chris@0
|
1205 * @param string $currScope
|
Chris@0
|
1206 *
|
Chris@0
|
1207 * @return bool
|
Chris@0
|
1208 */
|
Chris@0
|
1209 protected function checkForFunctionPrototype(
|
Chris@0
|
1210 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1211 $stackPtr,
|
Chris@0
|
1212 $varName,
|
Chris@0
|
1213 $currScope
|
Chris@0
|
1214 ) {
|
Chris@0
|
1215 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1216
|
Chris@0
|
1217 // Are we a function or closure parameter?
|
Chris@0
|
1218 // It would be nice to get the list of function parameters from watching for
|
Chris@0
|
1219 // T_FUNCTION, but AbstractVariableSniff and AbstractScopeSniff define everything
|
Chris@0
|
1220 // we need to do that as private or final, so we have to do it this hackish way.
|
Chris@0
|
1221 if (($openPtr = $this->findContainingBrackets($phpcsFile, $stackPtr)) === false) {
|
Chris@0
|
1222 return false;
|
Chris@0
|
1223 }
|
Chris@0
|
1224
|
Chris@0
|
1225 // Function names are T_STRING, and return-by-reference is T_BITWISE_AND,
|
Chris@0
|
1226 // so we look backwards from the opening bracket for the first thing that
|
Chris@0
|
1227 // isn't a function name, reference sigil or whitespace and check if
|
Chris@0
|
1228 // it's a function keyword.
|
Chris@0
|
1229 $functionPtr = $phpcsFile->findPrevious(
|
Chris@0
|
1230 array(
|
Chris@0
|
1231 T_STRING,
|
Chris@0
|
1232 T_WHITESPACE,
|
Chris@0
|
1233 T_BITWISE_AND,
|
Chris@0
|
1234 ),
|
Chris@0
|
1235 ($openPtr - 1),
|
Chris@0
|
1236 null,
|
Chris@0
|
1237 true,
|
Chris@0
|
1238 null,
|
Chris@0
|
1239 true
|
Chris@0
|
1240 );
|
Chris@0
|
1241 if (($functionPtr !== false)
|
Chris@0
|
1242 && (($tokens[$functionPtr]['code'] === T_FUNCTION)
|
Chris@0
|
1243 || ($tokens[$functionPtr]['code'] === T_CLOSURE))
|
Chris@0
|
1244 ) {
|
Chris@0
|
1245 // TODO: typeHint.
|
Chris@0
|
1246 $this->markVariableDeclaration($varName, 'param', null, $stackPtr, $functionPtr);
|
Chris@0
|
1247 // Are we pass-by-reference?
|
Chris@0
|
1248 $referencePtr = $phpcsFile->findPrevious(
|
Chris@0
|
1249 T_WHITESPACE,
|
Chris@0
|
1250 ($stackPtr - 1),
|
Chris@0
|
1251 null,
|
Chris@0
|
1252 true,
|
Chris@0
|
1253 null,
|
Chris@0
|
1254 true
|
Chris@0
|
1255 );
|
Chris@0
|
1256 if (($referencePtr !== false) && ($tokens[$referencePtr]['code'] === T_BITWISE_AND)) {
|
Chris@0
|
1257 $varInfo = $this->getVariableInfo($varName, $functionPtr);
|
Chris@0
|
1258 $varInfo->passByReference = true;
|
Chris@0
|
1259 }
|
Chris@0
|
1260
|
Chris@0
|
1261 // Are we optional with a default?
|
Chris@0
|
1262 if ($this->isNextThingAnAssign($phpcsFile, $stackPtr) !== false) {
|
Chris@0
|
1263 $this->markVariableAssignment($varName, $stackPtr, $functionPtr);
|
Chris@0
|
1264 }
|
Chris@0
|
1265
|
Chris@0
|
1266 return true;
|
Chris@0
|
1267 }//end if
|
Chris@0
|
1268
|
Chris@0
|
1269 // Is it a use keyword? Use is both a read and a define, fun!
|
Chris@0
|
1270 if (($functionPtr !== false) && ($tokens[$functionPtr]['code'] === T_USE)) {
|
Chris@0
|
1271 $this->markVariableRead($varName, $stackPtr, $currScope);
|
Chris@0
|
1272 if ($this->isVariableUndefined($varName, $stackPtr, $currScope) === true) {
|
Chris@0
|
1273 // We haven't been defined by this point.
|
Chris@0
|
1274 $phpcsFile->addWarning(
|
Chris@0
|
1275 "Variable %s is undefined.",
|
Chris@0
|
1276 $stackPtr,
|
Chris@0
|
1277 'UndefinedVariable',
|
Chris@0
|
1278 array("\${$varName}")
|
Chris@0
|
1279 );
|
Chris@0
|
1280 return true;
|
Chris@0
|
1281 }
|
Chris@0
|
1282
|
Chris@0
|
1283 // $functionPtr is at the use, we need the function keyword for start of scope.
|
Chris@0
|
1284 $functionPtr = $phpcsFile->findPrevious(
|
Chris@0
|
1285 T_CLOSURE,
|
Chris@0
|
1286 ($functionPtr - 1),
|
Chris@0
|
1287 ($currScope + 1),
|
Chris@0
|
1288 false,
|
Chris@0
|
1289 null,
|
Chris@0
|
1290 true
|
Chris@0
|
1291 );
|
Chris@0
|
1292 if ($functionPtr !== false) {
|
Chris@0
|
1293 // TODO: typeHints in use?
|
Chris@0
|
1294 $this->markVariableDeclaration($varName, 'bound', null, $stackPtr, $functionPtr);
|
Chris@0
|
1295 $this->markVariableAssignment($varName, $stackPtr, $functionPtr);
|
Chris@0
|
1296
|
Chris@0
|
1297 // Are we pass-by-reference?
|
Chris@0
|
1298 $referencePtr = $phpcsFile->findPrevious(
|
Chris@0
|
1299 T_WHITESPACE,
|
Chris@0
|
1300 ($stackPtr - 1),
|
Chris@0
|
1301 null,
|
Chris@0
|
1302 true,
|
Chris@0
|
1303 null,
|
Chris@0
|
1304 true
|
Chris@0
|
1305 );
|
Chris@0
|
1306 if (($referencePtr !== false) && ($tokens[$referencePtr]['code'] === T_BITWISE_AND)) {
|
Chris@0
|
1307 $varInfo = $this->getVariableInfo($varName, $functionPtr);
|
Chris@0
|
1308 $varInfo->passByReference = true;
|
Chris@0
|
1309 }
|
Chris@0
|
1310
|
Chris@0
|
1311 return true;
|
Chris@0
|
1312 }//end if
|
Chris@0
|
1313 }//end if
|
Chris@0
|
1314
|
Chris@0
|
1315 return false;
|
Chris@0
|
1316
|
Chris@0
|
1317 }//end checkForFunctionPrototype()
|
Chris@0
|
1318
|
Chris@0
|
1319
|
Chris@0
|
1320 /**
|
Chris@0
|
1321 * Checks if we are in a catch() block.
|
Chris@0
|
1322 *
|
Chris@0
|
1323 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1324 * @param int $stackPtr
|
Chris@0
|
1325 * @param string $varName
|
Chris@0
|
1326 * @param string $currScope
|
Chris@0
|
1327 *
|
Chris@0
|
1328 * @return bool
|
Chris@0
|
1329 */
|
Chris@0
|
1330 protected function checkForCatchBlock(
|
Chris@0
|
1331 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1332 $stackPtr,
|
Chris@0
|
1333 $varName,
|
Chris@0
|
1334 $currScope
|
Chris@0
|
1335 ) {
|
Chris@0
|
1336 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1337
|
Chris@0
|
1338 // Are we a catch block parameter?
|
Chris@0
|
1339 if (($openPtr = $this->findContainingBrackets($phpcsFile, $stackPtr)) === false) {
|
Chris@0
|
1340 return false;
|
Chris@0
|
1341 }
|
Chris@0
|
1342
|
Chris@0
|
1343 // Function names are T_STRING, and return-by-reference is T_BITWISE_AND,
|
Chris@0
|
1344 // so we look backwards from the opening bracket for the first thing that
|
Chris@0
|
1345 // isn't a function name, reference sigil or whitespace and check if
|
Chris@0
|
1346 // it's a function keyword.
|
Chris@0
|
1347 $catchPtr = $phpcsFile->findPrevious(
|
Chris@0
|
1348 T_WHITESPACE,
|
Chris@0
|
1349 ($openPtr - 1),
|
Chris@0
|
1350 null,
|
Chris@0
|
1351 true,
|
Chris@0
|
1352 null,
|
Chris@0
|
1353 true
|
Chris@0
|
1354 );
|
Chris@0
|
1355 if (($catchPtr !== false)
|
Chris@0
|
1356 && ($tokens[$catchPtr]['code'] === T_CATCH)
|
Chris@0
|
1357 ) {
|
Chris@0
|
1358 // Scope of the exception var is actually the function, not just the catch block.
|
Chris@0
|
1359 // TODO: typeHint.
|
Chris@0
|
1360 $this->markVariableDeclaration($varName, 'local', null, $stackPtr, $currScope, true);
|
Chris@0
|
1361 $this->markVariableAssignment($varName, $stackPtr, $currScope);
|
Chris@0
|
1362 if ($this->allowUnusedCaughtExceptions !== false) {
|
Chris@0
|
1363 $varInfo = $this->getVariableInfo($varName, $currScope);
|
Chris@0
|
1364 $varInfo->ignoreUnused = true;
|
Chris@0
|
1365 }
|
Chris@0
|
1366
|
Chris@0
|
1367 return true;
|
Chris@0
|
1368 }
|
Chris@0
|
1369
|
Chris@0
|
1370 return false;
|
Chris@0
|
1371
|
Chris@0
|
1372 }//end checkForCatchBlock()
|
Chris@0
|
1373
|
Chris@0
|
1374
|
Chris@0
|
1375 /**
|
Chris@0
|
1376 * Checks if $this is used within a class.
|
Chris@0
|
1377 *
|
Chris@0
|
1378 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1379 * @param int $stackPtr
|
Chris@0
|
1380 * @param string $varName
|
Chris@0
|
1381 * @param string $currScope
|
Chris@0
|
1382 *
|
Chris@0
|
1383 * @return bool
|
Chris@0
|
1384 */
|
Chris@0
|
1385 protected function checkForThisWithinClass(
|
Chris@0
|
1386 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1387 $stackPtr,
|
Chris@0
|
1388 $varName,
|
Chris@0
|
1389 $currScope
|
Chris@0
|
1390 ) {
|
Chris@0
|
1391 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1392 $token = $tokens[$stackPtr];
|
Chris@0
|
1393
|
Chris@0
|
1394 // Are we $this within a class?
|
Chris@0
|
1395 if (($varName !== 'this') || empty($token['conditions']) === true) {
|
Chris@0
|
1396 return false;
|
Chris@0
|
1397 }
|
Chris@0
|
1398
|
Chris@0
|
1399 foreach (array_reverse($token['conditions'], true) as $scopePtr => $scopeCode) {
|
Chris@0
|
1400 if ($scopeCode === T_CLASS || $scopeCode === T_TRAIT) {
|
Chris@0
|
1401 return true;
|
Chris@0
|
1402 }
|
Chris@0
|
1403 }
|
Chris@0
|
1404
|
Chris@0
|
1405 return false;
|
Chris@0
|
1406
|
Chris@0
|
1407 }//end checkForThisWithinClass()
|
Chris@0
|
1408
|
Chris@0
|
1409
|
Chris@0
|
1410 /**
|
Chris@0
|
1411 * Checks if the variable is a PHP super global.
|
Chris@0
|
1412 *
|
Chris@0
|
1413 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1414 * @param int $stackPtr
|
Chris@0
|
1415 * @param string $varName
|
Chris@0
|
1416 * @param string $currScope
|
Chris@0
|
1417 *
|
Chris@0
|
1418 * @return bool
|
Chris@0
|
1419 */
|
Chris@0
|
1420 protected function checkForSuperGlobal(
|
Chris@0
|
1421 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1422 $stackPtr,
|
Chris@0
|
1423 $varName,
|
Chris@0
|
1424 $currScope
|
Chris@0
|
1425 ) {
|
Chris@0
|
1426 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1427 $token = $tokens[$stackPtr];
|
Chris@0
|
1428
|
Chris@0
|
1429 // Are we a superglobal variable?
|
Chris@0
|
1430 if (in_array(
|
Chris@0
|
1431 $varName,
|
Chris@0
|
1432 array(
|
Chris@0
|
1433 'GLOBALS',
|
Chris@0
|
1434 '_SERVER',
|
Chris@0
|
1435 '_GET',
|
Chris@0
|
1436 '_POST',
|
Chris@0
|
1437 '_FILES',
|
Chris@0
|
1438 '_COOKIE',
|
Chris@0
|
1439 '_SESSION',
|
Chris@0
|
1440 '_REQUEST',
|
Chris@0
|
1441 '_ENV',
|
Chris@0
|
1442 'argv',
|
Chris@0
|
1443 'argc',
|
Chris@0
|
1444 )
|
Chris@0
|
1445 ) === true
|
Chris@0
|
1446 ) {
|
Chris@0
|
1447 return true;
|
Chris@0
|
1448 }
|
Chris@0
|
1449
|
Chris@0
|
1450 return false;
|
Chris@0
|
1451
|
Chris@0
|
1452 }//end checkForSuperGlobal()
|
Chris@0
|
1453
|
Chris@0
|
1454
|
Chris@0
|
1455 /**
|
Chris@0
|
1456 * Checks if the variable is a static class member.
|
Chris@0
|
1457 *
|
Chris@0
|
1458 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1459 * @param int $stackPtr
|
Chris@0
|
1460 * @param string $varName
|
Chris@0
|
1461 * @param string $currScope
|
Chris@0
|
1462 *
|
Chris@0
|
1463 * @return bool
|
Chris@0
|
1464 */
|
Chris@0
|
1465 protected function checkForStaticMember(
|
Chris@0
|
1466 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1467 $stackPtr,
|
Chris@0
|
1468 $varName,
|
Chris@0
|
1469 $currScope
|
Chris@0
|
1470 ) {
|
Chris@0
|
1471 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1472 $token = $tokens[$stackPtr];
|
Chris@0
|
1473
|
Chris@0
|
1474 // Are we a static member?
|
Chris@0
|
1475 $doubleColonPtr = ($stackPtr - 1);
|
Chris@0
|
1476 if ($tokens[$doubleColonPtr]['code'] !== T_DOUBLE_COLON) {
|
Chris@0
|
1477 return false;
|
Chris@0
|
1478 }
|
Chris@0
|
1479
|
Chris@0
|
1480 $classNamePtr = ($stackPtr - 2);
|
Chris@0
|
1481 if (($tokens[$classNamePtr]['code'] !== T_STRING)
|
Chris@0
|
1482 && ($tokens[$classNamePtr]['code'] !== T_SELF)
|
Chris@0
|
1483 && ($tokens[$classNamePtr]['code'] !== T_STATIC)
|
Chris@0
|
1484 ) {
|
Chris@0
|
1485 return false;
|
Chris@0
|
1486 }
|
Chris@0
|
1487
|
Chris@0
|
1488 // Are we referring to self:: outside a class?
|
Chris@0
|
1489 // TODO: not sure this is our business or should be some other sniff.
|
Chris@0
|
1490 if (($tokens[$classNamePtr]['code'] === T_SELF)
|
Chris@0
|
1491 || ($tokens[$classNamePtr]['code'] === T_STATIC)
|
Chris@0
|
1492 ) {
|
Chris@0
|
1493 if ($tokens[$classNamePtr]['code'] === T_SELF) {
|
Chris@0
|
1494 $err_class = 'SelfOutsideClass';
|
Chris@0
|
1495 $err_desc = 'self::';
|
Chris@0
|
1496 } else {
|
Chris@0
|
1497 $err_class = 'StaticOutsideClass';
|
Chris@0
|
1498 $err_desc = 'static::';
|
Chris@0
|
1499 }
|
Chris@0
|
1500
|
Chris@0
|
1501 if (empty($token['conditions']) === false) {
|
Chris@0
|
1502 foreach (array_reverse($token['conditions'], true) as $scopePtr => $scopeCode) {
|
Chris@0
|
1503 // Self within a closure is invalid.
|
Chris@0
|
1504 // Note: have to fetch code from $tokens, T_CLOSURE isn't set for conditions codes.
|
Chris@0
|
1505 if ($tokens[$scopePtr]['code'] === T_CLOSURE) {
|
Chris@0
|
1506 $phpcsFile->addError(
|
Chris@0
|
1507 "Use of {$err_desc}%s inside closure.",
|
Chris@0
|
1508 $stackPtr,
|
Chris@0
|
1509 $err_class,
|
Chris@0
|
1510 array("\${$varName}")
|
Chris@0
|
1511 );
|
Chris@0
|
1512 return true;
|
Chris@0
|
1513 }
|
Chris@0
|
1514
|
Chris@0
|
1515 if ($scopeCode === T_CLASS || $scopeCode === T_TRAIT) {
|
Chris@0
|
1516 return true;
|
Chris@0
|
1517 }
|
Chris@0
|
1518 }
|
Chris@0
|
1519 }
|
Chris@0
|
1520
|
Chris@0
|
1521 $phpcsFile->addError(
|
Chris@0
|
1522 "Use of {$err_desc}%s outside class definition.",
|
Chris@0
|
1523 $stackPtr,
|
Chris@0
|
1524 $err_class,
|
Chris@0
|
1525 array("\${$varName}")
|
Chris@0
|
1526 );
|
Chris@0
|
1527 return true;
|
Chris@0
|
1528 }//end if
|
Chris@0
|
1529
|
Chris@0
|
1530 return true;
|
Chris@0
|
1531
|
Chris@0
|
1532 }//end checkForStaticMember()
|
Chris@0
|
1533
|
Chris@0
|
1534
|
Chris@0
|
1535 /**
|
Chris@0
|
1536 * Checks if the variable is being assigned to.
|
Chris@0
|
1537 *
|
Chris@0
|
1538 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1539 * @param int $stackPtr
|
Chris@0
|
1540 * @param string $varName
|
Chris@0
|
1541 * @param string $currScope
|
Chris@0
|
1542 *
|
Chris@0
|
1543 * @return bool
|
Chris@0
|
1544 */
|
Chris@0
|
1545 protected function checkForAssignment(
|
Chris@0
|
1546 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1547 $stackPtr,
|
Chris@0
|
1548 $varName,
|
Chris@0
|
1549 $currScope
|
Chris@0
|
1550 ) {
|
Chris@0
|
1551 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1552
|
Chris@0
|
1553 // Is the next non-whitespace an assignment?
|
Chris@0
|
1554 if (($assignPtr = $this->isNextThingAnAssign($phpcsFile, $stackPtr)) === false) {
|
Chris@0
|
1555 return false;
|
Chris@0
|
1556 }
|
Chris@0
|
1557
|
Chris@0
|
1558 // Plain ol' assignment. Simpl(ish).
|
Chris@0
|
1559 if (($writtenPtr = $this->findWhereAssignExecuted($phpcsFile, $assignPtr)) === false) {
|
Chris@0
|
1560 $writtenPtr = $stackPtr;
|
Chris@0
|
1561 // I dunno.
|
Chris@0
|
1562 }
|
Chris@0
|
1563
|
Chris@0
|
1564 // Check for the ampersand '&' after the assignment, which means this
|
Chris@0
|
1565 // variable is taken by reference.
|
Chris@0
|
1566 $refPtr = $phpcsFile->findNext(T_WHITESPACE, ($assignPtr + 1), null, true);
|
Chris@0
|
1567 if ($tokens[$refPtr]['code'] === T_BITWISE_AND) {
|
Chris@0
|
1568 $varInfo = $this->getVariableInfo($varName, $currScope);
|
Chris@0
|
1569 $varInfo->passByReference = true;
|
Chris@0
|
1570 }
|
Chris@0
|
1571
|
Chris@0
|
1572 $this->markVariableAssignment($varName, $writtenPtr, $currScope);
|
Chris@0
|
1573 return true;
|
Chris@0
|
1574
|
Chris@0
|
1575 }//end checkForAssignment()
|
Chris@0
|
1576
|
Chris@0
|
1577
|
Chris@0
|
1578 /**
|
Chris@0
|
1579 * Check if this is a list language construct assignment.
|
Chris@0
|
1580 *
|
Chris@0
|
1581 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1582 * @param int $stackPtr
|
Chris@0
|
1583 * @param string $varName
|
Chris@0
|
1584 * @param string $currScope
|
Chris@0
|
1585 *
|
Chris@0
|
1586 * @return bool
|
Chris@0
|
1587 */
|
Chris@0
|
1588 protected function checkForListAssignment(
|
Chris@0
|
1589 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1590 $stackPtr,
|
Chris@0
|
1591 $varName,
|
Chris@0
|
1592 $currScope
|
Chris@0
|
1593 ) {
|
Chris@0
|
1594 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1595
|
Chris@0
|
1596 // OK, are we within a list (...) construct?
|
Chris@0
|
1597 if (($openPtr = $this->findContainingBrackets($phpcsFile, $stackPtr)) === false) {
|
Chris@0
|
1598 return false;
|
Chris@0
|
1599 }
|
Chris@0
|
1600
|
Chris@0
|
1601 $prevPtr = $phpcsFile->findPrevious(T_WHITESPACE, ($openPtr - 1), null, true, null, true);
|
Chris@0
|
1602 if (($prevPtr === false) || ($tokens[$prevPtr]['code'] !== T_LIST)) {
|
Chris@0
|
1603 return false;
|
Chris@0
|
1604 }
|
Chris@0
|
1605
|
Chris@0
|
1606 // OK, we're a list (...) construct... are we being assigned to?
|
Chris@0
|
1607 $closePtr = $tokens[$openPtr]['parenthesis_closer'];
|
Chris@0
|
1608 if (($assignPtr = $this->isNextThingAnAssign($phpcsFile, $closePtr)) === false) {
|
Chris@0
|
1609 return false;
|
Chris@0
|
1610 }
|
Chris@0
|
1611
|
Chris@0
|
1612 // Yes, we're being assigned.
|
Chris@0
|
1613 $writtenPtr = $this->findWhereAssignExecuted($phpcsFile, $assignPtr);
|
Chris@0
|
1614 $this->markVariableAssignment($varName, $writtenPtr, $currScope);
|
Chris@0
|
1615 return true;
|
Chris@0
|
1616
|
Chris@0
|
1617 }//end checkForListAssignment()
|
Chris@0
|
1618
|
Chris@0
|
1619
|
Chris@0
|
1620 /**
|
Chris@0
|
1621 * Check if this variable is declared globally.
|
Chris@0
|
1622 *
|
Chris@0
|
1623 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1624 * @param int $stackPtr
|
Chris@0
|
1625 * @param string $varName
|
Chris@0
|
1626 * @param string $currScope
|
Chris@0
|
1627 *
|
Chris@0
|
1628 * @return bool
|
Chris@0
|
1629 */
|
Chris@0
|
1630 protected function checkForGlobalDeclaration(
|
Chris@0
|
1631 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1632 $stackPtr,
|
Chris@0
|
1633 $varName,
|
Chris@0
|
1634 $currScope
|
Chris@0
|
1635 ) {
|
Chris@0
|
1636 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1637
|
Chris@0
|
1638 // Are we a global declaration?
|
Chris@0
|
1639 // Search backwards for first token that isn't whitespace, comma or variable.
|
Chris@0
|
1640 $globalPtr = $phpcsFile->findPrevious(
|
Chris@0
|
1641 array(
|
Chris@0
|
1642 T_WHITESPACE,
|
Chris@0
|
1643 T_VARIABLE,
|
Chris@0
|
1644 T_COMMA,
|
Chris@0
|
1645 ),
|
Chris@0
|
1646 ($stackPtr - 1),
|
Chris@0
|
1647 null,
|
Chris@0
|
1648 true,
|
Chris@0
|
1649 null,
|
Chris@0
|
1650 true
|
Chris@0
|
1651 );
|
Chris@0
|
1652 if (($globalPtr === false) || ($tokens[$globalPtr]['code'] !== T_GLOBAL)) {
|
Chris@0
|
1653 return false;
|
Chris@0
|
1654 }
|
Chris@0
|
1655
|
Chris@0
|
1656 // It's a global declaration.
|
Chris@0
|
1657 $this->markVariableDeclaration($varName, 'global', null, $stackPtr, $currScope);
|
Chris@0
|
1658 // Also mark this variable as being a reference, so that we don't get
|
Chris@0
|
1659 // unused variable warnings if it is never read.
|
Chris@0
|
1660 $varInfo = $this->getVariableInfo($varName, $currScope);
|
Chris@0
|
1661 $varInfo->passByReference = true;
|
Chris@0
|
1662 return true;
|
Chris@0
|
1663
|
Chris@0
|
1664 }//end checkForGlobalDeclaration()
|
Chris@0
|
1665
|
Chris@0
|
1666
|
Chris@0
|
1667 /**
|
Chris@0
|
1668 * Check is this is a static variable declaration.
|
Chris@0
|
1669 *
|
Chris@0
|
1670 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1671 * @param int $stackPtr
|
Chris@0
|
1672 * @param string $varName
|
Chris@0
|
1673 * @param string $currScope
|
Chris@0
|
1674 *
|
Chris@0
|
1675 * @return bool
|
Chris@0
|
1676 */
|
Chris@0
|
1677 protected function checkForStaticDeclaration(
|
Chris@0
|
1678 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1679 $stackPtr,
|
Chris@0
|
1680 $varName,
|
Chris@0
|
1681 $currScope
|
Chris@0
|
1682 ) {
|
Chris@0
|
1683 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1684
|
Chris@0
|
1685 // Are we a static declaration?
|
Chris@0
|
1686 // Static declarations are a bit more complicated than globals, since they
|
Chris@0
|
1687 // can contain assignments. The assignment is compile-time however so can
|
Chris@0
|
1688 // only be constant values, which makes life manageable.
|
Chris@0
|
1689 //
|
Chris@0
|
1690 // Just to complicate matters further, late static binding constants
|
Chris@0
|
1691 // take the form static::CONSTANT and are invalid within static variable
|
Chris@0
|
1692 // assignments, but we don't want to accidentally match their use of the
|
Chris@0
|
1693 // static keyword.
|
Chris@0
|
1694 //
|
Chris@0
|
1695 // Valid values are:
|
Chris@0
|
1696 // number T_MINUS T_LNUMBER T_DNUMBER
|
Chris@0
|
1697 // string T_CONSTANT_ENCAPSED_STRING
|
Chris@0
|
1698 // heredoc T_START_HEREDOC T_HEREDOC T_END_HEREDOC
|
Chris@0
|
1699 // nowdoc T_START_NOWDOC T_NOWDOC T_END_NOWDOC
|
Chris@0
|
1700 // define T_STRING
|
Chris@0
|
1701 // class constant T_STRING T_DOUBLE_COLON T_STRING
|
Chris@0
|
1702 // Search backwards for first token that isn't whitespace, comma, variable,
|
Chris@0
|
1703 // equals, or on the list of assignable constant values above.
|
Chris@0
|
1704 $staticPtr = $phpcsFile->findPrevious(
|
Chris@0
|
1705 array(
|
Chris@0
|
1706 T_WHITESPACE,
|
Chris@0
|
1707 T_VARIABLE,
|
Chris@0
|
1708 T_COMMA,
|
Chris@0
|
1709 T_EQUAL,
|
Chris@0
|
1710 T_MINUS,
|
Chris@0
|
1711 T_LNUMBER,
|
Chris@0
|
1712 T_DNUMBER,
|
Chris@0
|
1713 T_CONSTANT_ENCAPSED_STRING,
|
Chris@0
|
1714 T_STRING,
|
Chris@0
|
1715 T_DOUBLE_COLON,
|
Chris@0
|
1716 T_START_HEREDOC,
|
Chris@0
|
1717 T_HEREDOC,
|
Chris@0
|
1718 T_END_HEREDOC,
|
Chris@0
|
1719 T_START_NOWDOC,
|
Chris@0
|
1720 T_NOWDOC,
|
Chris@0
|
1721 T_END_NOWDOC,
|
Chris@0
|
1722 ),
|
Chris@0
|
1723 ($stackPtr - 1),
|
Chris@0
|
1724 null,
|
Chris@0
|
1725 true,
|
Chris@0
|
1726 null,
|
Chris@0
|
1727 true
|
Chris@0
|
1728 );
|
Chris@0
|
1729 if (($staticPtr === false) || ($tokens[$staticPtr]['code'] !== T_STATIC)) {
|
Chris@0
|
1730 // Debug code.
|
Chris@0
|
1731 // if ($varName == 'static4') {
|
Chris@0
|
1732 // echo "Failing token:\n" . print_r($tokens[$staticPtr], true);
|
Chris@0
|
1733 // }
|
Chris@0
|
1734 // End: Debug code.
|
Chris@0
|
1735 return false;
|
Chris@0
|
1736 }
|
Chris@0
|
1737
|
Chris@0
|
1738 // Is it a late static binding static::?
|
Chris@0
|
1739 // If so, this isn't the static keyword we're looking for, but since
|
Chris@0
|
1740 // static:: isn't allowed in a compile-time constant, we also know
|
Chris@0
|
1741 // we can't be part of a static declaration anyway, so there's no
|
Chris@0
|
1742 // need to look any further.
|
Chris@0
|
1743 $lateStaticBindingPtr = $phpcsFile->findNext(T_WHITESPACE, ($staticPtr + 1), null, true, null, true);
|
Chris@0
|
1744 if (($lateStaticBindingPtr !== false) && ($tokens[$lateStaticBindingPtr]['code'] === T_DOUBLE_COLON)) {
|
Chris@0
|
1745 return false;
|
Chris@0
|
1746 }
|
Chris@0
|
1747
|
Chris@0
|
1748 // It's a static declaration.
|
Chris@0
|
1749 $this->markVariableDeclaration($varName, 'static', null, $stackPtr, $currScope);
|
Chris@0
|
1750 if ($this->isNextThingAnAssign($phpcsFile, $stackPtr) !== false) {
|
Chris@0
|
1751 $this->markVariableAssignment($varName, $stackPtr, $currScope);
|
Chris@0
|
1752 }
|
Chris@0
|
1753
|
Chris@0
|
1754 return true;
|
Chris@0
|
1755
|
Chris@0
|
1756 }//end checkForStaticDeclaration()
|
Chris@0
|
1757
|
Chris@0
|
1758
|
Chris@0
|
1759 /**
|
Chris@0
|
1760 * Check if this is a foreach loop variable.
|
Chris@0
|
1761 *
|
Chris@0
|
1762 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1763 * @param int $stackPtr
|
Chris@0
|
1764 * @param string $varName
|
Chris@0
|
1765 * @param string $currScope
|
Chris@0
|
1766 *
|
Chris@0
|
1767 * @return bool
|
Chris@0
|
1768 */
|
Chris@0
|
1769 protected function checkForForeachLoopVar(
|
Chris@0
|
1770 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1771 $stackPtr,
|
Chris@0
|
1772 $varName,
|
Chris@0
|
1773 $currScope
|
Chris@0
|
1774 ) {
|
Chris@0
|
1775 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1776
|
Chris@0
|
1777 // Are we a foreach loopvar?
|
Chris@0
|
1778 if (($openPtr = $this->findContainingBrackets($phpcsFile, $stackPtr)) === false) {
|
Chris@0
|
1779 return false;
|
Chris@0
|
1780 }
|
Chris@0
|
1781
|
Chris@0
|
1782 // Is there an 'as' token between us and the opening bracket?
|
Chris@0
|
1783 if ($phpcsFile->findPrevious(T_AS, ($stackPtr - 1), $openPtr) === false) {
|
Chris@0
|
1784 return false;
|
Chris@0
|
1785 }
|
Chris@0
|
1786
|
Chris@0
|
1787 $this->markVariableAssignment($varName, $stackPtr, $currScope);
|
Chris@0
|
1788
|
Chris@0
|
1789 // Workaround: We want to allow foreach ($array as $key => $value) where
|
Chris@0
|
1790 // $value is never read, so we just mark it read immediately here.
|
Chris@0
|
1791 if ($phpcsFile->findPrevious(T_DOUBLE_ARROW, ($stackPtr - 1), $openPtr) !== false) {
|
Chris@0
|
1792 $this->markVariableRead($varName, $stackPtr, $currScope);
|
Chris@0
|
1793 }
|
Chris@0
|
1794
|
Chris@0
|
1795 // Foreach variables that are read as references like
|
Chris@0
|
1796 // foreach ($array as &$value) should not throw unused variable errors.
|
Chris@0
|
1797 if (($refPtr = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true)) !== false
|
Chris@0
|
1798 && $tokens[$refPtr]['code'] === T_BITWISE_AND
|
Chris@0
|
1799 ) {
|
Chris@0
|
1800 $varInfo = $this->getVariableInfo($varName, $currScope);
|
Chris@0
|
1801 $varInfo->passByReference = true;
|
Chris@0
|
1802 }
|
Chris@0
|
1803
|
Chris@0
|
1804 return true;
|
Chris@0
|
1805
|
Chris@0
|
1806 }//end checkForForeachLoopVar()
|
Chris@0
|
1807
|
Chris@0
|
1808
|
Chris@0
|
1809 /**
|
Chris@0
|
1810 * Check if this is a "&" function call.
|
Chris@0
|
1811 *
|
Chris@0
|
1812 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1813 * @param int $stackPtr
|
Chris@0
|
1814 * @param string $varName
|
Chris@0
|
1815 * @param string $currScope
|
Chris@0
|
1816 *
|
Chris@0
|
1817 * @return bool
|
Chris@0
|
1818 */
|
Chris@0
|
1819 protected function checkForPassByReferenceFunctionCall(
|
Chris@0
|
1820 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1821 $stackPtr,
|
Chris@0
|
1822 $varName,
|
Chris@0
|
1823 $currScope
|
Chris@0
|
1824 ) {
|
Chris@0
|
1825 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1826 $token = $tokens[$stackPtr];
|
Chris@0
|
1827
|
Chris@0
|
1828 // Are we pass-by-reference to known pass-by-reference function?
|
Chris@0
|
1829 if (($functionPtr = $this->findFunctionCall($phpcsFile, $stackPtr)) === false) {
|
Chris@0
|
1830 return false;
|
Chris@0
|
1831 }
|
Chris@0
|
1832
|
Chris@0
|
1833 // Is our function a known pass-by-reference function?
|
Chris@0
|
1834 $functionName = $tokens[$functionPtr]['content'];
|
Chris@0
|
1835 if (isset($this->_passByRefFunctions[$functionName]) === false) {
|
Chris@0
|
1836 return false;
|
Chris@0
|
1837 }
|
Chris@0
|
1838
|
Chris@0
|
1839 $refArgs = $this->_passByRefFunctions[$functionName];
|
Chris@0
|
1840
|
Chris@0
|
1841 if (($argPtrs = $this->findFunctionCallArguments($phpcsFile, $stackPtr)) === false) {
|
Chris@0
|
1842 return false;
|
Chris@0
|
1843 }
|
Chris@0
|
1844
|
Chris@0
|
1845 // We're within a function call arguments list, find which arg we are.
|
Chris@0
|
1846 $argPos = false;
|
Chris@0
|
1847 foreach ($argPtrs as $idx => $ptrs) {
|
Chris@0
|
1848 if (in_array($stackPtr, $ptrs) === true) {
|
Chris@0
|
1849 $argPos = ($idx + 1);
|
Chris@0
|
1850 break;
|
Chris@0
|
1851 }
|
Chris@0
|
1852 }
|
Chris@0
|
1853
|
Chris@0
|
1854 if ($argPos === false) {
|
Chris@0
|
1855 return false;
|
Chris@0
|
1856 }
|
Chris@0
|
1857
|
Chris@0
|
1858 if (in_array($argPos, $refArgs) === false) {
|
Chris@0
|
1859 // Our arg wasn't mentioned explicitly, are we after an elipsis catch-all?
|
Chris@0
|
1860 if (($elipsis = array_search('...', $refArgs)) === false) {
|
Chris@0
|
1861 return false;
|
Chris@0
|
1862 }
|
Chris@0
|
1863
|
Chris@0
|
1864 if ($argPos < $refArgs[($elipsis - 1)]) {
|
Chris@0
|
1865 return false;
|
Chris@0
|
1866 }
|
Chris@0
|
1867 }
|
Chris@0
|
1868
|
Chris@0
|
1869 // Our argument position matches that of a pass-by-ref argument,
|
Chris@0
|
1870 // check that we're the only part of the argument expression.
|
Chris@0
|
1871 foreach ($argPtrs[($argPos - 1)] as $ptr) {
|
Chris@0
|
1872 if ($ptr === $stackPtr) {
|
Chris@0
|
1873 continue;
|
Chris@0
|
1874 }
|
Chris@0
|
1875
|
Chris@0
|
1876 if ($tokens[$ptr]['code'] !== T_WHITESPACE) {
|
Chris@0
|
1877 return false;
|
Chris@0
|
1878 }
|
Chris@0
|
1879 }
|
Chris@0
|
1880
|
Chris@0
|
1881 // Just us, we can mark it as a write.
|
Chris@0
|
1882 $this->markVariableAssignment($varName, $stackPtr, $currScope);
|
Chris@0
|
1883 // It's a read as well for purposes of used-variables.
|
Chris@0
|
1884 $this->markVariableRead($varName, $stackPtr, $currScope);
|
Chris@0
|
1885 return true;
|
Chris@0
|
1886
|
Chris@0
|
1887 }//end checkForPassByReferenceFunctionCall()
|
Chris@0
|
1888
|
Chris@0
|
1889
|
Chris@0
|
1890 /**
|
Chris@0
|
1891 * Check if the variable is an object property.
|
Chris@0
|
1892 *
|
Chris@0
|
1893 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
1894 * @param int $stackPtr
|
Chris@0
|
1895 * @param string $varName
|
Chris@0
|
1896 * @param string $currScope
|
Chris@0
|
1897 *
|
Chris@0
|
1898 * @return bool
|
Chris@0
|
1899 */
|
Chris@0
|
1900 protected function checkForSymbolicObjectProperty(
|
Chris@0
|
1901 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1902 $stackPtr,
|
Chris@0
|
1903 $varName,
|
Chris@0
|
1904 $currScope
|
Chris@0
|
1905 ) {
|
Chris@0
|
1906 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1907 $token = $tokens[$stackPtr];
|
Chris@0
|
1908
|
Chris@0
|
1909 // Are we a symbolic object property/function derefeference?
|
Chris@0
|
1910 // Search backwards for first token that isn't whitespace, is it a "->" operator?
|
Chris@0
|
1911 $objectOperatorPtr = $phpcsFile->findPrevious(
|
Chris@0
|
1912 T_WHITESPACE,
|
Chris@0
|
1913 ($stackPtr - 1),
|
Chris@0
|
1914 null,
|
Chris@0
|
1915 true,
|
Chris@0
|
1916 null,
|
Chris@0
|
1917 true
|
Chris@0
|
1918 );
|
Chris@0
|
1919 if (($objectOperatorPtr === false) || ($tokens[$objectOperatorPtr]['code'] !== T_OBJECT_OPERATOR)) {
|
Chris@0
|
1920 return false;
|
Chris@0
|
1921 }
|
Chris@0
|
1922
|
Chris@0
|
1923 $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope);
|
Chris@0
|
1924 return true;
|
Chris@0
|
1925
|
Chris@0
|
1926 }//end checkForSymbolicObjectProperty()
|
Chris@0
|
1927
|
Chris@0
|
1928
|
Chris@0
|
1929 /**
|
Chris@0
|
1930 * Called to process class member vars.
|
Chris@0
|
1931 *
|
Chris@0
|
1932 * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this
|
Chris@0
|
1933 * token was found.
|
Chris@0
|
1934 * @param int $stackPtr The position where the token was found.
|
Chris@0
|
1935 *
|
Chris@0
|
1936 * @return void
|
Chris@0
|
1937 */
|
Chris@0
|
1938 protected function processMemberVar(
|
Chris@0
|
1939 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1940 $stackPtr
|
Chris@0
|
1941 ) {
|
Chris@0
|
1942 // TODO: don't care for now.
|
Chris@0
|
1943 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1944 $token = $tokens[$stackPtr];
|
Chris@0
|
1945
|
Chris@0
|
1946 }//end processMemberVar()
|
Chris@0
|
1947
|
Chris@0
|
1948
|
Chris@0
|
1949 /**
|
Chris@0
|
1950 * Called to process normal member vars.
|
Chris@0
|
1951 *
|
Chris@0
|
1952 * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this
|
Chris@0
|
1953 * token was found.
|
Chris@0
|
1954 * @param int $stackPtr The position where the token was found.
|
Chris@0
|
1955 *
|
Chris@0
|
1956 * @return void
|
Chris@0
|
1957 */
|
Chris@0
|
1958 protected function processVariable(
|
Chris@0
|
1959 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
1960 $stackPtr
|
Chris@0
|
1961 ) {
|
Chris@0
|
1962 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
1963 $token = $tokens[$stackPtr];
|
Chris@0
|
1964
|
Chris@0
|
1965 $varName = $this->normalizeVarName($token['content']);
|
Chris@0
|
1966 if (($currScope = $this->findVariableScope($phpcsFile, $stackPtr)) === false) {
|
Chris@0
|
1967 return;
|
Chris@0
|
1968 }
|
Chris@0
|
1969
|
Chris@0
|
1970 // Debug code.
|
Chris@0
|
1971 // static $dump_token = false;
|
Chris@0
|
1972 // if ($varName == 'property') {
|
Chris@0
|
1973 // $dump_token = true;
|
Chris@0
|
1974 // }
|
Chris@0
|
1975 // if ($dump_token) {
|
Chris@0
|
1976 // echo "Found variable {$varName} on line {$token['line']} in scope {$currScope}.\n" . print_r($token, true);
|
Chris@0
|
1977 // echo "Prev:\n" . print_r($tokens[$stackPtr - 1], true);
|
Chris@0
|
1978 // }
|
Chris@0
|
1979 // Determine if variable is being assigned or read.
|
Chris@0
|
1980 // Read methods that preempt assignment:
|
Chris@0
|
1981 // Are we a $object->$property type symbolic reference?
|
Chris@0
|
1982 // Possible assignment methods:
|
Chris@0
|
1983 // Is a mandatory function/closure parameter
|
Chris@0
|
1984 // Is an optional function/closure parameter with non-null value
|
Chris@0
|
1985 // Is closure use declaration of a variable defined within containing scope
|
Chris@0
|
1986 // catch (...) block start
|
Chris@0
|
1987 // $this within a class (but not within a closure).
|
Chris@0
|
1988 // $GLOBALS, $_REQUEST, etc superglobals.
|
Chris@0
|
1989 // $var part of class::$var static member
|
Chris@0
|
1990 // Assignment via =
|
Chris@0
|
1991 // Assignment via list (...) =
|
Chris@0
|
1992 // Declares as a global
|
Chris@0
|
1993 // Declares as a static
|
Chris@0
|
1994 // Assignment via foreach (... as ...) { }
|
Chris@0
|
1995 // Pass-by-reference to known pass-by-reference function
|
Chris@0
|
1996 // Are we a $object->$property type symbolic reference?
|
Chris@0
|
1997 if ($this->checkForSymbolicObjectProperty($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
1998 return;
|
Chris@0
|
1999 }
|
Chris@0
|
2000
|
Chris@0
|
2001 // Are we a function or closure parameter?
|
Chris@0
|
2002 if ($this->checkForFunctionPrototype($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2003 return;
|
Chris@0
|
2004 }
|
Chris@0
|
2005
|
Chris@0
|
2006 // Are we a catch parameter?
|
Chris@0
|
2007 if ($this->checkForCatchBlock($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2008 return;
|
Chris@0
|
2009 }
|
Chris@0
|
2010
|
Chris@0
|
2011 // Are we $this within a class?
|
Chris@0
|
2012 if ($this->checkForThisWithinClass($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2013 return;
|
Chris@0
|
2014 }
|
Chris@0
|
2015
|
Chris@0
|
2016 // Are we a $GLOBALS, $_REQUEST, etc superglobal?
|
Chris@0
|
2017 if ($this->checkForSuperGlobal($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2018 return;
|
Chris@0
|
2019 }
|
Chris@0
|
2020
|
Chris@0
|
2021 // $var part of class::$var static member
|
Chris@0
|
2022 if ($this->checkForStaticMember($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2023 return;
|
Chris@0
|
2024 }
|
Chris@0
|
2025
|
Chris@0
|
2026 // Is the next non-whitespace an assignment?
|
Chris@0
|
2027 if ($this->checkForAssignment($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2028 return;
|
Chris@0
|
2029 }
|
Chris@0
|
2030
|
Chris@0
|
2031 // OK, are we within a list (...) = construct?
|
Chris@0
|
2032 if ($this->checkForListAssignment($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2033 return;
|
Chris@0
|
2034 }
|
Chris@0
|
2035
|
Chris@0
|
2036 // Are we a global declaration?
|
Chris@0
|
2037 if ($this->checkForGlobalDeclaration($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2038 return;
|
Chris@0
|
2039 }
|
Chris@0
|
2040
|
Chris@0
|
2041 // Are we a static declaration?
|
Chris@0
|
2042 if ($this->checkForStaticDeclaration($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2043 return;
|
Chris@0
|
2044 }
|
Chris@0
|
2045
|
Chris@0
|
2046 // Are we a foreach loopvar?
|
Chris@0
|
2047 if ($this->checkForForeachLoopVar($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2048 return;
|
Chris@0
|
2049 }
|
Chris@0
|
2050
|
Chris@0
|
2051 // Are we pass-by-reference to known pass-by-reference function?
|
Chris@0
|
2052 if ($this->checkForPassByReferenceFunctionCall($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2053 return;
|
Chris@0
|
2054 }
|
Chris@0
|
2055
|
Chris@0
|
2056 // OK, we don't appear to be a write to the var, assume we're a read.
|
Chris@0
|
2057 $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope);
|
Chris@0
|
2058
|
Chris@0
|
2059 }//end processVariable()
|
Chris@0
|
2060
|
Chris@0
|
2061
|
Chris@0
|
2062 /**
|
Chris@0
|
2063 * Called to process variables found in double quoted strings.
|
Chris@0
|
2064 *
|
Chris@0
|
2065 * Note that there may be more than one variable in the string, which will
|
Chris@0
|
2066 * result only in one call for the string.
|
Chris@0
|
2067 *
|
Chris@0
|
2068 * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this
|
Chris@0
|
2069 * token was found.
|
Chris@0
|
2070 * @param int $stackPtr The position where the double quoted
|
Chris@0
|
2071 * string was found.
|
Chris@0
|
2072 *
|
Chris@0
|
2073 * @return void
|
Chris@0
|
2074 */
|
Chris@0
|
2075 protected function processVariableInString(
|
Chris@0
|
2076 PHP_CodeSniffer_File
|
Chris@0
|
2077 $phpcsFile,
|
Chris@0
|
2078 $stackPtr
|
Chris@0
|
2079 ) {
|
Chris@0
|
2080 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
2081 $token = $tokens[$stackPtr];
|
Chris@0
|
2082
|
Chris@0
|
2083 $runMatch = preg_match_all($this->_double_quoted_variable_regexp, $token['content'], $matches);
|
Chris@0
|
2084 if ($runMatch === 0 || $runMatch === false) {
|
Chris@0
|
2085 return;
|
Chris@0
|
2086 }
|
Chris@0
|
2087
|
Chris@0
|
2088 $currScope = $this->findVariableScope($phpcsFile, $stackPtr);
|
Chris@0
|
2089 foreach ($matches[1] as $varName) {
|
Chris@0
|
2090 $varName = $this->normalizeVarName($varName);
|
Chris@0
|
2091 // Are we $this within a class?
|
Chris@0
|
2092 if ($this->checkForThisWithinClass($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2093 continue;
|
Chris@0
|
2094 }
|
Chris@0
|
2095
|
Chris@0
|
2096 if ($this->checkForSuperGlobal($phpcsFile, $stackPtr, $varName, $currScope) === true) {
|
Chris@0
|
2097 continue;
|
Chris@0
|
2098 }
|
Chris@0
|
2099
|
Chris@0
|
2100 $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope);
|
Chris@0
|
2101 }
|
Chris@0
|
2102
|
Chris@0
|
2103 }//end processVariableInString()
|
Chris@0
|
2104
|
Chris@0
|
2105
|
Chris@0
|
2106 /**
|
Chris@0
|
2107 * Check variables in a compact() call.
|
Chris@0
|
2108 *
|
Chris@0
|
2109 * @param PHP_CodeSniffer_File $phpcsFile
|
Chris@0
|
2110 * @param int $stackPtr
|
Chris@0
|
2111 * @param array $arguments
|
Chris@0
|
2112 * @param string $currScope
|
Chris@0
|
2113 *
|
Chris@0
|
2114 * @return void
|
Chris@0
|
2115 */
|
Chris@0
|
2116 protected function processCompactArguments(
|
Chris@0
|
2117 PHP_CodeSniffer_File
|
Chris@0
|
2118 $phpcsFile,
|
Chris@0
|
2119 $stackPtr,
|
Chris@0
|
2120 $arguments,
|
Chris@0
|
2121 $currScope
|
Chris@0
|
2122 ) {
|
Chris@0
|
2123 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
2124
|
Chris@0
|
2125 foreach ($arguments as $argumentPtrs) {
|
Chris@0
|
2126 $argumentPtrs = array_values(
|
Chris@0
|
2127 array_filter(
|
Chris@0
|
2128 $argumentPtrs,
|
Chris@0
|
2129 function ($argumentPtr) use ($tokens) {
|
Chris@0
|
2130 return $tokens[$argumentPtr]['code'] !== T_WHITESPACE;
|
Chris@0
|
2131 }
|
Chris@0
|
2132 )
|
Chris@0
|
2133 );
|
Chris@0
|
2134 if (empty($argumentPtrs) === true) {
|
Chris@0
|
2135 continue;
|
Chris@0
|
2136 }
|
Chris@0
|
2137
|
Chris@0
|
2138 if (isset($tokens[$argumentPtrs[0]]) === false) {
|
Chris@0
|
2139 continue;
|
Chris@0
|
2140 }
|
Chris@0
|
2141
|
Chris@0
|
2142 $argument_first_token = $tokens[$argumentPtrs[0]];
|
Chris@0
|
2143 if ($argument_first_token['code'] === T_ARRAY) {
|
Chris@0
|
2144 // It's an array argument, recurse.
|
Chris@0
|
2145 if (($array_arguments = $this->findFunctionCallArguments($phpcsFile, $argumentPtrs[0])) !== false) {
|
Chris@0
|
2146 $this->processCompactArguments($phpcsFile, $stackPtr, $array_arguments, $currScope);
|
Chris@0
|
2147 }
|
Chris@0
|
2148
|
Chris@0
|
2149 continue;
|
Chris@0
|
2150 }
|
Chris@0
|
2151
|
Chris@0
|
2152 if (count($argumentPtrs) > 1) {
|
Chris@0
|
2153 // Complex argument, we can't handle it, ignore.
|
Chris@0
|
2154 continue;
|
Chris@0
|
2155 }
|
Chris@0
|
2156
|
Chris@0
|
2157 if ($argument_first_token['code'] === T_CONSTANT_ENCAPSED_STRING) {
|
Chris@0
|
2158 // Single-quoted string literal, ie compact('whatever').
|
Chris@0
|
2159 // Substr is to strip the enclosing single-quotes.
|
Chris@0
|
2160 $varName = substr($argument_first_token['content'], 1, -1);
|
Chris@0
|
2161 $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $argumentPtrs[0], $currScope);
|
Chris@0
|
2162 continue;
|
Chris@0
|
2163 }
|
Chris@0
|
2164
|
Chris@0
|
2165 if ($argument_first_token['code'] === T_DOUBLE_QUOTED_STRING) {
|
Chris@0
|
2166 // Double-quoted string literal.
|
Chris@0
|
2167 if (preg_match($this->_double_quoted_variable_regexp, $argument_first_token['content']) === 1) {
|
Chris@0
|
2168 // Bail if the string needs variable expansion, that's runtime stuff.
|
Chris@0
|
2169 continue;
|
Chris@0
|
2170 }
|
Chris@0
|
2171
|
Chris@0
|
2172 // Substr is to strip the enclosing double-quotes.
|
Chris@0
|
2173 $varName = substr($argument_first_token['content'], 1, -1);
|
Chris@0
|
2174 $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $argumentPtrs[0], $currScope);
|
Chris@0
|
2175 continue;
|
Chris@0
|
2176 }
|
Chris@0
|
2177 }//end foreach
|
Chris@0
|
2178
|
Chris@0
|
2179 }//end processCompactArguments()
|
Chris@0
|
2180
|
Chris@0
|
2181
|
Chris@0
|
2182 /**
|
Chris@0
|
2183 * Called to process variables named in a call to compact().
|
Chris@0
|
2184 *
|
Chris@0
|
2185 * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this
|
Chris@0
|
2186 * token was found.
|
Chris@0
|
2187 * @param int $stackPtr The position where the call to compact()
|
Chris@0
|
2188 * was found.
|
Chris@0
|
2189 *
|
Chris@0
|
2190 * @return void
|
Chris@0
|
2191 */
|
Chris@0
|
2192 protected function processCompact(
|
Chris@0
|
2193 PHP_CodeSniffer_File
|
Chris@0
|
2194 $phpcsFile,
|
Chris@0
|
2195 $stackPtr
|
Chris@0
|
2196 ) {
|
Chris@0
|
2197 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
2198 $token = $tokens[$stackPtr];
|
Chris@0
|
2199
|
Chris@0
|
2200 $currScope = $this->findVariableScope($phpcsFile, $stackPtr);
|
Chris@0
|
2201
|
Chris@0
|
2202 if (($arguments = $this->findFunctionCallArguments($phpcsFile, $stackPtr)) !== false) {
|
Chris@0
|
2203 $this->processCompactArguments($phpcsFile, $stackPtr, $arguments, $currScope);
|
Chris@0
|
2204 }
|
Chris@0
|
2205
|
Chris@0
|
2206 }//end processCompact()
|
Chris@0
|
2207
|
Chris@0
|
2208
|
Chris@0
|
2209 /**
|
Chris@0
|
2210 * Called to process the end of a scope.
|
Chris@0
|
2211 *
|
Chris@0
|
2212 * Note that although triggered by the closing curly brace of the scope, $stackPtr is
|
Chris@0
|
2213 * the scope conditional, not the closing curly brace.
|
Chris@0
|
2214 *
|
Chris@0
|
2215 * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this
|
Chris@0
|
2216 * token was found.
|
Chris@0
|
2217 * @param int $stackPtr The position of the scope conditional.
|
Chris@0
|
2218 *
|
Chris@0
|
2219 * @return void
|
Chris@0
|
2220 */
|
Chris@0
|
2221 protected function processScopeClose(
|
Chris@0
|
2222 PHP_CodeSniffer_File
|
Chris@0
|
2223 $phpcsFile,
|
Chris@0
|
2224 $stackPtr
|
Chris@0
|
2225 ) {
|
Chris@0
|
2226 $scopeInfo = $this->getScopeInfo($stackPtr, false);
|
Chris@0
|
2227 if (is_null($scopeInfo) === true) {
|
Chris@0
|
2228 return;
|
Chris@0
|
2229 }
|
Chris@0
|
2230
|
Chris@0
|
2231 foreach ($scopeInfo->variables as $varInfo) {
|
Chris@0
|
2232 if (($varInfo->ignoreUnused === true) || (isset($varInfo->firstRead) === true)) {
|
Chris@0
|
2233 continue;
|
Chris@0
|
2234 }
|
Chris@0
|
2235
|
Chris@0
|
2236 if (($this->allowUnusedFunctionParameters === true) && ($varInfo->scopeType === 'param')) {
|
Chris@0
|
2237 continue;
|
Chris@0
|
2238 }
|
Chris@0
|
2239
|
Chris@0
|
2240 if (($varInfo->passByReference === true) && isset($varInfo->lastAssignment) === true) {
|
Chris@0
|
2241 // If we're pass-by-reference then it's a common pattern to
|
Chris@0
|
2242 // use the variable to return data to the caller, so any
|
Chris@0
|
2243 // assignment also counts as "variable use" for the purposes
|
Chris@0
|
2244 // of "unused variable" warnings.
|
Chris@0
|
2245 continue;
|
Chris@0
|
2246 }
|
Chris@0
|
2247
|
Chris@0
|
2248 if (isset($varInfo->firstDeclared) === true) {
|
Chris@0
|
2249 $phpcsFile->addWarning(
|
Chris@0
|
2250 "Unused %s %s.",
|
Chris@0
|
2251 $varInfo->firstDeclared,
|
Chris@0
|
2252 'UnusedVariable',
|
Chris@0
|
2253 array(
|
Chris@0
|
2254 VariableInfo::$scopeTypeDescriptions[$varInfo->scopeType],
|
Chris@0
|
2255 "\${$varInfo->name}",
|
Chris@0
|
2256 )
|
Chris@0
|
2257 );
|
Chris@0
|
2258 } else if (isset($varInfo->firstInitialized) === true) {
|
Chris@0
|
2259 $phpcsFile->addWarning(
|
Chris@0
|
2260 "Unused %s %s.",
|
Chris@0
|
2261 $varInfo->firstInitialized,
|
Chris@0
|
2262 'UnusedVariable',
|
Chris@0
|
2263 array(
|
Chris@0
|
2264 VariableInfo::$scopeTypeDescriptions[$varInfo->scopeType],
|
Chris@0
|
2265 "\${$varInfo->name}",
|
Chris@0
|
2266 )
|
Chris@0
|
2267 );
|
Chris@0
|
2268 }//end if
|
Chris@0
|
2269 }//end foreach
|
Chris@0
|
2270
|
Chris@0
|
2271 }//end processScopeClose()
|
Chris@0
|
2272
|
Chris@0
|
2273
|
Chris@0
|
2274 }//end class
|