Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@0
|
3 * A PHP_CodeSniffer_File object represents a PHP source file and the tokens
|
Chris@0
|
4 * associated with it.
|
Chris@0
|
5 *
|
Chris@0
|
6 * PHP version 5
|
Chris@0
|
7 *
|
Chris@0
|
8 * @category PHP
|
Chris@0
|
9 * @package PHP_CodeSniffer
|
Chris@0
|
10 * @author Greg Sherwood <gsherwood@squiz.net>
|
Chris@0
|
11 * @author Marc McIntyre <mmcintyre@squiz.net>
|
Chris@0
|
12 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
|
Chris@0
|
13 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
|
Chris@0
|
14 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
15 */
|
Chris@0
|
16
|
Chris@0
|
17 /**
|
Chris@0
|
18 * A PHP_CodeSniffer_File object represents a PHP source file and the tokens
|
Chris@0
|
19 * associated with it.
|
Chris@0
|
20 *
|
Chris@0
|
21 * It provides a means for traversing the token stack, along with
|
Chris@0
|
22 * other token related operations. If a PHP_CodeSniffer_Sniff finds and error or
|
Chris@0
|
23 * warning within a PHP_CodeSniffer_File, you can raise an error using the
|
Chris@0
|
24 * addError() or addWarning() methods.
|
Chris@0
|
25 *
|
Chris@0
|
26 * <b>Token Information</b>
|
Chris@0
|
27 *
|
Chris@0
|
28 * Each token within the stack contains information about itself:
|
Chris@0
|
29 *
|
Chris@0
|
30 * <code>
|
Chris@0
|
31 * array(
|
Chris@0
|
32 * 'code' => 301, // the token type code (see token_get_all())
|
Chris@0
|
33 * 'content' => 'if', // the token content
|
Chris@0
|
34 * 'type' => 'T_IF', // the token name
|
Chris@0
|
35 * 'line' => 56, // the line number when the token is located
|
Chris@0
|
36 * 'column' => 12, // the column in the line where this token
|
Chris@0
|
37 * // starts (starts from 1)
|
Chris@0
|
38 * 'level' => 2 // the depth a token is within the scopes open
|
Chris@0
|
39 * 'conditions' => array( // a list of scope condition token
|
Chris@0
|
40 * // positions => codes that
|
Chris@0
|
41 * 2 => 50, // opened the scopes that this token exists
|
Chris@0
|
42 * 9 => 353, // in (see conditional tokens section below)
|
Chris@0
|
43 * ),
|
Chris@0
|
44 * );
|
Chris@0
|
45 * </code>
|
Chris@0
|
46 *
|
Chris@0
|
47 * <b>Conditional Tokens</b>
|
Chris@0
|
48 *
|
Chris@0
|
49 * In addition to the standard token fields, conditions contain information to
|
Chris@0
|
50 * determine where their scope begins and ends:
|
Chris@0
|
51 *
|
Chris@0
|
52 * <code>
|
Chris@0
|
53 * array(
|
Chris@0
|
54 * 'scope_condition' => 38, // the token position of the condition
|
Chris@0
|
55 * 'scope_opener' => 41, // the token position that started the scope
|
Chris@0
|
56 * 'scope_closer' => 70, // the token position that ended the scope
|
Chris@0
|
57 * );
|
Chris@0
|
58 * </code>
|
Chris@0
|
59 *
|
Chris@0
|
60 * The condition, the scope opener and the scope closer each contain this
|
Chris@0
|
61 * information.
|
Chris@0
|
62 *
|
Chris@0
|
63 * <b>Parenthesis Tokens</b>
|
Chris@0
|
64 *
|
Chris@0
|
65 * Each parenthesis token (T_OPEN_PARENTHESIS and T_CLOSE_PARENTHESIS) has a
|
Chris@0
|
66 * reference to their opening and closing parenthesis, one being itself, the
|
Chris@0
|
67 * other being its opposite.
|
Chris@0
|
68 *
|
Chris@0
|
69 * <code>
|
Chris@0
|
70 * array(
|
Chris@0
|
71 * 'parenthesis_opener' => 34,
|
Chris@0
|
72 * 'parenthesis_closer' => 40,
|
Chris@0
|
73 * );
|
Chris@0
|
74 * </code>
|
Chris@0
|
75 *
|
Chris@0
|
76 * Some tokens can "own" a set of parenthesis. For example a T_FUNCTION token
|
Chris@0
|
77 * has parenthesis around its argument list. These tokens also have the
|
Chris@0
|
78 * parenthesis_opener and and parenthesis_closer indices. Not all parenthesis
|
Chris@0
|
79 * have owners, for example parenthesis used for arithmetic operations and
|
Chris@0
|
80 * function calls. The parenthesis tokens that have an owner have the following
|
Chris@0
|
81 * auxiliary array indices.
|
Chris@0
|
82 *
|
Chris@0
|
83 * <code>
|
Chris@0
|
84 * array(
|
Chris@0
|
85 * 'parenthesis_opener' => 34,
|
Chris@0
|
86 * 'parenthesis_closer' => 40,
|
Chris@0
|
87 * 'parenthesis_owner' => 33,
|
Chris@0
|
88 * );
|
Chris@0
|
89 * </code>
|
Chris@0
|
90 *
|
Chris@0
|
91 * Each token within a set of parenthesis also has an array index
|
Chris@0
|
92 * 'nested_parenthesis' which is an array of the
|
Chris@0
|
93 * left parenthesis => right parenthesis token positions.
|
Chris@0
|
94 *
|
Chris@0
|
95 * <code>
|
Chris@0
|
96 * 'nested_parenthesis' => array(
|
Chris@0
|
97 * 12 => 15
|
Chris@0
|
98 * 11 => 14
|
Chris@0
|
99 * );
|
Chris@0
|
100 * </code>
|
Chris@0
|
101 *
|
Chris@0
|
102 * <b>Extended Tokens</b>
|
Chris@0
|
103 *
|
Chris@0
|
104 * PHP_CodeSniffer extends and augments some of the tokens created by
|
Chris@0
|
105 * <i>token_get_all()</i>. A full list of these tokens can be seen in the
|
Chris@0
|
106 * <i>Tokens.php</i> file.
|
Chris@0
|
107 *
|
Chris@0
|
108 * @category PHP
|
Chris@0
|
109 * @package PHP_CodeSniffer
|
Chris@0
|
110 * @author Greg Sherwood <gsherwood@squiz.net>
|
Chris@0
|
111 * @author Marc McIntyre <mmcintyre@squiz.net>
|
Chris@0
|
112 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
|
Chris@0
|
113 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
|
Chris@0
|
114 * @version Release: @package_version@
|
Chris@0
|
115 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
116 */
|
Chris@0
|
117 class PHP_CodeSniffer_File
|
Chris@0
|
118 {
|
Chris@0
|
119
|
Chris@0
|
120 /**
|
Chris@0
|
121 * The absolute path to the file associated with this object.
|
Chris@0
|
122 *
|
Chris@0
|
123 * @var string
|
Chris@0
|
124 */
|
Chris@0
|
125 private $_file = '';
|
Chris@0
|
126
|
Chris@0
|
127 /**
|
Chris@0
|
128 * The EOL character this file uses.
|
Chris@0
|
129 *
|
Chris@0
|
130 * @var string
|
Chris@0
|
131 */
|
Chris@0
|
132 public $eolChar = '';
|
Chris@0
|
133
|
Chris@0
|
134 /**
|
Chris@0
|
135 * The PHP_CodeSniffer object controlling this run.
|
Chris@0
|
136 *
|
Chris@0
|
137 * @var PHP_CodeSniffer
|
Chris@0
|
138 */
|
Chris@0
|
139 public $phpcs = null;
|
Chris@0
|
140
|
Chris@0
|
141 /**
|
Chris@0
|
142 * The Fixer object to control fixing errors.
|
Chris@0
|
143 *
|
Chris@0
|
144 * @var PHP_CodeSniffer_Fixer
|
Chris@0
|
145 */
|
Chris@0
|
146 public $fixer = null;
|
Chris@0
|
147
|
Chris@0
|
148 /**
|
Chris@0
|
149 * The tokenizer being used for this file.
|
Chris@0
|
150 *
|
Chris@0
|
151 * @var object
|
Chris@0
|
152 */
|
Chris@0
|
153 public $tokenizer = null;
|
Chris@0
|
154
|
Chris@0
|
155 /**
|
Chris@0
|
156 * The tokenizer being used for this file.
|
Chris@0
|
157 *
|
Chris@0
|
158 * @var string
|
Chris@0
|
159 */
|
Chris@0
|
160 public $tokenizerType = 'PHP';
|
Chris@0
|
161
|
Chris@0
|
162 /**
|
Chris@0
|
163 * The number of tokens in this file.
|
Chris@0
|
164 *
|
Chris@0
|
165 * Stored here to save calling count() everywhere.
|
Chris@0
|
166 *
|
Chris@0
|
167 * @var int
|
Chris@0
|
168 */
|
Chris@0
|
169 public $numTokens = 0;
|
Chris@0
|
170
|
Chris@0
|
171 /**
|
Chris@0
|
172 * The tokens stack map.
|
Chris@0
|
173 *
|
Chris@0
|
174 * Note that the tokens in this array differ in format to the tokens
|
Chris@0
|
175 * produced by token_get_all(). Tokens are initially produced with
|
Chris@0
|
176 * token_get_all(), then augmented so that it's easier to process them.
|
Chris@0
|
177 *
|
Chris@0
|
178 * @var array()
|
Chris@0
|
179 * @see Tokens.php
|
Chris@0
|
180 */
|
Chris@0
|
181 private $_tokens = array();
|
Chris@0
|
182
|
Chris@0
|
183 /**
|
Chris@0
|
184 * The errors raised from PHP_CodeSniffer_Sniffs.
|
Chris@0
|
185 *
|
Chris@0
|
186 * @var array()
|
Chris@0
|
187 * @see getErrors()
|
Chris@0
|
188 */
|
Chris@0
|
189 private $_errors = array();
|
Chris@0
|
190
|
Chris@0
|
191 /**
|
Chris@0
|
192 * The warnings raised from PHP_CodeSniffer_Sniffs.
|
Chris@0
|
193 *
|
Chris@0
|
194 * @var array()
|
Chris@0
|
195 * @see getWarnings()
|
Chris@0
|
196 */
|
Chris@0
|
197 private $_warnings = array();
|
Chris@0
|
198
|
Chris@0
|
199 /**
|
Chris@0
|
200 * The metrics recorded from PHP_CodeSniffer_Sniffs.
|
Chris@0
|
201 *
|
Chris@0
|
202 * @var array()
|
Chris@0
|
203 * @see getMetrics()
|
Chris@0
|
204 */
|
Chris@0
|
205 private $_metrics = array();
|
Chris@0
|
206
|
Chris@0
|
207 /**
|
Chris@0
|
208 * Record the errors and warnings raised.
|
Chris@0
|
209 *
|
Chris@0
|
210 * @var bool
|
Chris@0
|
211 */
|
Chris@0
|
212 private $_recordErrors = true;
|
Chris@0
|
213
|
Chris@0
|
214 /**
|
Chris@0
|
215 * An array of lines that are being ignored.
|
Chris@0
|
216 *
|
Chris@0
|
217 * @var array()
|
Chris@0
|
218 */
|
Chris@0
|
219 private static $_ignoredLines = array();
|
Chris@0
|
220
|
Chris@0
|
221 /**
|
Chris@0
|
222 * An array of sniffs that are being ignored.
|
Chris@0
|
223 *
|
Chris@0
|
224 * @var array()
|
Chris@0
|
225 */
|
Chris@0
|
226 private $_ignoredListeners = array();
|
Chris@0
|
227
|
Chris@0
|
228 /**
|
Chris@0
|
229 * An array of message codes that are being ignored.
|
Chris@0
|
230 *
|
Chris@0
|
231 * @var array()
|
Chris@0
|
232 */
|
Chris@0
|
233 private $_ignoredCodes = array();
|
Chris@0
|
234
|
Chris@0
|
235 /**
|
Chris@0
|
236 * The total number of errors raised.
|
Chris@0
|
237 *
|
Chris@0
|
238 * @var int
|
Chris@0
|
239 */
|
Chris@0
|
240 private $_errorCount = 0;
|
Chris@0
|
241
|
Chris@0
|
242 /**
|
Chris@0
|
243 * The total number of warnings raised.
|
Chris@0
|
244 *
|
Chris@0
|
245 * @var int
|
Chris@0
|
246 */
|
Chris@0
|
247 private $_warningCount = 0;
|
Chris@0
|
248
|
Chris@0
|
249 /**
|
Chris@0
|
250 * The total number of errors/warnings that can be fixed.
|
Chris@0
|
251 *
|
Chris@0
|
252 * @var int
|
Chris@0
|
253 */
|
Chris@0
|
254 private $_fixableCount = 0;
|
Chris@0
|
255
|
Chris@0
|
256 /**
|
Chris@0
|
257 * An array of sniffs listening to this file's processing.
|
Chris@0
|
258 *
|
Chris@0
|
259 * @var array(PHP_CodeSniffer_Sniff)
|
Chris@0
|
260 */
|
Chris@0
|
261 private $_listeners = array();
|
Chris@0
|
262
|
Chris@0
|
263 /**
|
Chris@0
|
264 * The class name of the sniff currently processing the file.
|
Chris@0
|
265 *
|
Chris@0
|
266 * @var string
|
Chris@0
|
267 */
|
Chris@0
|
268 private $_activeListener = '';
|
Chris@0
|
269
|
Chris@0
|
270 /**
|
Chris@0
|
271 * An array of sniffs being processed and how long they took.
|
Chris@0
|
272 *
|
Chris@0
|
273 * @var array()
|
Chris@0
|
274 */
|
Chris@0
|
275 private $_listenerTimes = array();
|
Chris@0
|
276
|
Chris@0
|
277 /**
|
Chris@0
|
278 * An array of rules from the ruleset.xml file.
|
Chris@0
|
279 *
|
Chris@0
|
280 * This value gets set by PHP_CodeSniffer when the object is created.
|
Chris@0
|
281 * It may be empty, indicating that the ruleset does not override
|
Chris@0
|
282 * any of the default sniff settings.
|
Chris@0
|
283 *
|
Chris@0
|
284 * @var array
|
Chris@0
|
285 */
|
Chris@0
|
286 protected $ruleset = array();
|
Chris@0
|
287
|
Chris@0
|
288
|
Chris@0
|
289 /**
|
Chris@0
|
290 * Constructs a PHP_CodeSniffer_File.
|
Chris@0
|
291 *
|
Chris@0
|
292 * @param string $file The absolute path to the file to process.
|
Chris@0
|
293 * @param array(string) $listeners The initial listeners listening to processing of this file.
|
Chris@0
|
294 * to processing of this file.
|
Chris@0
|
295 * @param array $ruleset An array of rules from the ruleset.xml file.
|
Chris@0
|
296 * ruleset.xml file.
|
Chris@0
|
297 * @param PHP_CodeSniffer $phpcs The PHP_CodeSniffer object controlling this run.
|
Chris@0
|
298 * this run.
|
Chris@0
|
299 *
|
Chris@0
|
300 * @throws PHP_CodeSniffer_Exception If the register() method does
|
Chris@0
|
301 * not return an array.
|
Chris@0
|
302 */
|
Chris@0
|
303 public function __construct(
|
Chris@0
|
304 $file,
|
Chris@0
|
305 array $listeners,
|
Chris@0
|
306 array $ruleset,
|
Chris@0
|
307 PHP_CodeSniffer $phpcs
|
Chris@0
|
308 ) {
|
Chris@0
|
309 $this->_file = trim($file);
|
Chris@0
|
310 $this->_listeners = $listeners;
|
Chris@0
|
311 $this->ruleset = $ruleset;
|
Chris@0
|
312 $this->phpcs = $phpcs;
|
Chris@0
|
313 $this->fixer = new PHP_CodeSniffer_Fixer();
|
Chris@0
|
314
|
Chris@0
|
315 if (PHP_CODESNIFFER_INTERACTIVE === false) {
|
Chris@0
|
316 $cliValues = $phpcs->cli->getCommandLineValues();
|
Chris@0
|
317 if (isset($cliValues['showSources']) === true
|
Chris@0
|
318 && $cliValues['showSources'] !== true
|
Chris@0
|
319 ) {
|
Chris@0
|
320 $recordErrors = false;
|
Chris@0
|
321 foreach ($cliValues['reports'] as $report => $output) {
|
Chris@0
|
322 $reportClass = $phpcs->reporting->factory($report);
|
Chris@0
|
323 if (property_exists($reportClass, 'recordErrors') === false
|
Chris@0
|
324 || $reportClass->recordErrors === true
|
Chris@0
|
325 ) {
|
Chris@0
|
326 $recordErrors = true;
|
Chris@0
|
327 break;
|
Chris@0
|
328 }
|
Chris@0
|
329 }
|
Chris@0
|
330
|
Chris@0
|
331 $this->_recordErrors = $recordErrors;
|
Chris@0
|
332 }
|
Chris@0
|
333 }
|
Chris@0
|
334
|
Chris@0
|
335 }//end __construct()
|
Chris@0
|
336
|
Chris@0
|
337
|
Chris@0
|
338 /**
|
Chris@0
|
339 * Sets the name of the currently active sniff.
|
Chris@0
|
340 *
|
Chris@0
|
341 * @param string $activeListener The class name of the current sniff.
|
Chris@0
|
342 *
|
Chris@0
|
343 * @return void
|
Chris@0
|
344 */
|
Chris@0
|
345 public function setActiveListener($activeListener)
|
Chris@0
|
346 {
|
Chris@0
|
347 $this->_activeListener = $activeListener;
|
Chris@0
|
348
|
Chris@0
|
349 }//end setActiveListener()
|
Chris@0
|
350
|
Chris@0
|
351
|
Chris@0
|
352 /**
|
Chris@0
|
353 * Adds a listener to the token stack that listens to the specific tokens.
|
Chris@0
|
354 *
|
Chris@0
|
355 * When PHP_CodeSniffer encounters on the the tokens specified in $tokens,
|
Chris@0
|
356 * it invokes the process method of the sniff.
|
Chris@0
|
357 *
|
Chris@0
|
358 * @param PHP_CodeSniffer_Sniff $listener The listener to add to the
|
Chris@0
|
359 * listener stack.
|
Chris@0
|
360 * @param array(int) $tokens The token types the listener wishes to
|
Chris@0
|
361 * listen to.
|
Chris@0
|
362 *
|
Chris@0
|
363 * @return void
|
Chris@0
|
364 */
|
Chris@0
|
365 public function addTokenListener(PHP_CodeSniffer_Sniff $listener, array $tokens)
|
Chris@0
|
366 {
|
Chris@0
|
367 $class = get_class($listener);
|
Chris@0
|
368 foreach ($tokens as $token) {
|
Chris@0
|
369 if (isset($this->_listeners[$token]) === false) {
|
Chris@0
|
370 $this->_listeners[$token] = array();
|
Chris@0
|
371 }
|
Chris@0
|
372
|
Chris@0
|
373 if (isset($this->_listeners[$token][$class]) === false) {
|
Chris@0
|
374 $this->_listeners[$token][$class] = $listener;
|
Chris@0
|
375 }
|
Chris@0
|
376 }
|
Chris@0
|
377
|
Chris@0
|
378 }//end addTokenListener()
|
Chris@0
|
379
|
Chris@0
|
380
|
Chris@0
|
381 /**
|
Chris@0
|
382 * Removes a listener from listening from the specified tokens.
|
Chris@0
|
383 *
|
Chris@0
|
384 * @param PHP_CodeSniffer_Sniff $listener The listener to remove from the
|
Chris@0
|
385 * listener stack.
|
Chris@0
|
386 * @param array(int) $tokens The token types the listener wishes to
|
Chris@0
|
387 * stop listen to.
|
Chris@0
|
388 *
|
Chris@0
|
389 * @return void
|
Chris@0
|
390 */
|
Chris@0
|
391 public function removeTokenListener(
|
Chris@0
|
392 PHP_CodeSniffer_Sniff $listener,
|
Chris@0
|
393 array $tokens
|
Chris@0
|
394 ) {
|
Chris@0
|
395 $class = get_class($listener);
|
Chris@0
|
396 foreach ($tokens as $token) {
|
Chris@0
|
397 if (isset($this->_listeners[$token]) === false) {
|
Chris@0
|
398 continue;
|
Chris@0
|
399 }
|
Chris@0
|
400
|
Chris@0
|
401 unset($this->_listeners[$token][$class]);
|
Chris@0
|
402 }
|
Chris@0
|
403
|
Chris@0
|
404 }//end removeTokenListener()
|
Chris@0
|
405
|
Chris@0
|
406
|
Chris@0
|
407 /**
|
Chris@0
|
408 * Rebuilds the list of listeners to ensure their state is cleared.
|
Chris@0
|
409 *
|
Chris@0
|
410 * @return void
|
Chris@0
|
411 */
|
Chris@0
|
412 public function refreshTokenListeners()
|
Chris@0
|
413 {
|
Chris@0
|
414 $this->phpcs->populateTokenListeners();
|
Chris@0
|
415 $this->_listeners = $this->phpcs->getTokenSniffs();
|
Chris@0
|
416
|
Chris@0
|
417 }//end refreshTokenListeners()
|
Chris@0
|
418
|
Chris@0
|
419
|
Chris@0
|
420 /**
|
Chris@0
|
421 * Returns the token stack for this file.
|
Chris@0
|
422 *
|
Chris@0
|
423 * @return array
|
Chris@0
|
424 */
|
Chris@0
|
425 public function getTokens()
|
Chris@0
|
426 {
|
Chris@0
|
427 return $this->_tokens;
|
Chris@0
|
428
|
Chris@0
|
429 }//end getTokens()
|
Chris@0
|
430
|
Chris@0
|
431
|
Chris@0
|
432 /**
|
Chris@0
|
433 * Starts the stack traversal and tells listeners when tokens are found.
|
Chris@0
|
434 *
|
Chris@0
|
435 * @param string $contents The contents to parse. If NULL, the content
|
Chris@0
|
436 * is taken from the file system.
|
Chris@0
|
437 *
|
Chris@0
|
438 * @return void
|
Chris@0
|
439 */
|
Chris@0
|
440 public function start($contents=null)
|
Chris@0
|
441 {
|
Chris@0
|
442 $this->_errors = array();
|
Chris@0
|
443 $this->_warnings = array();
|
Chris@0
|
444 $this->_errorCount = 0;
|
Chris@0
|
445 $this->_warningCount = 0;
|
Chris@0
|
446 $this->_fixableCount = 0;
|
Chris@0
|
447
|
Chris@0
|
448 // Reset the ignored lines because lines numbers may have changed
|
Chris@0
|
449 // if we are fixing this file.
|
Chris@0
|
450 self::$_ignoredLines = array();
|
Chris@0
|
451
|
Chris@0
|
452 try {
|
Chris@0
|
453 $this->eolChar = self::detectLineEndings($this->_file, $contents);
|
Chris@0
|
454 } catch (PHP_CodeSniffer_Exception $e) {
|
Chris@0
|
455 $this->addWarning($e->getMessage(), null, 'Internal.DetectLineEndings');
|
Chris@0
|
456 return;
|
Chris@0
|
457 }
|
Chris@0
|
458
|
Chris@0
|
459 // If this is standard input, see if a filename was passed in as well.
|
Chris@0
|
460 // This is done by including: phpcs_input_file: [file path]
|
Chris@0
|
461 // as the first line of content.
|
Chris@0
|
462 if ($this->_file === 'STDIN') {
|
Chris@0
|
463 $cliValues = $this->phpcs->cli->getCommandLineValues();
|
Chris@0
|
464 if ($cliValues['stdinPath'] !== '') {
|
Chris@0
|
465 $this->_file = $cliValues['stdinPath'];
|
Chris@0
|
466 } else if ($contents !== null && substr($contents, 0, 17) === 'phpcs_input_file:') {
|
Chris@0
|
467 $eolPos = strpos($contents, $this->eolChar);
|
Chris@0
|
468 $filename = trim(substr($contents, 17, ($eolPos - 17)));
|
Chris@0
|
469 $contents = substr($contents, ($eolPos + strlen($this->eolChar)));
|
Chris@0
|
470 $this->_file = $filename;
|
Chris@0
|
471 }
|
Chris@0
|
472 }
|
Chris@0
|
473
|
Chris@0
|
474 $this->_parse($contents);
|
Chris@0
|
475 $this->fixer->startFile($this);
|
Chris@0
|
476
|
Chris@0
|
477 if (PHP_CODESNIFFER_VERBOSITY > 2) {
|
Chris@0
|
478 echo "\t*** START TOKEN PROCESSING ***".PHP_EOL;
|
Chris@0
|
479 }
|
Chris@0
|
480
|
Chris@0
|
481 $foundCode = false;
|
Chris@0
|
482 $listeners = $this->phpcs->getSniffs();
|
Chris@0
|
483 $listenerIgnoreTo = array();
|
Chris@0
|
484 $inTests = defined('PHP_CODESNIFFER_IN_TESTS');
|
Chris@0
|
485
|
Chris@0
|
486 // Foreach of the listeners that have registered to listen for this
|
Chris@0
|
487 // token, get them to process it.
|
Chris@0
|
488 foreach ($this->_tokens as $stackPtr => $token) {
|
Chris@0
|
489 // Check for ignored lines.
|
Chris@0
|
490 if ($token['code'] === T_COMMENT
|
Chris@0
|
491 || $token['code'] === T_DOC_COMMENT_TAG
|
Chris@0
|
492 || ($inTests === true && $token['code'] === T_INLINE_HTML)
|
Chris@0
|
493 ) {
|
Chris@0
|
494 if (strpos($token['content'], '@codingStandards') !== false) {
|
Chris@0
|
495 if (strpos($token['content'], '@codingStandardsIgnoreFile') !== false) {
|
Chris@0
|
496 // Ignoring the whole file, just a little late.
|
Chris@0
|
497 $this->_errors = array();
|
Chris@0
|
498 $this->_warnings = array();
|
Chris@0
|
499 $this->_errorCount = 0;
|
Chris@0
|
500 $this->_warningCount = 0;
|
Chris@0
|
501 $this->_fixableCount = 0;
|
Chris@0
|
502 return;
|
Chris@0
|
503 } else if (strpos($token['content'], '@codingStandardsChangeSetting') !== false) {
|
Chris@0
|
504 $start = strpos($token['content'], '@codingStandardsChangeSetting');
|
Chris@0
|
505 $comment = substr($token['content'], ($start + 30));
|
Chris@0
|
506 $parts = explode(' ', $comment);
|
Chris@0
|
507 if (count($parts) >= 3
|
Chris@0
|
508 && isset($this->phpcs->sniffCodes[$parts[0]]) === true
|
Chris@0
|
509 ) {
|
Chris@0
|
510 $listenerCode = array_shift($parts);
|
Chris@0
|
511 $propertyCode = array_shift($parts);
|
Chris@0
|
512 $propertyValue = rtrim(implode(' ', $parts), " */\r\n");
|
Chris@0
|
513 $listenerClass = $this->phpcs->sniffCodes[$listenerCode];
|
Chris@0
|
514 $this->phpcs->setSniffProperty($listenerClass, $propertyCode, $propertyValue);
|
Chris@0
|
515 }
|
Chris@0
|
516 }//end if
|
Chris@0
|
517 }//end if
|
Chris@0
|
518 }//end if
|
Chris@0
|
519
|
Chris@0
|
520 if (PHP_CODESNIFFER_VERBOSITY > 2) {
|
Chris@0
|
521 $type = $token['type'];
|
Chris@0
|
522 $content = PHP_CodeSniffer::prepareForOutput($token['content']);
|
Chris@0
|
523 echo "\t\tProcess token $stackPtr: $type => $content".PHP_EOL;
|
Chris@0
|
524 }
|
Chris@0
|
525
|
Chris@0
|
526 if ($token['code'] !== T_INLINE_HTML) {
|
Chris@0
|
527 $foundCode = true;
|
Chris@0
|
528 }
|
Chris@0
|
529
|
Chris@0
|
530 if (isset($this->_listeners[$token['code']]) === false) {
|
Chris@0
|
531 continue;
|
Chris@0
|
532 }
|
Chris@0
|
533
|
Chris@0
|
534 foreach ($this->_listeners[$token['code']] as $listenerData) {
|
Chris@0
|
535 if (isset($this->_ignoredListeners[$listenerData['class']]) === true
|
Chris@0
|
536 || (isset($listenerIgnoreTo[$listenerData['class']]) === true
|
Chris@0
|
537 && $listenerIgnoreTo[$listenerData['class']] > $stackPtr)
|
Chris@0
|
538 ) {
|
Chris@0
|
539 // This sniff is ignoring past this token, or the whole file.
|
Chris@0
|
540 continue;
|
Chris@0
|
541 }
|
Chris@0
|
542
|
Chris@0
|
543 // Make sure this sniff supports the tokenizer
|
Chris@0
|
544 // we are currently using.
|
Chris@0
|
545 $class = $listenerData['class'];
|
Chris@0
|
546
|
Chris@0
|
547 if (isset($listenerData['tokenizers'][$this->tokenizerType]) === false) {
|
Chris@0
|
548 continue;
|
Chris@0
|
549 }
|
Chris@0
|
550
|
Chris@0
|
551 // If the file path matches one of our ignore patterns, skip it.
|
Chris@0
|
552 // While there is support for a type of each pattern
|
Chris@0
|
553 // (absolute or relative) we don't actually support it here.
|
Chris@0
|
554 foreach ($listenerData['ignore'] as $pattern) {
|
Chris@0
|
555 // We assume a / directory separator, as do the exclude rules
|
Chris@0
|
556 // most developers write, so we need a special case for any system
|
Chris@0
|
557 // that is different.
|
Chris@0
|
558 if (DIRECTORY_SEPARATOR === '\\') {
|
Chris@0
|
559 $pattern = str_replace('/', '\\\\', $pattern);
|
Chris@0
|
560 }
|
Chris@0
|
561
|
Chris@0
|
562 $pattern = '`'.$pattern.'`i';
|
Chris@0
|
563 if (preg_match($pattern, $this->_file) === 1) {
|
Chris@0
|
564 $this->_ignoredListeners[$class] = true;
|
Chris@0
|
565 continue(2);
|
Chris@0
|
566 }
|
Chris@0
|
567 }
|
Chris@0
|
568
|
Chris@0
|
569 $this->_activeListener = $class;
|
Chris@0
|
570
|
Chris@0
|
571 if (PHP_CODESNIFFER_VERBOSITY > 2) {
|
Chris@0
|
572 $startTime = microtime(true);
|
Chris@0
|
573 echo "\t\t\tProcessing ".$this->_activeListener.'... ';
|
Chris@0
|
574 }
|
Chris@0
|
575
|
Chris@0
|
576 $ignoreTo = $listeners[$class]->process($this, $stackPtr);
|
Chris@0
|
577 if ($ignoreTo !== null) {
|
Chris@0
|
578 $listenerIgnoreTo[$this->_activeListener] = $ignoreTo;
|
Chris@0
|
579 }
|
Chris@0
|
580
|
Chris@0
|
581 if (PHP_CODESNIFFER_VERBOSITY > 2) {
|
Chris@0
|
582 $timeTaken = (microtime(true) - $startTime);
|
Chris@0
|
583 if (isset($this->_listenerTimes[$this->_activeListener]) === false) {
|
Chris@0
|
584 $this->_listenerTimes[$this->_activeListener] = 0;
|
Chris@0
|
585 }
|
Chris@0
|
586
|
Chris@0
|
587 $this->_listenerTimes[$this->_activeListener] += $timeTaken;
|
Chris@0
|
588
|
Chris@0
|
589 $timeTaken = round(($timeTaken), 4);
|
Chris@0
|
590 echo "DONE in $timeTaken seconds".PHP_EOL;
|
Chris@0
|
591 }
|
Chris@0
|
592
|
Chris@0
|
593 $this->_activeListener = '';
|
Chris@0
|
594 }//end foreach
|
Chris@0
|
595 }//end foreach
|
Chris@0
|
596
|
Chris@0
|
597 if ($this->_recordErrors === false) {
|
Chris@0
|
598 $this->_errors = array();
|
Chris@0
|
599 $this->_warnings = array();
|
Chris@0
|
600 }
|
Chris@0
|
601
|
Chris@0
|
602 // If short open tags are off but the file being checked uses
|
Chris@0
|
603 // short open tags, the whole content will be inline HTML
|
Chris@0
|
604 // and nothing will be checked. So try and handle this case.
|
Chris@0
|
605 // We don't show this error for STDIN because we can't be sure the content
|
Chris@0
|
606 // actually came directly from the user. It could be something like
|
Chris@0
|
607 // refs from a Git pre-push hook.
|
Chris@0
|
608 if ($foundCode === false && $this->tokenizerType === 'PHP' && $this->_file !== 'STDIN') {
|
Chris@0
|
609 $shortTags = (bool) ini_get('short_open_tag');
|
Chris@0
|
610 if ($shortTags === false) {
|
Chris@0
|
611 $error = 'No PHP code was found in this file and short open tags are not allowed by this install of PHP. This file may be using short open tags but PHP does not allow them.';
|
Chris@0
|
612 $this->addWarning($error, null, 'Internal.NoCodeFound');
|
Chris@0
|
613 }
|
Chris@0
|
614 }
|
Chris@0
|
615
|
Chris@0
|
616 if (PHP_CODESNIFFER_VERBOSITY > 2) {
|
Chris@0
|
617 echo "\t*** END TOKEN PROCESSING ***".PHP_EOL;
|
Chris@0
|
618 echo "\t*** START SNIFF PROCESSING REPORT ***".PHP_EOL;
|
Chris@0
|
619
|
Chris@0
|
620 asort($this->_listenerTimes, SORT_NUMERIC);
|
Chris@0
|
621 $this->_listenerTimes = array_reverse($this->_listenerTimes, true);
|
Chris@0
|
622 foreach ($this->_listenerTimes as $listener => $timeTaken) {
|
Chris@0
|
623 echo "\t$listener: ".round(($timeTaken), 4).' secs'.PHP_EOL;
|
Chris@0
|
624 }
|
Chris@0
|
625
|
Chris@0
|
626 echo "\t*** END SNIFF PROCESSING REPORT ***".PHP_EOL;
|
Chris@0
|
627 }
|
Chris@0
|
628
|
Chris@0
|
629 }//end start()
|
Chris@0
|
630
|
Chris@0
|
631
|
Chris@0
|
632 /**
|
Chris@0
|
633 * Remove vars stored in this file that are no longer required.
|
Chris@0
|
634 *
|
Chris@0
|
635 * @return void
|
Chris@0
|
636 */
|
Chris@0
|
637 public function cleanUp()
|
Chris@0
|
638 {
|
Chris@0
|
639 $this->_tokens = null;
|
Chris@0
|
640 $this->_listeners = null;
|
Chris@0
|
641
|
Chris@0
|
642 }//end cleanUp()
|
Chris@0
|
643
|
Chris@0
|
644
|
Chris@0
|
645 /**
|
Chris@0
|
646 * Tokenizes the file and prepares it for the test run.
|
Chris@0
|
647 *
|
Chris@0
|
648 * @param string $contents The contents to parse. If NULL, the content
|
Chris@0
|
649 * is taken from the file system.
|
Chris@0
|
650 *
|
Chris@0
|
651 * @return void
|
Chris@0
|
652 */
|
Chris@0
|
653 private function _parse($contents=null)
|
Chris@0
|
654 {
|
Chris@0
|
655 if ($contents === null && empty($this->_tokens) === false) {
|
Chris@0
|
656 // File has already been parsed.
|
Chris@0
|
657 return;
|
Chris@0
|
658 }
|
Chris@0
|
659
|
Chris@0
|
660 $stdin = false;
|
Chris@0
|
661 $cliValues = $this->phpcs->cli->getCommandLineValues();
|
Chris@0
|
662 if (empty($cliValues['files']) === true) {
|
Chris@0
|
663 $stdin = true;
|
Chris@0
|
664 }
|
Chris@0
|
665
|
Chris@0
|
666 // Determine the tokenizer from the file extension.
|
Chris@0
|
667 $fileParts = explode('.', $this->_file);
|
Chris@0
|
668 $extension = array_pop($fileParts);
|
Chris@0
|
669 if (isset($this->phpcs->allowedFileExtensions[$extension]) === true) {
|
Chris@0
|
670 $tokenizerClass = 'PHP_CodeSniffer_Tokenizers_'.$this->phpcs->allowedFileExtensions[$extension];
|
Chris@0
|
671 $this->tokenizerType = $this->phpcs->allowedFileExtensions[$extension];
|
Chris@0
|
672 } else if (isset($this->phpcs->defaultFileExtensions[$extension]) === true) {
|
Chris@0
|
673 $tokenizerClass = 'PHP_CodeSniffer_Tokenizers_'.$this->phpcs->defaultFileExtensions[$extension];
|
Chris@0
|
674 $this->tokenizerType = $this->phpcs->defaultFileExtensions[$extension];
|
Chris@0
|
675 } else {
|
Chris@0
|
676 // Revert to default.
|
Chris@0
|
677 $tokenizerClass = 'PHP_CodeSniffer_Tokenizers_'.$this->tokenizerType;
|
Chris@0
|
678 }
|
Chris@0
|
679
|
Chris@0
|
680 $tokenizer = new $tokenizerClass();
|
Chris@0
|
681 $this->tokenizer = $tokenizer;
|
Chris@0
|
682
|
Chris@0
|
683 if ($contents === null) {
|
Chris@0
|
684 $contents = file_get_contents($this->_file);
|
Chris@0
|
685 }
|
Chris@0
|
686
|
Chris@0
|
687 try {
|
Chris@0
|
688 $tabWidth = null;
|
Chris@0
|
689 $encoding = null;
|
Chris@0
|
690 if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
|
Chris@0
|
691 $cliValues = $this->phpcs->cli->getCommandLineValues();
|
Chris@0
|
692 if (isset($cliValues['tabWidth']) === true) {
|
Chris@0
|
693 $tabWidth = $cliValues['tabWidth'];
|
Chris@0
|
694 }
|
Chris@0
|
695
|
Chris@0
|
696 if (isset($cliValues['encoding']) === true) {
|
Chris@0
|
697 $encoding = $cliValues['encoding'];
|
Chris@0
|
698 }
|
Chris@0
|
699 }
|
Chris@0
|
700
|
Chris@0
|
701 $this->_tokens = self::tokenizeString($contents, $tokenizer, $this->eolChar, $tabWidth, $encoding);
|
Chris@0
|
702 } catch (PHP_CodeSniffer_Exception $e) {
|
Chris@0
|
703 $this->addWarning($e->getMessage(), null, 'Internal.Tokenizer.Exception');
|
Chris@0
|
704 if (PHP_CODESNIFFER_VERBOSITY > 0 || (PHP_CODESNIFFER_CBF === true && $stdin === false)) {
|
Chris@0
|
705 echo "[$this->tokenizerType => tokenizer error]... ";
|
Chris@0
|
706 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
707 echo PHP_EOL;
|
Chris@0
|
708 }
|
Chris@0
|
709 }
|
Chris@0
|
710
|
Chris@0
|
711 return;
|
Chris@0
|
712 }//end try
|
Chris@0
|
713
|
Chris@0
|
714 $this->numTokens = count($this->_tokens);
|
Chris@0
|
715
|
Chris@0
|
716 // Check for mixed line endings as these can cause tokenizer errors and we
|
Chris@0
|
717 // should let the user know that the results they get may be incorrect.
|
Chris@0
|
718 // This is done by removing all backslashes, removing the newline char we
|
Chris@0
|
719 // detected, then converting newlines chars into text. If any backslashes
|
Chris@0
|
720 // are left at the end, we have additional newline chars in use.
|
Chris@0
|
721 $contents = str_replace('\\', '', $contents);
|
Chris@0
|
722 $contents = str_replace($this->eolChar, '', $contents);
|
Chris@0
|
723 $contents = str_replace("\n", '\n', $contents);
|
Chris@0
|
724 $contents = str_replace("\r", '\r', $contents);
|
Chris@0
|
725 if (strpos($contents, '\\') !== false) {
|
Chris@0
|
726 $error = 'File has mixed line endings; this may cause incorrect results';
|
Chris@0
|
727 $this->addWarning($error, 0, 'Internal.LineEndings.Mixed');
|
Chris@0
|
728 }
|
Chris@0
|
729
|
Chris@0
|
730 if (PHP_CODESNIFFER_VERBOSITY > 0 || (PHP_CODESNIFFER_CBF === true && $stdin === false)) {
|
Chris@0
|
731 if ($this->numTokens === 0) {
|
Chris@0
|
732 $numLines = 0;
|
Chris@0
|
733 } else {
|
Chris@0
|
734 $numLines = $this->_tokens[($this->numTokens - 1)]['line'];
|
Chris@0
|
735 }
|
Chris@0
|
736
|
Chris@0
|
737 echo "[$this->tokenizerType => $this->numTokens tokens in $numLines lines]... ";
|
Chris@0
|
738 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
739 echo PHP_EOL;
|
Chris@0
|
740 }
|
Chris@0
|
741 }
|
Chris@0
|
742
|
Chris@0
|
743 }//end _parse()
|
Chris@0
|
744
|
Chris@0
|
745
|
Chris@0
|
746 /**
|
Chris@0
|
747 * Opens a file and detects the EOL character being used.
|
Chris@0
|
748 *
|
Chris@0
|
749 * @param string $file The full path to the file.
|
Chris@0
|
750 * @param string $contents The contents to parse. If NULL, the content
|
Chris@0
|
751 * is taken from the file system.
|
Chris@0
|
752 *
|
Chris@0
|
753 * @return string
|
Chris@0
|
754 * @throws PHP_CodeSniffer_Exception If $file could not be opened.
|
Chris@0
|
755 */
|
Chris@0
|
756 public static function detectLineEndings($file, $contents=null)
|
Chris@0
|
757 {
|
Chris@0
|
758 if ($contents === null) {
|
Chris@0
|
759 // Determine the newline character being used in this file.
|
Chris@0
|
760 // Will be either \r, \r\n or \n.
|
Chris@0
|
761 if (is_readable($file) === false) {
|
Chris@0
|
762 $error = 'Error opening file; file no longer exists or you do not have access to read the file';
|
Chris@0
|
763 throw new PHP_CodeSniffer_Exception($error);
|
Chris@0
|
764 } else {
|
Chris@0
|
765 $handle = fopen($file, 'r');
|
Chris@0
|
766 if ($handle === false) {
|
Chris@0
|
767 $error = 'Error opening file; could not auto-detect line endings';
|
Chris@0
|
768 throw new PHP_CodeSniffer_Exception($error);
|
Chris@0
|
769 }
|
Chris@0
|
770 }
|
Chris@0
|
771
|
Chris@0
|
772 $firstLine = fgets($handle);
|
Chris@0
|
773 fclose($handle);
|
Chris@0
|
774
|
Chris@0
|
775 $eolChar = substr($firstLine, -1);
|
Chris@0
|
776 if ($eolChar === "\n") {
|
Chris@0
|
777 $secondLastChar = substr($firstLine, -2, 1);
|
Chris@0
|
778 if ($secondLastChar === "\r") {
|
Chris@0
|
779 $eolChar = "\r\n";
|
Chris@0
|
780 }
|
Chris@0
|
781 } else if ($eolChar !== "\r") {
|
Chris@0
|
782 // Must not be an EOL char at the end of the line.
|
Chris@0
|
783 // Probably a one-line file, so assume \n as it really
|
Chris@0
|
784 // doesn't matter considering there are no newlines.
|
Chris@0
|
785 $eolChar = "\n";
|
Chris@0
|
786 }
|
Chris@0
|
787 } else {
|
Chris@0
|
788 if (preg_match("/\r\n?|\n/", $contents, $matches) !== 1) {
|
Chris@0
|
789 // Assuming there are no newlines.
|
Chris@0
|
790 $eolChar = "\n";
|
Chris@0
|
791 } else {
|
Chris@0
|
792 $eolChar = $matches[0];
|
Chris@0
|
793 }
|
Chris@0
|
794 }//end if
|
Chris@0
|
795
|
Chris@0
|
796 return $eolChar;
|
Chris@0
|
797
|
Chris@0
|
798 }//end detectLineEndings()
|
Chris@0
|
799
|
Chris@0
|
800
|
Chris@0
|
801 /**
|
Chris@0
|
802 * Records an error against a specific token in the file.
|
Chris@0
|
803 *
|
Chris@0
|
804 * @param string $error The error message.
|
Chris@0
|
805 * @param int $stackPtr The stack position where the error occurred.
|
Chris@0
|
806 * @param string $code A violation code unique to the sniff message.
|
Chris@0
|
807 * @param array $data Replacements for the error message.
|
Chris@0
|
808 * @param int $severity The severity level for this error. A value of 0
|
Chris@0
|
809 * will be converted into the default severity level.
|
Chris@0
|
810 * @param boolean $fixable Can the error be fixed by the sniff?
|
Chris@0
|
811 *
|
Chris@0
|
812 * @return boolean
|
Chris@0
|
813 */
|
Chris@0
|
814 public function addError(
|
Chris@0
|
815 $error,
|
Chris@0
|
816 $stackPtr,
|
Chris@0
|
817 $code='',
|
Chris@0
|
818 $data=array(),
|
Chris@0
|
819 $severity=0,
|
Chris@0
|
820 $fixable=false
|
Chris@0
|
821 ) {
|
Chris@0
|
822 if ($stackPtr === null) {
|
Chris@0
|
823 $line = 1;
|
Chris@0
|
824 $column = 1;
|
Chris@0
|
825 } else {
|
Chris@0
|
826 $line = $this->_tokens[$stackPtr]['line'];
|
Chris@0
|
827 $column = $this->_tokens[$stackPtr]['column'];
|
Chris@0
|
828 }
|
Chris@0
|
829
|
Chris@0
|
830 return $this->_addError($error, $line, $column, $code, $data, $severity, $fixable);
|
Chris@0
|
831
|
Chris@0
|
832 }//end addError()
|
Chris@0
|
833
|
Chris@0
|
834
|
Chris@0
|
835 /**
|
Chris@0
|
836 * Records a warning against a specific token in the file.
|
Chris@0
|
837 *
|
Chris@0
|
838 * @param string $warning The error message.
|
Chris@0
|
839 * @param int $stackPtr The stack position where the error occurred.
|
Chris@0
|
840 * @param string $code A violation code unique to the sniff message.
|
Chris@0
|
841 * @param array $data Replacements for the warning message.
|
Chris@0
|
842 * @param int $severity The severity level for this warning. A value of 0
|
Chris@0
|
843 * will be converted into the default severity level.
|
Chris@0
|
844 * @param boolean $fixable Can the warning be fixed by the sniff?
|
Chris@0
|
845 *
|
Chris@0
|
846 * @return boolean
|
Chris@0
|
847 */
|
Chris@0
|
848 public function addWarning(
|
Chris@0
|
849 $warning,
|
Chris@0
|
850 $stackPtr,
|
Chris@0
|
851 $code='',
|
Chris@0
|
852 $data=array(),
|
Chris@0
|
853 $severity=0,
|
Chris@0
|
854 $fixable=false
|
Chris@0
|
855 ) {
|
Chris@0
|
856 if ($stackPtr === null) {
|
Chris@0
|
857 $line = 1;
|
Chris@0
|
858 $column = 1;
|
Chris@0
|
859 } else {
|
Chris@0
|
860 $line = $this->_tokens[$stackPtr]['line'];
|
Chris@0
|
861 $column = $this->_tokens[$stackPtr]['column'];
|
Chris@0
|
862 }
|
Chris@0
|
863
|
Chris@0
|
864 return $this->_addWarning($warning, $line, $column, $code, $data, $severity, $fixable);
|
Chris@0
|
865
|
Chris@0
|
866 }//end addWarning()
|
Chris@0
|
867
|
Chris@0
|
868
|
Chris@0
|
869 /**
|
Chris@0
|
870 * Records an error against a specific line in the file.
|
Chris@0
|
871 *
|
Chris@0
|
872 * @param string $error The error message.
|
Chris@0
|
873 * @param int $line The line on which the error occurred.
|
Chris@0
|
874 * @param string $code A violation code unique to the sniff message.
|
Chris@0
|
875 * @param array $data Replacements for the error message.
|
Chris@0
|
876 * @param int $severity The severity level for this error. A value of 0
|
Chris@0
|
877 * will be converted into the default severity level.
|
Chris@0
|
878 *
|
Chris@0
|
879 * @return boolean
|
Chris@0
|
880 */
|
Chris@0
|
881 public function addErrorOnLine(
|
Chris@0
|
882 $error,
|
Chris@0
|
883 $line,
|
Chris@0
|
884 $code='',
|
Chris@0
|
885 $data=array(),
|
Chris@0
|
886 $severity=0
|
Chris@0
|
887 ) {
|
Chris@0
|
888 return $this->_addError($error, $line, 1, $code, $data, $severity, false);
|
Chris@0
|
889
|
Chris@0
|
890 }//end addErrorOnLine()
|
Chris@0
|
891
|
Chris@0
|
892
|
Chris@0
|
893 /**
|
Chris@0
|
894 * Records a warning against a specific token in the file.
|
Chris@0
|
895 *
|
Chris@0
|
896 * @param string $warning The error message.
|
Chris@0
|
897 * @param int $line The line on which the warning occurred.
|
Chris@0
|
898 * @param string $code A violation code unique to the sniff message.
|
Chris@0
|
899 * @param array $data Replacements for the warning message.
|
Chris@0
|
900 * @param int $severity The severity level for this warning. A value of 0
|
Chris@0
|
901 * will be converted into the default severity level.
|
Chris@0
|
902 *
|
Chris@0
|
903 * @return boolean
|
Chris@0
|
904 */
|
Chris@0
|
905 public function addWarningOnLine(
|
Chris@0
|
906 $warning,
|
Chris@0
|
907 $line,
|
Chris@0
|
908 $code='',
|
Chris@0
|
909 $data=array(),
|
Chris@0
|
910 $severity=0
|
Chris@0
|
911 ) {
|
Chris@0
|
912 return $this->_addWarning($warning, $line, 1, $code, $data, $severity, false);
|
Chris@0
|
913
|
Chris@0
|
914 }//end addWarningOnLine()
|
Chris@0
|
915
|
Chris@0
|
916
|
Chris@0
|
917 /**
|
Chris@0
|
918 * Records a fixable error against a specific token in the file.
|
Chris@0
|
919 *
|
Chris@0
|
920 * Returns true if the error was recorded and should be fixed.
|
Chris@0
|
921 *
|
Chris@0
|
922 * @param string $error The error message.
|
Chris@0
|
923 * @param int $stackPtr The stack position where the error occurred.
|
Chris@0
|
924 * @param string $code A violation code unique to the sniff message.
|
Chris@0
|
925 * @param array $data Replacements for the error message.
|
Chris@0
|
926 * @param int $severity The severity level for this error. A value of 0
|
Chris@0
|
927 * will be converted into the default severity level.
|
Chris@0
|
928 *
|
Chris@0
|
929 * @return boolean
|
Chris@0
|
930 */
|
Chris@0
|
931 public function addFixableError(
|
Chris@0
|
932 $error,
|
Chris@0
|
933 $stackPtr,
|
Chris@0
|
934 $code='',
|
Chris@0
|
935 $data=array(),
|
Chris@0
|
936 $severity=0
|
Chris@0
|
937 ) {
|
Chris@0
|
938 $recorded = $this->addError($error, $stackPtr, $code, $data, $severity, true);
|
Chris@0
|
939 if ($recorded === true && $this->fixer->enabled === true) {
|
Chris@0
|
940 return true;
|
Chris@0
|
941 }
|
Chris@0
|
942
|
Chris@0
|
943 return false;
|
Chris@0
|
944
|
Chris@0
|
945 }//end addFixableError()
|
Chris@0
|
946
|
Chris@0
|
947
|
Chris@0
|
948 /**
|
Chris@0
|
949 * Records a fixable warning against a specific token in the file.
|
Chris@0
|
950 *
|
Chris@0
|
951 * Returns true if the warning was recorded and should be fixed.
|
Chris@0
|
952 *
|
Chris@0
|
953 * @param string $warning The error message.
|
Chris@0
|
954 * @param int $stackPtr The stack position where the error occurred.
|
Chris@0
|
955 * @param string $code A violation code unique to the sniff message.
|
Chris@0
|
956 * @param array $data Replacements for the warning message.
|
Chris@0
|
957 * @param int $severity The severity level for this warning. A value of 0
|
Chris@0
|
958 * will be converted into the default severity level.
|
Chris@0
|
959 *
|
Chris@0
|
960 * @return boolean
|
Chris@0
|
961 */
|
Chris@0
|
962 public function addFixableWarning(
|
Chris@0
|
963 $warning,
|
Chris@0
|
964 $stackPtr,
|
Chris@0
|
965 $code='',
|
Chris@0
|
966 $data=array(),
|
Chris@0
|
967 $severity=0
|
Chris@0
|
968 ) {
|
Chris@0
|
969 $recorded = $this->addWarning($warning, $stackPtr, $code, $data, $severity, true);
|
Chris@0
|
970 if ($recorded === true && $this->fixer->enabled === true) {
|
Chris@0
|
971 return true;
|
Chris@0
|
972 }
|
Chris@0
|
973
|
Chris@0
|
974 return false;
|
Chris@0
|
975
|
Chris@0
|
976 }//end addFixableWarning()
|
Chris@0
|
977
|
Chris@0
|
978
|
Chris@0
|
979 /**
|
Chris@0
|
980 * Adds an error to the error stack.
|
Chris@0
|
981 *
|
Chris@0
|
982 * @param string $error The error message.
|
Chris@0
|
983 * @param int $line The line on which the error occurred.
|
Chris@0
|
984 * @param int $column The column at which the error occurred.
|
Chris@0
|
985 * @param string $code A violation code unique to the sniff message.
|
Chris@0
|
986 * @param array $data Replacements for the error message.
|
Chris@0
|
987 * @param int $severity The severity level for this error. A value of 0
|
Chris@0
|
988 * will be converted into the default severity level.
|
Chris@0
|
989 * @param boolean $fixable Can the error be fixed by the sniff?
|
Chris@0
|
990 *
|
Chris@0
|
991 * @return boolean
|
Chris@0
|
992 */
|
Chris@0
|
993 private function _addError($error, $line, $column, $code, $data, $severity, $fixable)
|
Chris@0
|
994 {
|
Chris@0
|
995 if (isset(self::$_ignoredLines[$line]) === true) {
|
Chris@0
|
996 return false;
|
Chris@0
|
997 }
|
Chris@0
|
998
|
Chris@0
|
999 // Work out which sniff generated the error.
|
Chris@0
|
1000 if (substr($code, 0, 9) === 'Internal.') {
|
Chris@0
|
1001 // Any internal message.
|
Chris@0
|
1002 $sniffCode = $code;
|
Chris@0
|
1003 } else {
|
Chris@0
|
1004 $parts = explode('_', str_replace('\\', '_', $this->_activeListener));
|
Chris@0
|
1005 if (isset($parts[3]) === true) {
|
Chris@0
|
1006 $sniff = $parts[0].'.'.$parts[2].'.'.$parts[3];
|
Chris@0
|
1007
|
Chris@0
|
1008 // Remove "Sniff" from the end.
|
Chris@0
|
1009 $sniff = substr($sniff, 0, -5);
|
Chris@0
|
1010 } else {
|
Chris@0
|
1011 $sniff = 'unknownSniff';
|
Chris@0
|
1012 }
|
Chris@0
|
1013
|
Chris@0
|
1014 $sniffCode = $sniff;
|
Chris@0
|
1015 if ($code !== '') {
|
Chris@0
|
1016 $sniffCode .= '.'.$code;
|
Chris@0
|
1017 }
|
Chris@0
|
1018 }//end if
|
Chris@0
|
1019
|
Chris@0
|
1020 // If we know this sniff code is being ignored for this file, return early.
|
Chris@0
|
1021 if (isset($this->_ignoredCodes[$sniffCode]) === true) {
|
Chris@0
|
1022 return false;
|
Chris@0
|
1023 }
|
Chris@0
|
1024
|
Chris@0
|
1025 // Make sure this message type has not been set to "warning".
|
Chris@0
|
1026 if (isset($this->ruleset[$sniffCode]['type']) === true
|
Chris@0
|
1027 && $this->ruleset[$sniffCode]['type'] === 'warning'
|
Chris@0
|
1028 ) {
|
Chris@0
|
1029 // Pass this off to the warning handler.
|
Chris@0
|
1030 return $this->_addWarning($error, $line, $column, $code, $data, $severity, $fixable);
|
Chris@0
|
1031 } else if ($this->phpcs->cli->errorSeverity === 0) {
|
Chris@0
|
1032 // Don't bother doing any processing as errors are just going to
|
Chris@0
|
1033 // be hidden in the reports anyway.
|
Chris@0
|
1034 return false;
|
Chris@0
|
1035 }
|
Chris@0
|
1036
|
Chris@0
|
1037 // Make sure we are interested in this severity level.
|
Chris@0
|
1038 if (isset($this->ruleset[$sniffCode]['severity']) === true) {
|
Chris@0
|
1039 $severity = $this->ruleset[$sniffCode]['severity'];
|
Chris@0
|
1040 } else if ($severity === 0) {
|
Chris@0
|
1041 $severity = PHPCS_DEFAULT_ERROR_SEV;
|
Chris@0
|
1042 }
|
Chris@0
|
1043
|
Chris@0
|
1044 if ($this->phpcs->cli->errorSeverity > $severity) {
|
Chris@0
|
1045 return false;
|
Chris@0
|
1046 }
|
Chris@0
|
1047
|
Chris@0
|
1048 // Make sure we are not ignoring this file.
|
Chris@0
|
1049 $patterns = $this->phpcs->getIgnorePatterns($sniffCode);
|
Chris@0
|
1050 foreach ($patterns as $pattern => $type) {
|
Chris@0
|
1051 // While there is support for a type of each pattern
|
Chris@0
|
1052 // (absolute or relative) we don't actually support it here.
|
Chris@0
|
1053 $replacements = array(
|
Chris@0
|
1054 '\\,' => ',',
|
Chris@0
|
1055 '*' => '.*',
|
Chris@0
|
1056 );
|
Chris@0
|
1057
|
Chris@0
|
1058 // We assume a / directory separator, as do the exclude rules
|
Chris@0
|
1059 // most developers write, so we need a special case for any system
|
Chris@0
|
1060 // that is different.
|
Chris@0
|
1061 if (DIRECTORY_SEPARATOR === '\\') {
|
Chris@0
|
1062 $replacements['/'] = '\\\\';
|
Chris@0
|
1063 }
|
Chris@0
|
1064
|
Chris@0
|
1065 $pattern = '`'.strtr($pattern, $replacements).'`i';
|
Chris@0
|
1066 if (preg_match($pattern, $this->_file) === 1) {
|
Chris@0
|
1067 $this->_ignoredCodes[$sniffCode] = true;
|
Chris@0
|
1068 return false;
|
Chris@0
|
1069 }
|
Chris@0
|
1070 }//end foreach
|
Chris@0
|
1071
|
Chris@0
|
1072 $this->_errorCount++;
|
Chris@0
|
1073 if ($fixable === true) {
|
Chris@0
|
1074 $this->_fixableCount++;
|
Chris@0
|
1075 }
|
Chris@0
|
1076
|
Chris@0
|
1077 if ($this->_recordErrors === false) {
|
Chris@0
|
1078 if (isset($this->_errors[$line]) === false) {
|
Chris@0
|
1079 $this->_errors[$line] = 0;
|
Chris@0
|
1080 }
|
Chris@0
|
1081
|
Chris@0
|
1082 $this->_errors[$line]++;
|
Chris@0
|
1083 return true;
|
Chris@0
|
1084 }
|
Chris@0
|
1085
|
Chris@0
|
1086 // Work out the error message.
|
Chris@0
|
1087 if (isset($this->ruleset[$sniffCode]['message']) === true) {
|
Chris@0
|
1088 $error = $this->ruleset[$sniffCode]['message'];
|
Chris@0
|
1089 }
|
Chris@0
|
1090
|
Chris@0
|
1091 if (empty($data) === true) {
|
Chris@0
|
1092 $message = $error;
|
Chris@0
|
1093 } else {
|
Chris@0
|
1094 $message = vsprintf($error, $data);
|
Chris@0
|
1095 }
|
Chris@0
|
1096
|
Chris@0
|
1097 if (isset($this->_errors[$line]) === false) {
|
Chris@0
|
1098 $this->_errors[$line] = array();
|
Chris@0
|
1099 }
|
Chris@0
|
1100
|
Chris@0
|
1101 if (isset($this->_errors[$line][$column]) === false) {
|
Chris@0
|
1102 $this->_errors[$line][$column] = array();
|
Chris@0
|
1103 }
|
Chris@0
|
1104
|
Chris@0
|
1105 $this->_errors[$line][$column][] = array(
|
Chris@0
|
1106 'message' => $message,
|
Chris@0
|
1107 'source' => $sniffCode,
|
Chris@0
|
1108 'severity' => $severity,
|
Chris@0
|
1109 'fixable' => $fixable,
|
Chris@0
|
1110 );
|
Chris@0
|
1111
|
Chris@0
|
1112 if (PHP_CODESNIFFER_VERBOSITY > 1
|
Chris@0
|
1113 && $this->fixer->enabled === true
|
Chris@0
|
1114 && $fixable === true
|
Chris@0
|
1115 ) {
|
Chris@0
|
1116 @ob_end_clean();
|
Chris@0
|
1117 echo "\tE: [Line $line] $message ($sniffCode)".PHP_EOL;
|
Chris@0
|
1118 ob_start();
|
Chris@0
|
1119 }
|
Chris@0
|
1120
|
Chris@0
|
1121 return true;
|
Chris@0
|
1122
|
Chris@0
|
1123 }//end _addError()
|
Chris@0
|
1124
|
Chris@0
|
1125
|
Chris@0
|
1126 /**
|
Chris@0
|
1127 * Adds an warning to the warning stack.
|
Chris@0
|
1128 *
|
Chris@0
|
1129 * @param string $warning The error message.
|
Chris@0
|
1130 * @param int $line The line on which the warning occurred.
|
Chris@0
|
1131 * @param int $column The column at which the warning occurred.
|
Chris@0
|
1132 * @param string $code A violation code unique to the sniff message.
|
Chris@0
|
1133 * @param array $data Replacements for the warning message.
|
Chris@0
|
1134 * @param int $severity The severity level for this warning. A value of 0
|
Chris@0
|
1135 * will be converted into the default severity level.
|
Chris@0
|
1136 * @param boolean $fixable Can the warning be fixed by the sniff?
|
Chris@0
|
1137 *
|
Chris@0
|
1138 * @return boolean
|
Chris@0
|
1139 */
|
Chris@0
|
1140 private function _addWarning($warning, $line, $column, $code, $data, $severity, $fixable)
|
Chris@0
|
1141 {
|
Chris@0
|
1142 if (isset(self::$_ignoredLines[$line]) === true) {
|
Chris@0
|
1143 return false;
|
Chris@0
|
1144 }
|
Chris@0
|
1145
|
Chris@0
|
1146 // Work out which sniff generated the warning.
|
Chris@0
|
1147 if (substr($code, 0, 9) === 'Internal.') {
|
Chris@0
|
1148 // Any internal message.
|
Chris@0
|
1149 $sniffCode = $code;
|
Chris@0
|
1150 } else {
|
Chris@0
|
1151 $parts = explode('_', str_replace('\\', '_', $this->_activeListener));
|
Chris@0
|
1152 if (isset($parts[3]) === true) {
|
Chris@0
|
1153 $sniff = $parts[0].'.'.$parts[2].'.'.$parts[3];
|
Chris@0
|
1154
|
Chris@0
|
1155 // Remove "Sniff" from the end.
|
Chris@0
|
1156 $sniff = substr($sniff, 0, -5);
|
Chris@0
|
1157 } else {
|
Chris@0
|
1158 $sniff = 'unknownSniff';
|
Chris@0
|
1159 }
|
Chris@0
|
1160
|
Chris@0
|
1161 $sniffCode = $sniff;
|
Chris@0
|
1162 if ($code !== '') {
|
Chris@0
|
1163 $sniffCode .= '.'.$code;
|
Chris@0
|
1164 }
|
Chris@0
|
1165 }//end if
|
Chris@0
|
1166
|
Chris@0
|
1167 // If we know this sniff code is being ignored for this file, return early.
|
Chris@0
|
1168 if (isset($this->_ignoredCodes[$sniffCode]) === true) {
|
Chris@0
|
1169 return false;
|
Chris@0
|
1170 }
|
Chris@0
|
1171
|
Chris@0
|
1172 // Make sure this message type has not been set to "error".
|
Chris@0
|
1173 if (isset($this->ruleset[$sniffCode]['type']) === true
|
Chris@0
|
1174 && $this->ruleset[$sniffCode]['type'] === 'error'
|
Chris@0
|
1175 ) {
|
Chris@0
|
1176 // Pass this off to the error handler.
|
Chris@0
|
1177 return $this->_addError($warning, $line, $column, $code, $data, $severity, $fixable);
|
Chris@0
|
1178 } else if ($this->phpcs->cli->warningSeverity === 0) {
|
Chris@0
|
1179 // Don't bother doing any processing as warnings are just going to
|
Chris@0
|
1180 // be hidden in the reports anyway.
|
Chris@0
|
1181 return false;
|
Chris@0
|
1182 }
|
Chris@0
|
1183
|
Chris@0
|
1184 // Make sure we are interested in this severity level.
|
Chris@0
|
1185 if (isset($this->ruleset[$sniffCode]['severity']) === true) {
|
Chris@0
|
1186 $severity = $this->ruleset[$sniffCode]['severity'];
|
Chris@0
|
1187 } else if ($severity === 0) {
|
Chris@0
|
1188 $severity = PHPCS_DEFAULT_WARN_SEV;
|
Chris@0
|
1189 }
|
Chris@0
|
1190
|
Chris@0
|
1191 if ($this->phpcs->cli->warningSeverity > $severity) {
|
Chris@0
|
1192 return false;
|
Chris@0
|
1193 }
|
Chris@0
|
1194
|
Chris@0
|
1195 // Make sure we are not ignoring this file.
|
Chris@0
|
1196 $patterns = $this->phpcs->getIgnorePatterns($sniffCode);
|
Chris@0
|
1197 foreach ($patterns as $pattern => $type) {
|
Chris@0
|
1198 // While there is support for a type of each pattern
|
Chris@0
|
1199 // (absolute or relative) we don't actually support it here.
|
Chris@0
|
1200 $replacements = array(
|
Chris@0
|
1201 '\\,' => ',',
|
Chris@0
|
1202 '*' => '.*',
|
Chris@0
|
1203 );
|
Chris@0
|
1204
|
Chris@0
|
1205 // We assume a / directory separator, as do the exclude rules
|
Chris@0
|
1206 // most developers write, so we need a special case for any system
|
Chris@0
|
1207 // that is different.
|
Chris@0
|
1208 if (DIRECTORY_SEPARATOR === '\\') {
|
Chris@0
|
1209 $replacements['/'] = '\\\\';
|
Chris@0
|
1210 }
|
Chris@0
|
1211
|
Chris@0
|
1212 $pattern = '`'.strtr($pattern, $replacements).'`i';
|
Chris@0
|
1213 if (preg_match($pattern, $this->_file) === 1) {
|
Chris@0
|
1214 $this->_ignoredCodes[$sniffCode] = true;
|
Chris@0
|
1215 return false;
|
Chris@0
|
1216 }
|
Chris@0
|
1217 }//end foreach
|
Chris@0
|
1218
|
Chris@0
|
1219 $this->_warningCount++;
|
Chris@0
|
1220 if ($fixable === true) {
|
Chris@0
|
1221 $this->_fixableCount++;
|
Chris@0
|
1222 }
|
Chris@0
|
1223
|
Chris@0
|
1224 if ($this->_recordErrors === false) {
|
Chris@0
|
1225 if (isset($this->_warnings[$line]) === false) {
|
Chris@0
|
1226 $this->_warnings[$line] = 0;
|
Chris@0
|
1227 }
|
Chris@0
|
1228
|
Chris@0
|
1229 $this->_warnings[$line]++;
|
Chris@0
|
1230 return true;
|
Chris@0
|
1231 }
|
Chris@0
|
1232
|
Chris@0
|
1233 // Work out the warning message.
|
Chris@0
|
1234 if (isset($this->ruleset[$sniffCode]['message']) === true) {
|
Chris@0
|
1235 $warning = $this->ruleset[$sniffCode]['message'];
|
Chris@0
|
1236 }
|
Chris@0
|
1237
|
Chris@0
|
1238 if (empty($data) === true) {
|
Chris@0
|
1239 $message = $warning;
|
Chris@0
|
1240 } else {
|
Chris@0
|
1241 $message = vsprintf($warning, $data);
|
Chris@0
|
1242 }
|
Chris@0
|
1243
|
Chris@0
|
1244 if (isset($this->_warnings[$line]) === false) {
|
Chris@0
|
1245 $this->_warnings[$line] = array();
|
Chris@0
|
1246 }
|
Chris@0
|
1247
|
Chris@0
|
1248 if (isset($this->_warnings[$line][$column]) === false) {
|
Chris@0
|
1249 $this->_warnings[$line][$column] = array();
|
Chris@0
|
1250 }
|
Chris@0
|
1251
|
Chris@0
|
1252 $this->_warnings[$line][$column][] = array(
|
Chris@0
|
1253 'message' => $message,
|
Chris@0
|
1254 'source' => $sniffCode,
|
Chris@0
|
1255 'severity' => $severity,
|
Chris@0
|
1256 'fixable' => $fixable,
|
Chris@0
|
1257 );
|
Chris@0
|
1258
|
Chris@0
|
1259 if (PHP_CODESNIFFER_VERBOSITY > 1
|
Chris@0
|
1260 && $this->fixer->enabled === true
|
Chris@0
|
1261 && $fixable === true
|
Chris@0
|
1262 ) {
|
Chris@0
|
1263 @ob_end_clean();
|
Chris@0
|
1264 echo "\tW: $message ($sniffCode)".PHP_EOL;
|
Chris@0
|
1265 ob_start();
|
Chris@0
|
1266 }
|
Chris@0
|
1267
|
Chris@0
|
1268 return true;
|
Chris@0
|
1269
|
Chris@0
|
1270 }//end _addWarning()
|
Chris@0
|
1271
|
Chris@0
|
1272
|
Chris@0
|
1273 /**
|
Chris@0
|
1274 * Adds an warning to the warning stack.
|
Chris@0
|
1275 *
|
Chris@0
|
1276 * @param int $stackPtr The stack position where the metric was recorded.
|
Chris@0
|
1277 * @param string $metric The name of the metric being recorded.
|
Chris@0
|
1278 * @param string $value The value of the metric being recorded.
|
Chris@0
|
1279 *
|
Chris@0
|
1280 * @return boolean
|
Chris@0
|
1281 */
|
Chris@0
|
1282 public function recordMetric($stackPtr, $metric, $value)
|
Chris@0
|
1283 {
|
Chris@0
|
1284 if (isset($this->_metrics[$metric]) === false) {
|
Chris@0
|
1285 $this->_metrics[$metric] = array(
|
Chris@0
|
1286 'values' => array(
|
Chris@0
|
1287 $value => array($stackPtr),
|
Chris@0
|
1288 ),
|
Chris@0
|
1289 );
|
Chris@0
|
1290 } else {
|
Chris@0
|
1291 if (isset($this->_metrics[$metric]['values'][$value]) === false) {
|
Chris@0
|
1292 $this->_metrics[$metric]['values'][$value] = array($stackPtr);
|
Chris@0
|
1293 } else {
|
Chris@0
|
1294 $this->_metrics[$metric]['values'][$value][] = $stackPtr;
|
Chris@0
|
1295 }
|
Chris@0
|
1296 }
|
Chris@0
|
1297
|
Chris@0
|
1298 return true;
|
Chris@0
|
1299
|
Chris@0
|
1300 }//end recordMetric()
|
Chris@0
|
1301
|
Chris@0
|
1302
|
Chris@0
|
1303 /**
|
Chris@0
|
1304 * Returns the number of errors raised.
|
Chris@0
|
1305 *
|
Chris@0
|
1306 * @return int
|
Chris@0
|
1307 */
|
Chris@0
|
1308 public function getErrorCount()
|
Chris@0
|
1309 {
|
Chris@0
|
1310 return $this->_errorCount;
|
Chris@0
|
1311
|
Chris@0
|
1312 }//end getErrorCount()
|
Chris@0
|
1313
|
Chris@0
|
1314
|
Chris@0
|
1315 /**
|
Chris@0
|
1316 * Returns the number of warnings raised.
|
Chris@0
|
1317 *
|
Chris@0
|
1318 * @return int
|
Chris@0
|
1319 */
|
Chris@0
|
1320 public function getWarningCount()
|
Chris@0
|
1321 {
|
Chris@0
|
1322 return $this->_warningCount;
|
Chris@0
|
1323
|
Chris@0
|
1324 }//end getWarningCount()
|
Chris@0
|
1325
|
Chris@0
|
1326
|
Chris@0
|
1327 /**
|
Chris@0
|
1328 * Returns the number of successes recorded.
|
Chris@0
|
1329 *
|
Chris@0
|
1330 * @return int
|
Chris@0
|
1331 */
|
Chris@0
|
1332 public function getSuccessCount()
|
Chris@0
|
1333 {
|
Chris@0
|
1334 return $this->_successCount;
|
Chris@0
|
1335
|
Chris@0
|
1336 }//end getSuccessCount()
|
Chris@0
|
1337
|
Chris@0
|
1338
|
Chris@0
|
1339 /**
|
Chris@0
|
1340 * Returns the number of fixable errors/warnings raised.
|
Chris@0
|
1341 *
|
Chris@0
|
1342 * @return int
|
Chris@0
|
1343 */
|
Chris@0
|
1344 public function getFixableCount()
|
Chris@0
|
1345 {
|
Chris@0
|
1346 return $this->_fixableCount;
|
Chris@0
|
1347
|
Chris@0
|
1348 }//end getFixableCount()
|
Chris@0
|
1349
|
Chris@0
|
1350
|
Chris@0
|
1351 /**
|
Chris@0
|
1352 * Returns the list of ignored lines.
|
Chris@0
|
1353 *
|
Chris@0
|
1354 * @return array
|
Chris@0
|
1355 */
|
Chris@0
|
1356 public function getIgnoredLines()
|
Chris@0
|
1357 {
|
Chris@0
|
1358 return self::$_ignoredLines;
|
Chris@0
|
1359
|
Chris@0
|
1360 }//end getIgnoredLines()
|
Chris@0
|
1361
|
Chris@0
|
1362
|
Chris@0
|
1363 /**
|
Chris@0
|
1364 * Returns the errors raised from processing this file.
|
Chris@0
|
1365 *
|
Chris@0
|
1366 * @return array
|
Chris@0
|
1367 */
|
Chris@0
|
1368 public function getErrors()
|
Chris@0
|
1369 {
|
Chris@0
|
1370 return $this->_errors;
|
Chris@0
|
1371
|
Chris@0
|
1372 }//end getErrors()
|
Chris@0
|
1373
|
Chris@0
|
1374
|
Chris@0
|
1375 /**
|
Chris@0
|
1376 * Returns the warnings raised from processing this file.
|
Chris@0
|
1377 *
|
Chris@0
|
1378 * @return array
|
Chris@0
|
1379 */
|
Chris@0
|
1380 public function getWarnings()
|
Chris@0
|
1381 {
|
Chris@0
|
1382 return $this->_warnings;
|
Chris@0
|
1383
|
Chris@0
|
1384 }//end getWarnings()
|
Chris@0
|
1385
|
Chris@0
|
1386
|
Chris@0
|
1387 /**
|
Chris@0
|
1388 * Returns the metrics found while processing this file.
|
Chris@0
|
1389 *
|
Chris@0
|
1390 * @return array
|
Chris@0
|
1391 */
|
Chris@0
|
1392 public function getMetrics()
|
Chris@0
|
1393 {
|
Chris@0
|
1394 return $this->_metrics;
|
Chris@0
|
1395
|
Chris@0
|
1396 }//end getMetrics()
|
Chris@0
|
1397
|
Chris@0
|
1398
|
Chris@0
|
1399 /**
|
Chris@0
|
1400 * Returns the absolute filename of this file.
|
Chris@0
|
1401 *
|
Chris@0
|
1402 * @return string
|
Chris@0
|
1403 */
|
Chris@0
|
1404 public function getFilename()
|
Chris@0
|
1405 {
|
Chris@0
|
1406 return $this->_file;
|
Chris@0
|
1407
|
Chris@0
|
1408 }//end getFilename()
|
Chris@0
|
1409
|
Chris@0
|
1410
|
Chris@0
|
1411 /**
|
Chris@0
|
1412 * Creates an array of tokens when given some PHP code.
|
Chris@0
|
1413 *
|
Chris@0
|
1414 * Starts by using token_get_all() but does a lot of extra processing
|
Chris@0
|
1415 * to insert information about the context of the token.
|
Chris@0
|
1416 *
|
Chris@0
|
1417 * @param string $string The string to tokenize.
|
Chris@0
|
1418 * @param object $tokenizer A tokenizer class to use to tokenize the string.
|
Chris@0
|
1419 * @param string $eolChar The EOL character to use for splitting strings.
|
Chris@0
|
1420 * @param int $tabWidth The number of spaces each tab respresents.
|
Chris@0
|
1421 * @param string $encoding The charset of the sniffed file.
|
Chris@0
|
1422 *
|
Chris@0
|
1423 * @throws PHP_CodeSniffer_Exception If the file cannot be processed.
|
Chris@0
|
1424 * @return array
|
Chris@0
|
1425 */
|
Chris@0
|
1426 public static function tokenizeString($string, $tokenizer, $eolChar='\n', $tabWidth=null, $encoding=null)
|
Chris@0
|
1427 {
|
Chris@0
|
1428 // Minified files often have a very large number of characters per line
|
Chris@0
|
1429 // and cause issues when tokenizing.
|
Chris@0
|
1430 if (property_exists($tokenizer, 'skipMinified') === true
|
Chris@0
|
1431 && $tokenizer->skipMinified === true
|
Chris@0
|
1432 ) {
|
Chris@0
|
1433 $numChars = strlen($string);
|
Chris@0
|
1434 $numLines = (substr_count($string, $eolChar) + 1);
|
Chris@0
|
1435 $average = ($numChars / $numLines);
|
Chris@0
|
1436 if ($average > 100) {
|
Chris@0
|
1437 throw new PHP_CodeSniffer_Exception('File appears to be minified and cannot be processed');
|
Chris@0
|
1438 }
|
Chris@0
|
1439 }
|
Chris@0
|
1440
|
Chris@0
|
1441 $tokens = $tokenizer->tokenizeString($string, $eolChar);
|
Chris@0
|
1442
|
Chris@0
|
1443 if ($tabWidth === null) {
|
Chris@0
|
1444 $tabWidth = PHP_CODESNIFFER_TAB_WIDTH;
|
Chris@0
|
1445 }
|
Chris@0
|
1446
|
Chris@0
|
1447 if ($encoding === null) {
|
Chris@0
|
1448 $encoding = PHP_CODESNIFFER_ENCODING;
|
Chris@0
|
1449 }
|
Chris@0
|
1450
|
Chris@0
|
1451 self::_createPositionMap($tokens, $tokenizer, $eolChar, $encoding, $tabWidth);
|
Chris@0
|
1452 self::_createTokenMap($tokens, $tokenizer, $eolChar);
|
Chris@0
|
1453 self::_createParenthesisNestingMap($tokens, $tokenizer, $eolChar);
|
Chris@0
|
1454 self::_createScopeMap($tokens, $tokenizer, $eolChar);
|
Chris@0
|
1455
|
Chris@0
|
1456 self::_createLevelMap($tokens, $tokenizer, $eolChar);
|
Chris@0
|
1457
|
Chris@0
|
1458 // Allow the tokenizer to do additional processing if required.
|
Chris@0
|
1459 $tokenizer->processAdditional($tokens, $eolChar);
|
Chris@0
|
1460
|
Chris@0
|
1461 return $tokens;
|
Chris@0
|
1462
|
Chris@0
|
1463 }//end tokenizeString()
|
Chris@0
|
1464
|
Chris@0
|
1465
|
Chris@0
|
1466 /**
|
Chris@0
|
1467 * Sets token position information.
|
Chris@0
|
1468 *
|
Chris@0
|
1469 * Can also convert tabs into spaces. Each tab can represent between
|
Chris@0
|
1470 * 1 and $width spaces, so this cannot be a straight string replace.
|
Chris@0
|
1471 *
|
Chris@0
|
1472 * @param array $tokens The array of tokens to process.
|
Chris@0
|
1473 * @param object $tokenizer The tokenizer being used to process this file.
|
Chris@0
|
1474 * @param string $eolChar The EOL character to use for splitting strings.
|
Chris@0
|
1475 * @param string $encoding The charset of the sniffed file.
|
Chris@0
|
1476 * @param int $tabWidth The number of spaces that each tab represents.
|
Chris@0
|
1477 * Set to 0 to disable tab replacement.
|
Chris@0
|
1478 *
|
Chris@0
|
1479 * @return void
|
Chris@0
|
1480 */
|
Chris@0
|
1481 private static function _createPositionMap(&$tokens, $tokenizer, $eolChar, $encoding, $tabWidth)
|
Chris@0
|
1482 {
|
Chris@0
|
1483 $currColumn = 1;
|
Chris@0
|
1484 $lineNumber = 1;
|
Chris@0
|
1485 $eolLen = (strlen($eolChar) * -1);
|
Chris@0
|
1486 $tokenizerType = get_class($tokenizer);
|
Chris@0
|
1487 $ignoring = false;
|
Chris@0
|
1488 $inTests = defined('PHP_CODESNIFFER_IN_TESTS');
|
Chris@0
|
1489
|
Chris@0
|
1490 $checkEncoding = false;
|
Chris@0
|
1491 if ($encoding !== 'iso-8859-1' && function_exists('iconv_strlen') === true) {
|
Chris@0
|
1492 $checkEncoding = true;
|
Chris@0
|
1493 }
|
Chris@0
|
1494
|
Chris@0
|
1495 $tokensWithTabs = array(
|
Chris@0
|
1496 T_WHITESPACE => true,
|
Chris@0
|
1497 T_COMMENT => true,
|
Chris@0
|
1498 T_DOC_COMMENT => true,
|
Chris@0
|
1499 T_DOC_COMMENT_WHITESPACE => true,
|
Chris@0
|
1500 T_DOC_COMMENT_STRING => true,
|
Chris@0
|
1501 T_CONSTANT_ENCAPSED_STRING => true,
|
Chris@0
|
1502 T_DOUBLE_QUOTED_STRING => true,
|
Chris@0
|
1503 T_HEREDOC => true,
|
Chris@0
|
1504 T_NOWDOC => true,
|
Chris@0
|
1505 T_INLINE_HTML => true,
|
Chris@0
|
1506 );
|
Chris@0
|
1507
|
Chris@0
|
1508 $numTokens = count($tokens);
|
Chris@0
|
1509 for ($i = 0; $i < $numTokens; $i++) {
|
Chris@0
|
1510 $tokens[$i]['line'] = $lineNumber;
|
Chris@0
|
1511 $tokens[$i]['column'] = $currColumn;
|
Chris@0
|
1512
|
Chris@0
|
1513 if ($tokenizerType === 'PHP_CodeSniffer_Tokenizers_PHP'
|
Chris@0
|
1514 && isset(PHP_CodeSniffer_Tokens::$knownLengths[$tokens[$i]['code']]) === true
|
Chris@0
|
1515 ) {
|
Chris@0
|
1516 // There are no tabs in the tokens we know the length of.
|
Chris@0
|
1517 $length = PHP_CodeSniffer_Tokens::$knownLengths[$tokens[$i]['code']];
|
Chris@0
|
1518 $currColumn += $length;
|
Chris@0
|
1519 } else if ($tabWidth === 0
|
Chris@0
|
1520 || isset($tokensWithTabs[$tokens[$i]['code']]) === false
|
Chris@0
|
1521 || strpos($tokens[$i]['content'], "\t") === false
|
Chris@0
|
1522 ) {
|
Chris@0
|
1523 // There are no tabs in this content, or we aren't replacing them.
|
Chris@0
|
1524 if ($checkEncoding === true) {
|
Chris@0
|
1525 // Not using the default encoding, so take a bit more care.
|
Chris@0
|
1526 $length = @iconv_strlen($tokens[$i]['content'], $encoding);
|
Chris@0
|
1527 if ($length === false) {
|
Chris@0
|
1528 // String contained invalid characters, so revert to default.
|
Chris@0
|
1529 $length = strlen($tokens[$i]['content']);
|
Chris@0
|
1530 }
|
Chris@0
|
1531 } else {
|
Chris@0
|
1532 $length = strlen($tokens[$i]['content']);
|
Chris@0
|
1533 }
|
Chris@0
|
1534
|
Chris@0
|
1535 $currColumn += $length;
|
Chris@0
|
1536 } else {
|
Chris@0
|
1537 if (str_replace("\t", '', $tokens[$i]['content']) === '') {
|
Chris@0
|
1538 // String only contains tabs, so we can shortcut the process.
|
Chris@0
|
1539 $numTabs = strlen($tokens[$i]['content']);
|
Chris@0
|
1540
|
Chris@0
|
1541 $newContent = '';
|
Chris@0
|
1542 $firstTabSize = ($tabWidth - ($currColumn % $tabWidth) + 1);
|
Chris@0
|
1543 $length = ($firstTabSize + ($tabWidth * ($numTabs - 1)));
|
Chris@0
|
1544 $currColumn += $length;
|
Chris@0
|
1545 $newContent = str_repeat(' ', $length);
|
Chris@0
|
1546 } else {
|
Chris@0
|
1547 // We need to determine the length of each tab.
|
Chris@0
|
1548 $tabs = explode("\t", $tokens[$i]['content']);
|
Chris@0
|
1549
|
Chris@0
|
1550 $numTabs = (count($tabs) - 1);
|
Chris@0
|
1551 $tabNum = 0;
|
Chris@0
|
1552 $newContent = '';
|
Chris@0
|
1553 $length = 0;
|
Chris@0
|
1554
|
Chris@0
|
1555 foreach ($tabs as $content) {
|
Chris@0
|
1556 if ($content !== '') {
|
Chris@0
|
1557 $newContent .= $content;
|
Chris@0
|
1558 if ($checkEncoding === true) {
|
Chris@0
|
1559 // Not using the default encoding, so take a bit more care.
|
Chris@0
|
1560 $contentLength = @iconv_strlen($content, $encoding);
|
Chris@0
|
1561 if ($contentLength === false) {
|
Chris@0
|
1562 // String contained invalid characters, so revert to default.
|
Chris@0
|
1563 $contentLength = strlen($content);
|
Chris@0
|
1564 }
|
Chris@0
|
1565 } else {
|
Chris@0
|
1566 $contentLength = strlen($content);
|
Chris@0
|
1567 }
|
Chris@0
|
1568
|
Chris@0
|
1569 $currColumn += $contentLength;
|
Chris@0
|
1570 $length += $contentLength;
|
Chris@0
|
1571 }
|
Chris@0
|
1572
|
Chris@0
|
1573 // The last piece of content does not have a tab after it.
|
Chris@0
|
1574 if ($tabNum === $numTabs) {
|
Chris@0
|
1575 break;
|
Chris@0
|
1576 }
|
Chris@0
|
1577
|
Chris@0
|
1578 // Process the tab that comes after the content.
|
Chris@0
|
1579 $lastCurrColumn = $currColumn;
|
Chris@0
|
1580 $tabNum++;
|
Chris@0
|
1581
|
Chris@0
|
1582 // Move the pointer to the next tab stop.
|
Chris@0
|
1583 if (($currColumn % $tabWidth) === 0) {
|
Chris@0
|
1584 // This is the first tab, and we are already at a
|
Chris@0
|
1585 // tab stop, so this tab counts as a single space.
|
Chris@0
|
1586 $currColumn++;
|
Chris@0
|
1587 } else {
|
Chris@0
|
1588 $currColumn++;
|
Chris@0
|
1589 while (($currColumn % $tabWidth) !== 0) {
|
Chris@0
|
1590 $currColumn++;
|
Chris@0
|
1591 }
|
Chris@0
|
1592
|
Chris@0
|
1593 $currColumn++;
|
Chris@0
|
1594 }
|
Chris@0
|
1595
|
Chris@0
|
1596 $length += ($currColumn - $lastCurrColumn);
|
Chris@0
|
1597 $newContent .= str_repeat(' ', ($currColumn - $lastCurrColumn));
|
Chris@0
|
1598 }//end foreach
|
Chris@0
|
1599 }//end if
|
Chris@0
|
1600
|
Chris@0
|
1601 $tokens[$i]['orig_content'] = $tokens[$i]['content'];
|
Chris@0
|
1602 $tokens[$i]['content'] = $newContent;
|
Chris@0
|
1603 }//end if
|
Chris@0
|
1604
|
Chris@0
|
1605 $tokens[$i]['length'] = $length;
|
Chris@0
|
1606
|
Chris@0
|
1607 if (isset(PHP_CodeSniffer_Tokens::$knownLengths[$tokens[$i]['code']]) === false
|
Chris@0
|
1608 && strpos($tokens[$i]['content'], $eolChar) !== false
|
Chris@0
|
1609 ) {
|
Chris@0
|
1610 $lineNumber++;
|
Chris@0
|
1611 $currColumn = 1;
|
Chris@0
|
1612
|
Chris@0
|
1613 // Newline chars are not counted in the token length.
|
Chris@0
|
1614 $tokens[$i]['length'] += $eolLen;
|
Chris@0
|
1615 }
|
Chris@0
|
1616
|
Chris@0
|
1617 if ($tokens[$i]['code'] === T_COMMENT
|
Chris@0
|
1618 || $tokens[$i]['code'] === T_DOC_COMMENT_TAG
|
Chris@0
|
1619 || ($inTests === true && $tokens[$i]['code'] === T_INLINE_HTML)
|
Chris@0
|
1620 ) {
|
Chris@0
|
1621 if (strpos($tokens[$i]['content'], '@codingStandards') !== false) {
|
Chris@0
|
1622 if ($ignoring === false
|
Chris@0
|
1623 && strpos($tokens[$i]['content'], '@codingStandardsIgnoreStart') !== false
|
Chris@0
|
1624 ) {
|
Chris@0
|
1625 $ignoring = true;
|
Chris@0
|
1626 } else if ($ignoring === true
|
Chris@0
|
1627 && strpos($tokens[$i]['content'], '@codingStandardsIgnoreEnd') !== false
|
Chris@0
|
1628 ) {
|
Chris@0
|
1629 $ignoring = false;
|
Chris@0
|
1630 // Ignore this comment too.
|
Chris@0
|
1631 self::$_ignoredLines[$tokens[$i]['line']] = true;
|
Chris@0
|
1632 } else if ($ignoring === false
|
Chris@0
|
1633 && strpos($tokens[$i]['content'], '@codingStandardsIgnoreLine') !== false
|
Chris@0
|
1634 ) {
|
Chris@0
|
1635 self::$_ignoredLines[($tokens[$i]['line'] + 1)] = true;
|
Chris@0
|
1636 // Ignore this comment too.
|
Chris@0
|
1637 self::$_ignoredLines[$tokens[$i]['line']] = true;
|
Chris@0
|
1638 }
|
Chris@0
|
1639 }
|
Chris@0
|
1640 }//end if
|
Chris@0
|
1641
|
Chris@0
|
1642 if ($ignoring === true) {
|
Chris@0
|
1643 self::$_ignoredLines[$tokens[$i]['line']] = true;
|
Chris@0
|
1644 }
|
Chris@0
|
1645 }//end for
|
Chris@0
|
1646
|
Chris@0
|
1647 }//end _createPositionMap()
|
Chris@0
|
1648
|
Chris@0
|
1649
|
Chris@0
|
1650 /**
|
Chris@0
|
1651 * Creates a map of brackets positions.
|
Chris@0
|
1652 *
|
Chris@0
|
1653 * @param array $tokens The array of tokens to process.
|
Chris@0
|
1654 * @param object $tokenizer The tokenizer being used to process this file.
|
Chris@0
|
1655 * @param string $eolChar The EOL character to use for splitting strings.
|
Chris@0
|
1656 *
|
Chris@0
|
1657 * @return void
|
Chris@0
|
1658 */
|
Chris@0
|
1659 private static function _createTokenMap(&$tokens, $tokenizer, $eolChar)
|
Chris@0
|
1660 {
|
Chris@0
|
1661 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1662 echo "\t*** START TOKEN MAP ***".PHP_EOL;
|
Chris@0
|
1663 }
|
Chris@0
|
1664
|
Chris@0
|
1665 $squareOpeners = array();
|
Chris@0
|
1666 $curlyOpeners = array();
|
Chris@0
|
1667 $numTokens = count($tokens);
|
Chris@0
|
1668
|
Chris@0
|
1669 $openers = array();
|
Chris@0
|
1670 $openOwner = null;
|
Chris@0
|
1671
|
Chris@0
|
1672 for ($i = 0; $i < $numTokens; $i++) {
|
Chris@0
|
1673 /*
|
Chris@0
|
1674 Parenthesis mapping.
|
Chris@0
|
1675 */
|
Chris@0
|
1676
|
Chris@0
|
1677 if (isset(PHP_CodeSniffer_Tokens::$parenthesisOpeners[$tokens[$i]['code']]) === true) {
|
Chris@0
|
1678 $tokens[$i]['parenthesis_opener'] = null;
|
Chris@0
|
1679 $tokens[$i]['parenthesis_closer'] = null;
|
Chris@0
|
1680 $tokens[$i]['parenthesis_owner'] = $i;
|
Chris@0
|
1681 $openOwner = $i;
|
Chris@0
|
1682 } else if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
|
Chris@0
|
1683 $openers[] = $i;
|
Chris@0
|
1684 $tokens[$i]['parenthesis_opener'] = $i;
|
Chris@0
|
1685 if ($openOwner !== null) {
|
Chris@0
|
1686 $tokens[$openOwner]['parenthesis_opener'] = $i;
|
Chris@0
|
1687 $tokens[$i]['parenthesis_owner'] = $openOwner;
|
Chris@0
|
1688 $openOwner = null;
|
Chris@0
|
1689 }
|
Chris@0
|
1690 } else if ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
|
Chris@0
|
1691 // Did we set an owner for this set of parenthesis?
|
Chris@0
|
1692 $numOpeners = count($openers);
|
Chris@0
|
1693 if ($numOpeners !== 0) {
|
Chris@0
|
1694 $opener = array_pop($openers);
|
Chris@0
|
1695 if (isset($tokens[$opener]['parenthesis_owner']) === true) {
|
Chris@0
|
1696 $owner = $tokens[$opener]['parenthesis_owner'];
|
Chris@0
|
1697
|
Chris@0
|
1698 $tokens[$owner]['parenthesis_closer'] = $i;
|
Chris@0
|
1699 $tokens[$i]['parenthesis_owner'] = $owner;
|
Chris@0
|
1700 }
|
Chris@0
|
1701
|
Chris@0
|
1702 $tokens[$i]['parenthesis_opener'] = $opener;
|
Chris@0
|
1703 $tokens[$i]['parenthesis_closer'] = $i;
|
Chris@0
|
1704 $tokens[$opener]['parenthesis_closer'] = $i;
|
Chris@0
|
1705 }
|
Chris@0
|
1706 }//end if
|
Chris@0
|
1707
|
Chris@0
|
1708 /*
|
Chris@0
|
1709 Bracket mapping.
|
Chris@0
|
1710 */
|
Chris@0
|
1711
|
Chris@0
|
1712 switch ($tokens[$i]['code']) {
|
Chris@0
|
1713 case T_OPEN_SQUARE_BRACKET:
|
Chris@0
|
1714 $squareOpeners[] = $i;
|
Chris@0
|
1715
|
Chris@0
|
1716 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1717 echo str_repeat("\t", count($squareOpeners));
|
Chris@0
|
1718 echo str_repeat("\t", count($curlyOpeners));
|
Chris@0
|
1719 echo "=> Found square bracket opener at $i".PHP_EOL;
|
Chris@0
|
1720 }
|
Chris@0
|
1721 break;
|
Chris@0
|
1722 case T_OPEN_CURLY_BRACKET:
|
Chris@0
|
1723 if (isset($tokens[$i]['scope_closer']) === false) {
|
Chris@0
|
1724 $curlyOpeners[] = $i;
|
Chris@0
|
1725
|
Chris@0
|
1726 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1727 echo str_repeat("\t", count($squareOpeners));
|
Chris@0
|
1728 echo str_repeat("\t", count($curlyOpeners));
|
Chris@0
|
1729 echo "=> Found curly bracket opener at $i".PHP_EOL;
|
Chris@0
|
1730 }
|
Chris@0
|
1731 }
|
Chris@0
|
1732 break;
|
Chris@0
|
1733 case T_CLOSE_SQUARE_BRACKET:
|
Chris@0
|
1734 if (empty($squareOpeners) === false) {
|
Chris@0
|
1735 $opener = array_pop($squareOpeners);
|
Chris@0
|
1736 $tokens[$i]['bracket_opener'] = $opener;
|
Chris@0
|
1737 $tokens[$i]['bracket_closer'] = $i;
|
Chris@0
|
1738 $tokens[$opener]['bracket_opener'] = $opener;
|
Chris@0
|
1739 $tokens[$opener]['bracket_closer'] = $i;
|
Chris@0
|
1740
|
Chris@0
|
1741 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1742 echo str_repeat("\t", count($squareOpeners));
|
Chris@0
|
1743 echo str_repeat("\t", count($curlyOpeners));
|
Chris@0
|
1744 echo "\t=> Found square bracket closer at $i for $opener".PHP_EOL;
|
Chris@0
|
1745 }
|
Chris@0
|
1746 }
|
Chris@0
|
1747 break;
|
Chris@0
|
1748 case T_CLOSE_CURLY_BRACKET:
|
Chris@0
|
1749 if (empty($curlyOpeners) === false
|
Chris@0
|
1750 && isset($tokens[$i]['scope_opener']) === false
|
Chris@0
|
1751 ) {
|
Chris@0
|
1752 $opener = array_pop($curlyOpeners);
|
Chris@0
|
1753 $tokens[$i]['bracket_opener'] = $opener;
|
Chris@0
|
1754 $tokens[$i]['bracket_closer'] = $i;
|
Chris@0
|
1755 $tokens[$opener]['bracket_opener'] = $opener;
|
Chris@0
|
1756 $tokens[$opener]['bracket_closer'] = $i;
|
Chris@0
|
1757
|
Chris@0
|
1758 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1759 echo str_repeat("\t", count($squareOpeners));
|
Chris@0
|
1760 echo str_repeat("\t", count($curlyOpeners));
|
Chris@0
|
1761 echo "\t=> Found curly bracket closer at $i for $opener".PHP_EOL;
|
Chris@0
|
1762 }
|
Chris@0
|
1763 }
|
Chris@0
|
1764 break;
|
Chris@0
|
1765 default:
|
Chris@0
|
1766 continue;
|
Chris@0
|
1767 }//end switch
|
Chris@0
|
1768 }//end for
|
Chris@0
|
1769
|
Chris@0
|
1770 // Cleanup for any openers that we didn't find closers for.
|
Chris@0
|
1771 // This typically means there was a syntax error breaking things.
|
Chris@0
|
1772 foreach ($openers as $opener) {
|
Chris@0
|
1773 unset($tokens[$opener]['parenthesis_opener']);
|
Chris@0
|
1774 unset($tokens[$opener]['parenthesis_owner']);
|
Chris@0
|
1775 }
|
Chris@0
|
1776
|
Chris@0
|
1777 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1778 echo "\t*** END TOKEN MAP ***".PHP_EOL;
|
Chris@0
|
1779 }
|
Chris@0
|
1780
|
Chris@0
|
1781 }//end _createTokenMap()
|
Chris@0
|
1782
|
Chris@0
|
1783
|
Chris@0
|
1784 /**
|
Chris@0
|
1785 * Creates a map for the parenthesis tokens that surround other tokens.
|
Chris@0
|
1786 *
|
Chris@0
|
1787 * @param array $tokens The array of tokens to process.
|
Chris@0
|
1788 * @param object $tokenizer The tokenizer being used to process this file.
|
Chris@0
|
1789 * @param string $eolChar The EOL character to use for splitting strings.
|
Chris@0
|
1790 *
|
Chris@0
|
1791 * @return void
|
Chris@0
|
1792 */
|
Chris@0
|
1793 private static function _createParenthesisNestingMap(
|
Chris@0
|
1794 &$tokens,
|
Chris@0
|
1795 $tokenizer,
|
Chris@0
|
1796 $eolChar
|
Chris@0
|
1797 ) {
|
Chris@0
|
1798 $numTokens = count($tokens);
|
Chris@0
|
1799 $map = array();
|
Chris@0
|
1800 for ($i = 0; $i < $numTokens; $i++) {
|
Chris@0
|
1801 if (isset($tokens[$i]['parenthesis_opener']) === true
|
Chris@0
|
1802 && $i === $tokens[$i]['parenthesis_opener']
|
Chris@0
|
1803 ) {
|
Chris@0
|
1804 if (empty($map) === false) {
|
Chris@0
|
1805 $tokens[$i]['nested_parenthesis'] = $map;
|
Chris@0
|
1806 }
|
Chris@0
|
1807
|
Chris@0
|
1808 if (isset($tokens[$i]['parenthesis_closer']) === true) {
|
Chris@0
|
1809 $map[$tokens[$i]['parenthesis_opener']]
|
Chris@0
|
1810 = $tokens[$i]['parenthesis_closer'];
|
Chris@0
|
1811 }
|
Chris@0
|
1812 } else if (isset($tokens[$i]['parenthesis_closer']) === true
|
Chris@0
|
1813 && $i === $tokens[$i]['parenthesis_closer']
|
Chris@0
|
1814 ) {
|
Chris@0
|
1815 array_pop($map);
|
Chris@0
|
1816 if (empty($map) === false) {
|
Chris@0
|
1817 $tokens[$i]['nested_parenthesis'] = $map;
|
Chris@0
|
1818 }
|
Chris@0
|
1819 } else {
|
Chris@0
|
1820 if (empty($map) === false) {
|
Chris@0
|
1821 $tokens[$i]['nested_parenthesis'] = $map;
|
Chris@0
|
1822 }
|
Chris@0
|
1823 }//end if
|
Chris@0
|
1824 }//end for
|
Chris@0
|
1825
|
Chris@0
|
1826 }//end _createParenthesisNestingMap()
|
Chris@0
|
1827
|
Chris@0
|
1828
|
Chris@0
|
1829 /**
|
Chris@0
|
1830 * Creates a scope map of tokens that open scopes.
|
Chris@0
|
1831 *
|
Chris@0
|
1832 * @param array $tokens The array of tokens to process.
|
Chris@0
|
1833 * @param object $tokenizer The tokenizer being used to process this file.
|
Chris@0
|
1834 * @param string $eolChar The EOL character to use for splitting strings.
|
Chris@0
|
1835 *
|
Chris@0
|
1836 * @return void
|
Chris@0
|
1837 * @see _recurseScopeMap()
|
Chris@0
|
1838 */
|
Chris@0
|
1839 private static function _createScopeMap(&$tokens, $tokenizer, $eolChar)
|
Chris@0
|
1840 {
|
Chris@0
|
1841 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1842 echo "\t*** START SCOPE MAP ***".PHP_EOL;
|
Chris@0
|
1843 }
|
Chris@0
|
1844
|
Chris@0
|
1845 $numTokens = count($tokens);
|
Chris@0
|
1846 for ($i = 0; $i < $numTokens; $i++) {
|
Chris@0
|
1847 // Check to see if the current token starts a new scope.
|
Chris@0
|
1848 if (isset($tokenizer->scopeOpeners[$tokens[$i]['code']]) === true) {
|
Chris@0
|
1849 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1850 $type = $tokens[$i]['type'];
|
Chris@0
|
1851 $content = PHP_CodeSniffer::prepareForOutput($tokens[$i]['content']);
|
Chris@0
|
1852 echo "\tStart scope map at $i:$type => $content".PHP_EOL;
|
Chris@0
|
1853 }
|
Chris@0
|
1854
|
Chris@0
|
1855 if (isset($tokens[$i]['scope_condition']) === true) {
|
Chris@0
|
1856 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1857 echo "\t* already processed, skipping *".PHP_EOL;
|
Chris@0
|
1858 }
|
Chris@0
|
1859
|
Chris@0
|
1860 continue;
|
Chris@0
|
1861 }
|
Chris@0
|
1862
|
Chris@0
|
1863 $i = self::_recurseScopeMap(
|
Chris@0
|
1864 $tokens,
|
Chris@0
|
1865 $numTokens,
|
Chris@0
|
1866 $tokenizer,
|
Chris@0
|
1867 $eolChar,
|
Chris@0
|
1868 $i
|
Chris@0
|
1869 );
|
Chris@0
|
1870 }//end if
|
Chris@0
|
1871 }//end for
|
Chris@0
|
1872
|
Chris@0
|
1873 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1874 echo "\t*** END SCOPE MAP ***".PHP_EOL;
|
Chris@0
|
1875 }
|
Chris@0
|
1876
|
Chris@0
|
1877 }//end _createScopeMap()
|
Chris@0
|
1878
|
Chris@0
|
1879
|
Chris@0
|
1880 /**
|
Chris@0
|
1881 * Recurses though the scope openers to build a scope map.
|
Chris@0
|
1882 *
|
Chris@0
|
1883 * @param array $tokens The array of tokens to process.
|
Chris@0
|
1884 * @param int $numTokens The size of the tokens array.
|
Chris@0
|
1885 * @param object $tokenizer The tokenizer being used to process this file.
|
Chris@0
|
1886 * @param string $eolChar The EOL character to use for splitting strings.
|
Chris@0
|
1887 * @param int $stackPtr The position in the stack of the token that
|
Chris@0
|
1888 * opened the scope (eg. an IF token or FOR token).
|
Chris@0
|
1889 * @param int $depth How many scope levels down we are.
|
Chris@0
|
1890 * @param int $ignore How many curly braces we are ignoring.
|
Chris@0
|
1891 *
|
Chris@0
|
1892 * @return int The position in the stack that closed the scope.
|
Chris@0
|
1893 */
|
Chris@0
|
1894 private static function _recurseScopeMap(
|
Chris@0
|
1895 &$tokens,
|
Chris@0
|
1896 $numTokens,
|
Chris@0
|
1897 $tokenizer,
|
Chris@0
|
1898 $eolChar,
|
Chris@0
|
1899 $stackPtr,
|
Chris@0
|
1900 $depth=1,
|
Chris@0
|
1901 &$ignore=0
|
Chris@0
|
1902 ) {
|
Chris@0
|
1903 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1904 echo str_repeat("\t", $depth);
|
Chris@0
|
1905 echo "=> Begin scope map recursion at token $stackPtr with depth $depth".PHP_EOL;
|
Chris@0
|
1906 }
|
Chris@0
|
1907
|
Chris@0
|
1908 $opener = null;
|
Chris@0
|
1909 $currType = $tokens[$stackPtr]['code'];
|
Chris@0
|
1910 $startLine = $tokens[$stackPtr]['line'];
|
Chris@0
|
1911
|
Chris@0
|
1912 // We will need this to restore the value if we end up
|
Chris@0
|
1913 // returning a token ID that causes our calling function to go back
|
Chris@0
|
1914 // over already ignored braces.
|
Chris@0
|
1915 $originalIgnore = $ignore;
|
Chris@0
|
1916
|
Chris@0
|
1917 // If the start token for this scope opener is the same as
|
Chris@0
|
1918 // the scope token, we have already found our opener.
|
Chris@0
|
1919 if (isset($tokenizer->scopeOpeners[$currType]['start'][$currType]) === true) {
|
Chris@0
|
1920 $opener = $stackPtr;
|
Chris@0
|
1921 }
|
Chris@0
|
1922
|
Chris@0
|
1923 for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
|
Chris@0
|
1924 $tokenType = $tokens[$i]['code'];
|
Chris@0
|
1925
|
Chris@0
|
1926 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1927 $type = $tokens[$i]['type'];
|
Chris@0
|
1928 $line = $tokens[$i]['line'];
|
Chris@0
|
1929 $content = PHP_CodeSniffer::prepareForOutput($tokens[$i]['content']);
|
Chris@0
|
1930
|
Chris@0
|
1931 echo str_repeat("\t", $depth);
|
Chris@0
|
1932 echo "Process token $i on line $line [";
|
Chris@0
|
1933 if ($opener !== null) {
|
Chris@0
|
1934 echo "opener:$opener;";
|
Chris@0
|
1935 }
|
Chris@0
|
1936
|
Chris@0
|
1937 if ($ignore > 0) {
|
Chris@0
|
1938 echo "ignore=$ignore;";
|
Chris@0
|
1939 }
|
Chris@0
|
1940
|
Chris@0
|
1941 echo "]: $type => $content".PHP_EOL;
|
Chris@0
|
1942 }//end if
|
Chris@0
|
1943
|
Chris@0
|
1944 // Very special case for IF statements in PHP that can be defined without
|
Chris@0
|
1945 // scope tokens. E.g., if (1) 1; 1 ? (1 ? 1 : 1) : 1;
|
Chris@0
|
1946 // If an IF statement below this one has an opener but no
|
Chris@0
|
1947 // keyword, the opener will be incorrectly assigned to this IF statement.
|
Chris@0
|
1948 // The same case also applies to USE statements, which don't have to have
|
Chris@0
|
1949 // openers, so a following USE statement can cause an incorrect brace match.
|
Chris@0
|
1950 if (($currType === T_IF || $currType === T_ELSE || $currType === T_USE)
|
Chris@0
|
1951 && $opener === null
|
Chris@0
|
1952 && $tokens[$i]['code'] === T_SEMICOLON
|
Chris@0
|
1953 ) {
|
Chris@0
|
1954 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1955 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
1956 echo str_repeat("\t", $depth);
|
Chris@0
|
1957 echo "=> Found semicolon before scope opener for $stackPtr:$type, bailing".PHP_EOL;
|
Chris@0
|
1958 }
|
Chris@0
|
1959
|
Chris@0
|
1960 return $i;
|
Chris@0
|
1961 }
|
Chris@0
|
1962
|
Chris@0
|
1963 if ($opener === null
|
Chris@0
|
1964 && $ignore === 0
|
Chris@0
|
1965 && $tokenType === T_CLOSE_CURLY_BRACKET
|
Chris@0
|
1966 && isset($tokenizer->scopeOpeners[$currType]['end'][$tokenType]) === true
|
Chris@0
|
1967 ) {
|
Chris@0
|
1968 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1969 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
1970 echo str_repeat("\t", $depth);
|
Chris@0
|
1971 echo "=> Found curly brace closer before scope opener for $stackPtr:$type, bailing".PHP_EOL;
|
Chris@0
|
1972 }
|
Chris@0
|
1973
|
Chris@0
|
1974 return ($i - 1);
|
Chris@0
|
1975 }
|
Chris@0
|
1976
|
Chris@0
|
1977 if ($opener !== null
|
Chris@0
|
1978 && (isset($tokens[$i]['scope_opener']) === false
|
Chris@0
|
1979 || $tokenizer->scopeOpeners[$tokens[$stackPtr]['code']]['shared'] === true)
|
Chris@0
|
1980 && isset($tokenizer->scopeOpeners[$currType]['end'][$tokenType]) === true
|
Chris@0
|
1981 ) {
|
Chris@0
|
1982 if ($ignore > 0 && $tokenType === T_CLOSE_CURLY_BRACKET) {
|
Chris@0
|
1983 // The last opening bracket must have been for a string
|
Chris@0
|
1984 // offset or alike, so let's ignore it.
|
Chris@0
|
1985 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1986 echo str_repeat("\t", $depth);
|
Chris@0
|
1987 echo '* finished ignoring curly brace *'.PHP_EOL;
|
Chris@0
|
1988 }
|
Chris@0
|
1989
|
Chris@0
|
1990 $ignore--;
|
Chris@0
|
1991 continue;
|
Chris@0
|
1992 } else if ($tokens[$opener]['code'] === T_OPEN_CURLY_BRACKET
|
Chris@0
|
1993 && $tokenType !== T_CLOSE_CURLY_BRACKET
|
Chris@0
|
1994 ) {
|
Chris@0
|
1995 // The opener is a curly bracket so the closer must be a curly bracket as well.
|
Chris@0
|
1996 // We ignore this closer to handle cases such as T_ELSE or T_ELSEIF being considered
|
Chris@0
|
1997 // a closer of T_IF when it should not.
|
Chris@0
|
1998 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
1999 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
2000 echo str_repeat("\t", $depth);
|
Chris@0
|
2001 echo "=> Ignoring non-curly scope closer for $stackPtr:$type".PHP_EOL;
|
Chris@0
|
2002 }
|
Chris@0
|
2003 } else {
|
Chris@0
|
2004 $scopeCloser = $i;
|
Chris@0
|
2005 $todo = array(
|
Chris@0
|
2006 $stackPtr,
|
Chris@0
|
2007 $opener,
|
Chris@0
|
2008 );
|
Chris@0
|
2009
|
Chris@0
|
2010 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2011 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
2012 $closerType = $tokens[$scopeCloser]['type'];
|
Chris@0
|
2013 echo str_repeat("\t", $depth);
|
Chris@0
|
2014 echo "=> Found scope closer ($scopeCloser:$closerType) for $stackPtr:$type".PHP_EOL;
|
Chris@0
|
2015 }
|
Chris@0
|
2016
|
Chris@0
|
2017 $validCloser = true;
|
Chris@0
|
2018 if (($tokens[$stackPtr]['code'] === T_IF || $tokens[$stackPtr]['code'] === T_ELSEIF)
|
Chris@0
|
2019 && ($tokenType === T_ELSE || $tokenType === T_ELSEIF)
|
Chris@0
|
2020 ) {
|
Chris@0
|
2021 // To be a closer, this token must have an opener.
|
Chris@0
|
2022 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2023 echo str_repeat("\t", $depth);
|
Chris@0
|
2024 echo "* closer needs to be tested *".PHP_EOL;
|
Chris@0
|
2025 }
|
Chris@0
|
2026
|
Chris@0
|
2027 $i = self::_recurseScopeMap(
|
Chris@0
|
2028 $tokens,
|
Chris@0
|
2029 $numTokens,
|
Chris@0
|
2030 $tokenizer,
|
Chris@0
|
2031 $eolChar,
|
Chris@0
|
2032 $i,
|
Chris@0
|
2033 ($depth + 1),
|
Chris@0
|
2034 $ignore
|
Chris@0
|
2035 );
|
Chris@0
|
2036
|
Chris@0
|
2037 if (isset($tokens[$scopeCloser]['scope_opener']) === false) {
|
Chris@0
|
2038 $validCloser = false;
|
Chris@0
|
2039 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2040 echo str_repeat("\t", $depth);
|
Chris@0
|
2041 echo "* closer is not valid (no opener found) *".PHP_EOL;
|
Chris@0
|
2042 }
|
Chris@0
|
2043 } else if ($tokens[$tokens[$scopeCloser]['scope_opener']]['code'] !== $tokens[$opener]['code']) {
|
Chris@0
|
2044 $validCloser = false;
|
Chris@0
|
2045 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2046 echo str_repeat("\t", $depth);
|
Chris@0
|
2047 $type = $tokens[$tokens[$scopeCloser]['scope_opener']]['type'];
|
Chris@0
|
2048 $openerType = $tokens[$opener]['type'];
|
Chris@0
|
2049 echo "* closer is not valid (mismatched opener type; $type != $openerType) *".PHP_EOL;
|
Chris@0
|
2050 }
|
Chris@0
|
2051 } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2052 echo str_repeat("\t", $depth);
|
Chris@0
|
2053 echo "* closer was valid *".PHP_EOL;
|
Chris@0
|
2054 }
|
Chris@0
|
2055 } else {
|
Chris@0
|
2056 // The closer was not processed, so we need to
|
Chris@0
|
2057 // complete that token as well.
|
Chris@0
|
2058 $todo[] = $scopeCloser;
|
Chris@0
|
2059 }//end if
|
Chris@0
|
2060
|
Chris@0
|
2061 if ($validCloser === true) {
|
Chris@0
|
2062 foreach ($todo as $token) {
|
Chris@0
|
2063 $tokens[$token]['scope_condition'] = $stackPtr;
|
Chris@0
|
2064 $tokens[$token]['scope_opener'] = $opener;
|
Chris@0
|
2065 $tokens[$token]['scope_closer'] = $scopeCloser;
|
Chris@0
|
2066 }
|
Chris@0
|
2067
|
Chris@0
|
2068 if ($tokenizer->scopeOpeners[$tokens[$stackPtr]['code']]['shared'] === true) {
|
Chris@0
|
2069 // As we are going back to where we started originally, restore
|
Chris@0
|
2070 // the ignore value back to its original value.
|
Chris@0
|
2071 $ignore = $originalIgnore;
|
Chris@0
|
2072 return $opener;
|
Chris@0
|
2073 } else if ($scopeCloser === $i
|
Chris@0
|
2074 && isset($tokenizer->scopeOpeners[$tokenType]) === true
|
Chris@0
|
2075 ) {
|
Chris@0
|
2076 // Unset scope_condition here or else the token will appear to have
|
Chris@0
|
2077 // already been processed, and it will be skipped. Normally we want that,
|
Chris@0
|
2078 // but in this case, the token is both a closer and an opener, so
|
Chris@0
|
2079 // it needs to act like an opener. This is also why we return the
|
Chris@0
|
2080 // token before this one; so the closer has a chance to be processed
|
Chris@0
|
2081 // a second time, but as an opener.
|
Chris@0
|
2082 unset($tokens[$scopeCloser]['scope_condition']);
|
Chris@0
|
2083 return ($i - 1);
|
Chris@0
|
2084 } else {
|
Chris@0
|
2085 return $i;
|
Chris@0
|
2086 }
|
Chris@0
|
2087 } else {
|
Chris@0
|
2088 continue;
|
Chris@0
|
2089 }//end if
|
Chris@0
|
2090 }//end if
|
Chris@0
|
2091 }//end if
|
Chris@0
|
2092
|
Chris@0
|
2093 // Is this an opening condition ?
|
Chris@0
|
2094 if (isset($tokenizer->scopeOpeners[$tokenType]) === true) {
|
Chris@0
|
2095 if ($opener === null) {
|
Chris@0
|
2096 if ($tokenType === T_USE) {
|
Chris@0
|
2097 // PHP use keywords are special because they can be
|
Chris@0
|
2098 // used as blocks but also inline in function definitions.
|
Chris@0
|
2099 // So if we find them nested inside another opener, just skip them.
|
Chris@0
|
2100 continue;
|
Chris@0
|
2101 }
|
Chris@0
|
2102
|
Chris@0
|
2103 if ($tokenType === T_FUNCTION
|
Chris@0
|
2104 && $tokens[$stackPtr]['code'] !== T_FUNCTION
|
Chris@0
|
2105 ) {
|
Chris@0
|
2106 // Probably a closure, so process it manually.
|
Chris@0
|
2107 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2108 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
2109 echo str_repeat("\t", $depth);
|
Chris@0
|
2110 echo "=> Found function before scope opener for $stackPtr:$type, processing manually".PHP_EOL;
|
Chris@0
|
2111 }
|
Chris@0
|
2112
|
Chris@0
|
2113 if (isset($tokens[$i]['scope_closer']) === true) {
|
Chris@0
|
2114 // We've already processed this closure.
|
Chris@0
|
2115 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2116 echo str_repeat("\t", $depth);
|
Chris@0
|
2117 echo '* already processed, skipping *'.PHP_EOL;
|
Chris@0
|
2118 }
|
Chris@0
|
2119
|
Chris@0
|
2120 $i = $tokens[$i]['scope_closer'];
|
Chris@0
|
2121 continue;
|
Chris@0
|
2122 }
|
Chris@0
|
2123
|
Chris@0
|
2124 $i = self::_recurseScopeMap(
|
Chris@0
|
2125 $tokens,
|
Chris@0
|
2126 $numTokens,
|
Chris@0
|
2127 $tokenizer,
|
Chris@0
|
2128 $eolChar,
|
Chris@0
|
2129 $i,
|
Chris@0
|
2130 ($depth + 1),
|
Chris@0
|
2131 $ignore
|
Chris@0
|
2132 );
|
Chris@0
|
2133
|
Chris@0
|
2134 continue;
|
Chris@0
|
2135 }//end if
|
Chris@0
|
2136
|
Chris@0
|
2137 // Found another opening condition but still haven't
|
Chris@0
|
2138 // found our opener, so we are never going to find one.
|
Chris@0
|
2139 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2140 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
2141 echo str_repeat("\t", $depth);
|
Chris@0
|
2142 echo "=> Found new opening condition before scope opener for $stackPtr:$type, ";
|
Chris@0
|
2143 }
|
Chris@0
|
2144
|
Chris@0
|
2145 if (($tokens[$stackPtr]['code'] === T_IF
|
Chris@0
|
2146 || $tokens[$stackPtr]['code'] === T_ELSEIF
|
Chris@0
|
2147 || $tokens[$stackPtr]['code'] === T_ELSE)
|
Chris@0
|
2148 && ($tokens[$i]['code'] === T_ELSE
|
Chris@0
|
2149 || $tokens[$i]['code'] === T_ELSEIF)
|
Chris@0
|
2150 ) {
|
Chris@0
|
2151 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2152 echo "continuing".PHP_EOL;
|
Chris@0
|
2153 }
|
Chris@0
|
2154
|
Chris@0
|
2155 return ($i - 1);
|
Chris@0
|
2156 } else {
|
Chris@0
|
2157 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2158 echo "backtracking".PHP_EOL;
|
Chris@0
|
2159 }
|
Chris@0
|
2160
|
Chris@0
|
2161 return $stackPtr;
|
Chris@0
|
2162 }
|
Chris@0
|
2163 }//end if
|
Chris@0
|
2164
|
Chris@0
|
2165 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2166 echo str_repeat("\t", $depth);
|
Chris@0
|
2167 echo '* token is an opening condition *'.PHP_EOL;
|
Chris@0
|
2168 }
|
Chris@0
|
2169
|
Chris@0
|
2170 $isShared = ($tokenizer->scopeOpeners[$tokenType]['shared'] === true);
|
Chris@0
|
2171
|
Chris@0
|
2172 if (isset($tokens[$i]['scope_condition']) === true) {
|
Chris@0
|
2173 // We've been here before.
|
Chris@0
|
2174 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2175 echo str_repeat("\t", $depth);
|
Chris@0
|
2176 echo '* already processed, skipping *'.PHP_EOL;
|
Chris@0
|
2177 }
|
Chris@0
|
2178
|
Chris@0
|
2179 if ($isShared === false
|
Chris@0
|
2180 && isset($tokens[$i]['scope_closer']) === true
|
Chris@0
|
2181 ) {
|
Chris@0
|
2182 $i = $tokens[$i]['scope_closer'];
|
Chris@0
|
2183 }
|
Chris@0
|
2184
|
Chris@0
|
2185 continue;
|
Chris@0
|
2186 } else if ($currType === $tokenType
|
Chris@0
|
2187 && $isShared === false
|
Chris@0
|
2188 && $opener === null
|
Chris@0
|
2189 ) {
|
Chris@0
|
2190 // We haven't yet found our opener, but we have found another
|
Chris@0
|
2191 // scope opener which is the same type as us, and we don't
|
Chris@0
|
2192 // share openers, so we will never find one.
|
Chris@0
|
2193 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2194 echo str_repeat("\t", $depth);
|
Chris@0
|
2195 echo '* it was another token\'s opener, bailing *'.PHP_EOL;
|
Chris@0
|
2196 }
|
Chris@0
|
2197
|
Chris@0
|
2198 return $stackPtr;
|
Chris@0
|
2199 } else {
|
Chris@0
|
2200 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2201 echo str_repeat("\t", $depth);
|
Chris@0
|
2202 echo '* searching for opener *'.PHP_EOL;
|
Chris@0
|
2203 }
|
Chris@0
|
2204
|
Chris@0
|
2205 if (isset($tokenizer->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
|
Chris@0
|
2206 $oldIgnore = $ignore;
|
Chris@0
|
2207 $ignore = 0;
|
Chris@0
|
2208 }
|
Chris@0
|
2209
|
Chris@0
|
2210 // PHP has a max nesting level for functions. Stop before we hit that limit
|
Chris@0
|
2211 // because too many loops means we've run into trouble anyway.
|
Chris@0
|
2212 if ($depth > 50) {
|
Chris@0
|
2213 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2214 echo str_repeat("\t", $depth);
|
Chris@0
|
2215 echo '* reached maximum nesting level; aborting *'.PHP_EOL;
|
Chris@0
|
2216 }
|
Chris@0
|
2217
|
Chris@0
|
2218 throw new PHP_CodeSniffer_Exception('Maximum nesting level reached; file could not be processed');
|
Chris@0
|
2219 }
|
Chris@0
|
2220
|
Chris@0
|
2221 $oldDepth = $depth;
|
Chris@0
|
2222 if ($isShared === true
|
Chris@0
|
2223 && isset($tokenizer->scopeOpeners[$tokenType]['with'][$currType]) === true
|
Chris@0
|
2224 ) {
|
Chris@0
|
2225 // Don't allow the depth to increment because this is
|
Chris@0
|
2226 // possibly not a true nesting if we are sharing our closer.
|
Chris@0
|
2227 // This can happen, for example, when a SWITCH has a large
|
Chris@0
|
2228 // number of CASE statements with the same shared BREAK.
|
Chris@0
|
2229 $depth--;
|
Chris@0
|
2230 }
|
Chris@0
|
2231
|
Chris@0
|
2232 $i = self::_recurseScopeMap(
|
Chris@0
|
2233 $tokens,
|
Chris@0
|
2234 $numTokens,
|
Chris@0
|
2235 $tokenizer,
|
Chris@0
|
2236 $eolChar,
|
Chris@0
|
2237 $i,
|
Chris@0
|
2238 ($depth + 1),
|
Chris@0
|
2239 $ignore
|
Chris@0
|
2240 );
|
Chris@0
|
2241
|
Chris@0
|
2242 $depth = $oldDepth;
|
Chris@0
|
2243
|
Chris@0
|
2244 if (isset($tokenizer->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
|
Chris@0
|
2245 $ignore = $oldIgnore;
|
Chris@0
|
2246 }
|
Chris@0
|
2247 }//end if
|
Chris@0
|
2248 }//end if
|
Chris@0
|
2249
|
Chris@0
|
2250 if (isset($tokenizer->scopeOpeners[$currType]['start'][$tokenType]) === true
|
Chris@0
|
2251 && $opener === null
|
Chris@0
|
2252 ) {
|
Chris@0
|
2253 if ($tokenType === T_OPEN_CURLY_BRACKET) {
|
Chris@0
|
2254 if (isset($tokens[$stackPtr]['parenthesis_closer']) === true
|
Chris@0
|
2255 && $i < $tokens[$stackPtr]['parenthesis_closer']
|
Chris@0
|
2256 ) {
|
Chris@0
|
2257 // We found a curly brace inside the condition of the
|
Chris@0
|
2258 // current scope opener, so it must be a string offset.
|
Chris@0
|
2259 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2260 echo str_repeat("\t", $depth);
|
Chris@0
|
2261 echo '* ignoring curly brace *'.PHP_EOL;
|
Chris@0
|
2262 }
|
Chris@0
|
2263
|
Chris@0
|
2264 $ignore++;
|
Chris@0
|
2265 } else {
|
Chris@0
|
2266 // Make sure this is actually an opener and not a
|
Chris@0
|
2267 // string offset (e.g., $var{0}).
|
Chris@0
|
2268 for ($x = ($i - 1); $x > 0; $x--) {
|
Chris@0
|
2269 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === true) {
|
Chris@0
|
2270 continue;
|
Chris@0
|
2271 } else {
|
Chris@0
|
2272 // If the first non-whitespace/comment token is a
|
Chris@0
|
2273 // variable or object operator then this is an opener
|
Chris@0
|
2274 // for a string offset and not a scope.
|
Chris@0
|
2275 if ($tokens[$x]['code'] === T_VARIABLE
|
Chris@0
|
2276 || $tokens[$x]['code'] === T_OBJECT_OPERATOR
|
Chris@0
|
2277 ) {
|
Chris@0
|
2278 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2279 echo str_repeat("\t", $depth);
|
Chris@0
|
2280 echo '* ignoring curly brace *'.PHP_EOL;
|
Chris@0
|
2281 }
|
Chris@0
|
2282
|
Chris@0
|
2283 $ignore++;
|
Chris@0
|
2284 }//end if
|
Chris@0
|
2285
|
Chris@0
|
2286 break;
|
Chris@0
|
2287 }//end if
|
Chris@0
|
2288 }//end for
|
Chris@0
|
2289 }//end if
|
Chris@0
|
2290 }//end if
|
Chris@0
|
2291
|
Chris@0
|
2292 if ($ignore === 0 || $tokenType !== T_OPEN_CURLY_BRACKET) {
|
Chris@0
|
2293 // We found the opening scope token for $currType.
|
Chris@0
|
2294 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2295 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
2296 echo str_repeat("\t", $depth);
|
Chris@0
|
2297 echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
|
Chris@0
|
2298 }
|
Chris@0
|
2299
|
Chris@0
|
2300 $opener = $i;
|
Chris@0
|
2301 }
|
Chris@0
|
2302 } else if ($tokenType === T_OPEN_PARENTHESIS) {
|
Chris@0
|
2303 if (isset($tokens[$i]['parenthesis_owner']) === true) {
|
Chris@0
|
2304 $owner = $tokens[$i]['parenthesis_owner'];
|
Chris@0
|
2305 if (isset(PHP_CodeSniffer_Tokens::$scopeOpeners[$tokens[$owner]['code']]) === true
|
Chris@0
|
2306 && isset($tokens[$i]['parenthesis_closer']) === true
|
Chris@0
|
2307 ) {
|
Chris@0
|
2308 // If we get into here, then we opened a parenthesis for
|
Chris@0
|
2309 // a scope (eg. an if or else if) so we need to update the
|
Chris@0
|
2310 // start of the line so that when we check to see
|
Chris@0
|
2311 // if the closing parenthesis is more than 3 lines away from
|
Chris@0
|
2312 // the statement, we check from the closing parenthesis.
|
Chris@0
|
2313 $startLine = $tokens[$tokens[$i]['parenthesis_closer']]['line'];
|
Chris@0
|
2314 }
|
Chris@0
|
2315 }
|
Chris@0
|
2316 } else if ($tokenType === T_OPEN_CURLY_BRACKET && $opener !== null) {
|
Chris@0
|
2317 // We opened something that we don't have a scope opener for.
|
Chris@0
|
2318 // Examples of this are curly brackets for string offsets etc.
|
Chris@0
|
2319 // We want to ignore this so that we don't have an invalid scope
|
Chris@0
|
2320 // map.
|
Chris@0
|
2321 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2322 echo str_repeat("\t", $depth);
|
Chris@0
|
2323 echo '* ignoring curly brace *'.PHP_EOL;
|
Chris@0
|
2324 }
|
Chris@0
|
2325
|
Chris@0
|
2326 $ignore++;
|
Chris@0
|
2327 } else if ($tokenType === T_CLOSE_CURLY_BRACKET && $ignore > 0) {
|
Chris@0
|
2328 // We found the end token for the opener we were ignoring.
|
Chris@0
|
2329 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2330 echo str_repeat("\t", $depth);
|
Chris@0
|
2331 echo '* finished ignoring curly brace *'.PHP_EOL;
|
Chris@0
|
2332 }
|
Chris@0
|
2333
|
Chris@0
|
2334 $ignore--;
|
Chris@0
|
2335 } else if ($opener === null
|
Chris@0
|
2336 && isset($tokenizer->scopeOpeners[$currType]) === true
|
Chris@0
|
2337 ) {
|
Chris@0
|
2338 // If we still haven't found the opener after 3 lines,
|
Chris@0
|
2339 // we're not going to find it, unless we know it requires
|
Chris@0
|
2340 // an opener (in which case we better keep looking) or the last
|
Chris@0
|
2341 // token was empty (in which case we'll just confirm there is
|
Chris@0
|
2342 // more code in this file and not just a big comment).
|
Chris@0
|
2343 if ($tokens[$i]['line'] >= ($startLine + 3)
|
Chris@0
|
2344 && isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[($i - 1)]['code']]) === false
|
Chris@0
|
2345 ) {
|
Chris@0
|
2346 if ($tokenizer->scopeOpeners[$currType]['strict'] === true) {
|
Chris@0
|
2347 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2348 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
2349 $lines = ($tokens[$i]['line'] - $startLine);
|
Chris@0
|
2350 echo str_repeat("\t", $depth);
|
Chris@0
|
2351 echo "=> Still looking for $stackPtr:$type scope opener after $lines lines".PHP_EOL;
|
Chris@0
|
2352 }
|
Chris@0
|
2353 } else {
|
Chris@0
|
2354 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2355 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
2356 echo str_repeat("\t", $depth);
|
Chris@0
|
2357 echo "=> Couldn't find scope opener for $stackPtr:$type, bailing".PHP_EOL;
|
Chris@0
|
2358 }
|
Chris@0
|
2359
|
Chris@0
|
2360 return $stackPtr;
|
Chris@0
|
2361 }
|
Chris@0
|
2362 }
|
Chris@0
|
2363 } else if ($opener !== null
|
Chris@0
|
2364 && $tokenType !== T_BREAK
|
Chris@0
|
2365 && isset($tokenizer->endScopeTokens[$tokenType]) === true
|
Chris@0
|
2366 ) {
|
Chris@0
|
2367 if (isset($tokens[$i]['scope_condition']) === false) {
|
Chris@0
|
2368 if ($ignore > 0) {
|
Chris@0
|
2369 // We found the end token for the opener we were ignoring.
|
Chris@0
|
2370 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2371 echo str_repeat("\t", $depth);
|
Chris@0
|
2372 echo '* finished ignoring curly brace *'.PHP_EOL;
|
Chris@0
|
2373 }
|
Chris@0
|
2374
|
Chris@0
|
2375 $ignore--;
|
Chris@0
|
2376 } else {
|
Chris@0
|
2377 // We found a token that closes the scope but it doesn't
|
Chris@0
|
2378 // have a condition, so it belongs to another token and
|
Chris@0
|
2379 // our token doesn't have a closer, so pretend this is
|
Chris@0
|
2380 // the closer.
|
Chris@0
|
2381 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2382 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
2383 echo str_repeat("\t", $depth);
|
Chris@0
|
2384 echo "=> Found (unexpected) scope closer for $stackPtr:$type".PHP_EOL;
|
Chris@0
|
2385 }
|
Chris@0
|
2386
|
Chris@0
|
2387 foreach (array($stackPtr, $opener) as $token) {
|
Chris@0
|
2388 $tokens[$token]['scope_condition'] = $stackPtr;
|
Chris@0
|
2389 $tokens[$token]['scope_opener'] = $opener;
|
Chris@0
|
2390 $tokens[$token]['scope_closer'] = $i;
|
Chris@0
|
2391 }
|
Chris@0
|
2392
|
Chris@0
|
2393 return ($i - 1);
|
Chris@0
|
2394 }//end if
|
Chris@0
|
2395 }//end if
|
Chris@0
|
2396 }//end if
|
Chris@0
|
2397 }//end for
|
Chris@0
|
2398
|
Chris@0
|
2399 return $stackPtr;
|
Chris@0
|
2400
|
Chris@0
|
2401 }//end _recurseScopeMap()
|
Chris@0
|
2402
|
Chris@0
|
2403
|
Chris@0
|
2404 /**
|
Chris@0
|
2405 * Constructs the level map.
|
Chris@0
|
2406 *
|
Chris@0
|
2407 * The level map adds a 'level' index to each token which indicates the
|
Chris@0
|
2408 * depth that a token within a set of scope blocks. It also adds a
|
Chris@0
|
2409 * 'condition' index which is an array of the scope conditions that opened
|
Chris@0
|
2410 * each of the scopes - position 0 being the first scope opener.
|
Chris@0
|
2411 *
|
Chris@0
|
2412 * @param array $tokens The array of tokens to process.
|
Chris@0
|
2413 * @param object $tokenizer The tokenizer being used to process this file.
|
Chris@0
|
2414 * @param string $eolChar The EOL character to use for splitting strings.
|
Chris@0
|
2415 *
|
Chris@0
|
2416 * @return void
|
Chris@0
|
2417 */
|
Chris@0
|
2418 private static function _createLevelMap(&$tokens, $tokenizer, $eolChar)
|
Chris@0
|
2419 {
|
Chris@0
|
2420 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2421 echo "\t*** START LEVEL MAP ***".PHP_EOL;
|
Chris@0
|
2422 }
|
Chris@0
|
2423
|
Chris@0
|
2424 $numTokens = count($tokens);
|
Chris@0
|
2425 $level = 0;
|
Chris@0
|
2426 $conditions = array();
|
Chris@0
|
2427 $lastOpener = null;
|
Chris@0
|
2428 $openers = array();
|
Chris@0
|
2429
|
Chris@0
|
2430 for ($i = 0; $i < $numTokens; $i++) {
|
Chris@0
|
2431 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2432 $type = $tokens[$i]['type'];
|
Chris@0
|
2433 $line = $tokens[$i]['line'];
|
Chris@0
|
2434 $len = $tokens[$i]['length'];
|
Chris@0
|
2435 $col = $tokens[$i]['column'];
|
Chris@0
|
2436
|
Chris@0
|
2437 $content = PHP_CodeSniffer::prepareForOutput($tokens[$i]['content']);
|
Chris@0
|
2438
|
Chris@0
|
2439 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2440 echo "Process token $i on line $line [col:$col;len:$len;lvl:$level;";
|
Chris@0
|
2441 if (empty($conditions) !== true) {
|
Chris@0
|
2442 $condString = 'conds;';
|
Chris@0
|
2443 foreach ($conditions as $condition) {
|
Chris@0
|
2444 $condString .= token_name($condition).',';
|
Chris@0
|
2445 }
|
Chris@0
|
2446
|
Chris@0
|
2447 echo rtrim($condString, ',').';';
|
Chris@0
|
2448 }
|
Chris@0
|
2449
|
Chris@0
|
2450 echo "]: $type => $content".PHP_EOL;
|
Chris@0
|
2451 }//end if
|
Chris@0
|
2452
|
Chris@0
|
2453 $tokens[$i]['level'] = $level;
|
Chris@0
|
2454 $tokens[$i]['conditions'] = $conditions;
|
Chris@0
|
2455
|
Chris@0
|
2456 if (isset($tokens[$i]['scope_condition']) === true) {
|
Chris@0
|
2457 // Check to see if this token opened the scope.
|
Chris@0
|
2458 if ($tokens[$i]['scope_opener'] === $i) {
|
Chris@0
|
2459 $stackPtr = $tokens[$i]['scope_condition'];
|
Chris@0
|
2460 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2461 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
2462 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2463 echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
|
Chris@0
|
2464 }
|
Chris@0
|
2465
|
Chris@0
|
2466 $stackPtr = $tokens[$i]['scope_condition'];
|
Chris@0
|
2467
|
Chris@0
|
2468 // If we find a scope opener that has a shared closer,
|
Chris@0
|
2469 // then we need to go back over the condition map that we
|
Chris@0
|
2470 // just created and fix ourselves as we just added some
|
Chris@0
|
2471 // conditions where there was none. This happens for T_CASE
|
Chris@0
|
2472 // statements that are using the same break statement.
|
Chris@0
|
2473 if ($lastOpener !== null && $tokens[$lastOpener]['scope_closer'] === $tokens[$i]['scope_closer']) {
|
Chris@0
|
2474 // This opener shares its closer with the previous opener,
|
Chris@0
|
2475 // but we still need to check if the two openers share their
|
Chris@0
|
2476 // closer with each other directly (like CASE and DEFAULT)
|
Chris@0
|
2477 // or if they are just sharing because one doesn't have a
|
Chris@0
|
2478 // closer (like CASE with no BREAK using a SWITCHes closer).
|
Chris@0
|
2479 $thisType = $tokens[$tokens[$i]['scope_condition']]['code'];
|
Chris@0
|
2480 $opener = $tokens[$lastOpener]['scope_condition'];
|
Chris@0
|
2481
|
Chris@0
|
2482 $isShared = isset($tokenizer->scopeOpeners[$thisType]['with'][$tokens[$opener]['code']]);
|
Chris@0
|
2483
|
Chris@0
|
2484 reset($tokenizer->scopeOpeners[$thisType]['end']);
|
Chris@0
|
2485 reset($tokenizer->scopeOpeners[$tokens[$opener]['code']]['end']);
|
Chris@0
|
2486 $sameEnd = (current($tokenizer->scopeOpeners[$thisType]['end']) === current($tokenizer->scopeOpeners[$tokens[$opener]['code']]['end']));
|
Chris@0
|
2487
|
Chris@0
|
2488 if ($isShared === true && $sameEnd === true) {
|
Chris@0
|
2489 $badToken = $opener;
|
Chris@0
|
2490 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2491 $type = $tokens[$badToken]['type'];
|
Chris@0
|
2492 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2493 echo "* shared closer, cleaning up $badToken:$type *".PHP_EOL;
|
Chris@0
|
2494 }
|
Chris@0
|
2495
|
Chris@0
|
2496 for ($x = $tokens[$i]['scope_condition']; $x <= $i; $x++) {
|
Chris@0
|
2497 $oldConditions = $tokens[$x]['conditions'];
|
Chris@0
|
2498 $oldLevel = $tokens[$x]['level'];
|
Chris@0
|
2499 $tokens[$x]['level']--;
|
Chris@0
|
2500 unset($tokens[$x]['conditions'][$badToken]);
|
Chris@0
|
2501 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2502 $type = $tokens[$x]['type'];
|
Chris@0
|
2503 $oldConds = '';
|
Chris@0
|
2504 foreach ($oldConditions as $condition) {
|
Chris@0
|
2505 $oldConds .= token_name($condition).',';
|
Chris@0
|
2506 }
|
Chris@0
|
2507
|
Chris@0
|
2508 $oldConds = rtrim($oldConds, ',');
|
Chris@0
|
2509
|
Chris@0
|
2510 $newConds = '';
|
Chris@0
|
2511 foreach ($tokens[$x]['conditions'] as $condition) {
|
Chris@0
|
2512 $newConds .= token_name($condition).',';
|
Chris@0
|
2513 }
|
Chris@0
|
2514
|
Chris@0
|
2515 $newConds = rtrim($newConds, ',');
|
Chris@0
|
2516
|
Chris@0
|
2517 $newLevel = $tokens[$x]['level'];
|
Chris@0
|
2518 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2519 echo "* cleaned $x:$type *".PHP_EOL;
|
Chris@0
|
2520 echo str_repeat("\t", ($level + 2));
|
Chris@0
|
2521 echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
|
Chris@0
|
2522 echo str_repeat("\t", ($level + 2));
|
Chris@0
|
2523 echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
|
Chris@0
|
2524 }//end if
|
Chris@0
|
2525 }//end for
|
Chris@0
|
2526
|
Chris@0
|
2527 unset($conditions[$badToken]);
|
Chris@0
|
2528 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2529 $type = $tokens[$badToken]['type'];
|
Chris@0
|
2530 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2531 echo "* token $badToken:$type removed from conditions array *".PHP_EOL;
|
Chris@0
|
2532 }
|
Chris@0
|
2533
|
Chris@0
|
2534 unset($openers[$lastOpener]);
|
Chris@0
|
2535
|
Chris@0
|
2536 $level--;
|
Chris@0
|
2537 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2538 echo str_repeat("\t", ($level + 2));
|
Chris@0
|
2539 echo '* level decreased *'.PHP_EOL;
|
Chris@0
|
2540 }
|
Chris@0
|
2541 }//end if
|
Chris@0
|
2542 }//end if
|
Chris@0
|
2543
|
Chris@0
|
2544 $level++;
|
Chris@0
|
2545 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2546 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2547 echo '* level increased *'.PHP_EOL;
|
Chris@0
|
2548 }
|
Chris@0
|
2549
|
Chris@0
|
2550 $conditions[$stackPtr] = $tokens[$stackPtr]['code'];
|
Chris@0
|
2551 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2552 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
2553 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2554 echo "* token $stackPtr:$type added to conditions array *".PHP_EOL;
|
Chris@0
|
2555 }
|
Chris@0
|
2556
|
Chris@0
|
2557 $lastOpener = $tokens[$i]['scope_opener'];
|
Chris@0
|
2558 if ($lastOpener !== null) {
|
Chris@0
|
2559 $openers[$lastOpener] = $lastOpener;
|
Chris@0
|
2560 }
|
Chris@0
|
2561 } else if ($lastOpener !== null && $tokens[$lastOpener]['scope_closer'] === $i) {
|
Chris@0
|
2562 foreach (array_reverse($openers) as $opener) {
|
Chris@0
|
2563 if ($tokens[$opener]['scope_closer'] === $i) {
|
Chris@0
|
2564 $oldOpener = array_pop($openers);
|
Chris@0
|
2565 if (empty($openers) === false) {
|
Chris@0
|
2566 $lastOpener = array_pop($openers);
|
Chris@0
|
2567 $openers[$lastOpener] = $lastOpener;
|
Chris@0
|
2568 } else {
|
Chris@0
|
2569 $lastOpener = null;
|
Chris@0
|
2570 }
|
Chris@0
|
2571
|
Chris@0
|
2572 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2573 $type = $tokens[$oldOpener]['type'];
|
Chris@0
|
2574 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2575 echo "=> Found scope closer for $oldOpener:$type".PHP_EOL;
|
Chris@0
|
2576 }
|
Chris@0
|
2577
|
Chris@0
|
2578 $oldCondition = array_pop($conditions);
|
Chris@0
|
2579 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2580 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2581 echo '* token '.token_name($oldCondition).' removed from conditions array *'.PHP_EOL;
|
Chris@0
|
2582 }
|
Chris@0
|
2583
|
Chris@0
|
2584 // Make sure this closer actually belongs to us.
|
Chris@0
|
2585 // Either the condition also has to think this is the
|
Chris@0
|
2586 // closer, or it has to allow sharing with us.
|
Chris@0
|
2587 $condition = $tokens[$tokens[$i]['scope_condition']]['code'];
|
Chris@0
|
2588 if ($condition !== $oldCondition) {
|
Chris@0
|
2589 if (isset($tokenizer->scopeOpeners[$oldCondition]['with'][$condition]) === false) {
|
Chris@0
|
2590 $badToken = $tokens[$oldOpener]['scope_condition'];
|
Chris@0
|
2591
|
Chris@0
|
2592 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2593 $type = token_name($oldCondition);
|
Chris@0
|
2594 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2595 echo "* scope closer was bad, cleaning up $badToken:$type *".PHP_EOL;
|
Chris@0
|
2596 }
|
Chris@0
|
2597
|
Chris@0
|
2598 for ($x = ($oldOpener + 1); $x <= $i; $x++) {
|
Chris@0
|
2599 $oldConditions = $tokens[$x]['conditions'];
|
Chris@0
|
2600 $oldLevel = $tokens[$x]['level'];
|
Chris@0
|
2601 $tokens[$x]['level']--;
|
Chris@0
|
2602 unset($tokens[$x]['conditions'][$badToken]);
|
Chris@0
|
2603 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2604 $type = $tokens[$x]['type'];
|
Chris@0
|
2605 $oldConds = '';
|
Chris@0
|
2606 foreach ($oldConditions as $condition) {
|
Chris@0
|
2607 $oldConds .= token_name($condition).',';
|
Chris@0
|
2608 }
|
Chris@0
|
2609
|
Chris@0
|
2610 $oldConds = rtrim($oldConds, ',');
|
Chris@0
|
2611
|
Chris@0
|
2612 $newConds = '';
|
Chris@0
|
2613 foreach ($tokens[$x]['conditions'] as $condition) {
|
Chris@0
|
2614 $newConds .= token_name($condition).',';
|
Chris@0
|
2615 }
|
Chris@0
|
2616
|
Chris@0
|
2617 $newConds = rtrim($newConds, ',');
|
Chris@0
|
2618
|
Chris@0
|
2619 $newLevel = $tokens[$x]['level'];
|
Chris@0
|
2620 echo str_repeat("\t", ($level + 1));
|
Chris@0
|
2621 echo "* cleaned $x:$type *".PHP_EOL;
|
Chris@0
|
2622 echo str_repeat("\t", ($level + 2));
|
Chris@0
|
2623 echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
|
Chris@0
|
2624 echo str_repeat("\t", ($level + 2));
|
Chris@0
|
2625 echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
|
Chris@0
|
2626 }//end if
|
Chris@0
|
2627 }//end for
|
Chris@0
|
2628 }//end if
|
Chris@0
|
2629 }//end if
|
Chris@0
|
2630
|
Chris@0
|
2631 $level--;
|
Chris@0
|
2632 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2633 echo str_repeat("\t", ($level + 2));
|
Chris@0
|
2634 echo '* level decreased *'.PHP_EOL;
|
Chris@0
|
2635 }
|
Chris@0
|
2636
|
Chris@0
|
2637 $tokens[$i]['level'] = $level;
|
Chris@0
|
2638 $tokens[$i]['conditions'] = $conditions;
|
Chris@0
|
2639 }//end if
|
Chris@0
|
2640 }//end foreach
|
Chris@0
|
2641 }//end if
|
Chris@0
|
2642 }//end if
|
Chris@0
|
2643 }//end for
|
Chris@0
|
2644
|
Chris@0
|
2645 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
2646 echo "\t*** END LEVEL MAP ***".PHP_EOL;
|
Chris@0
|
2647 }
|
Chris@0
|
2648
|
Chris@0
|
2649 }//end _createLevelMap()
|
Chris@0
|
2650
|
Chris@0
|
2651
|
Chris@0
|
2652 /**
|
Chris@0
|
2653 * Returns the declaration names for classes, interfaces, and functions.
|
Chris@0
|
2654 *
|
Chris@0
|
2655 * @param int $stackPtr The position of the declaration token which
|
Chris@0
|
2656 * declared the class, interface or function.
|
Chris@0
|
2657 *
|
Chris@0
|
2658 * @return string|null The name of the class, interface or function.
|
Chris@0
|
2659 * or NULL if the function or class is anonymous.
|
Chris@0
|
2660 * @throws PHP_CodeSniffer_Exception If the specified token is not of type
|
Chris@0
|
2661 * T_FUNCTION, T_CLASS, T_ANON_CLASS,
|
Chris@0
|
2662 * or T_INTERFACE.
|
Chris@0
|
2663 */
|
Chris@0
|
2664 public function getDeclarationName($stackPtr)
|
Chris@0
|
2665 {
|
Chris@0
|
2666 $tokenCode = $this->_tokens[$stackPtr]['code'];
|
Chris@0
|
2667
|
Chris@0
|
2668 if ($tokenCode === T_ANON_CLASS) {
|
Chris@0
|
2669 return null;
|
Chris@0
|
2670 }
|
Chris@0
|
2671
|
Chris@0
|
2672 if ($tokenCode === T_FUNCTION
|
Chris@0
|
2673 && $this->isAnonymousFunction($stackPtr) === true
|
Chris@0
|
2674 ) {
|
Chris@0
|
2675 return null;
|
Chris@0
|
2676 }
|
Chris@0
|
2677
|
Chris@0
|
2678 if ($tokenCode !== T_FUNCTION
|
Chris@0
|
2679 && $tokenCode !== T_CLASS
|
Chris@0
|
2680 && $tokenCode !== T_INTERFACE
|
Chris@0
|
2681 && $tokenCode !== T_TRAIT
|
Chris@0
|
2682 ) {
|
Chris@0
|
2683 throw new PHP_CodeSniffer_Exception('Token type "'.$this->_tokens[$stackPtr]['type'].'" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT');
|
Chris@0
|
2684 }
|
Chris@0
|
2685
|
Chris@0
|
2686 $content = null;
|
Chris@0
|
2687 for ($i = $stackPtr; $i < $this->numTokens; $i++) {
|
Chris@0
|
2688 if ($this->_tokens[$i]['code'] === T_STRING) {
|
Chris@0
|
2689 $content = $this->_tokens[$i]['content'];
|
Chris@0
|
2690 break;
|
Chris@0
|
2691 }
|
Chris@0
|
2692 }
|
Chris@0
|
2693
|
Chris@0
|
2694 return $content;
|
Chris@0
|
2695
|
Chris@0
|
2696 }//end getDeclarationName()
|
Chris@0
|
2697
|
Chris@0
|
2698
|
Chris@0
|
2699 /**
|
Chris@0
|
2700 * Check if the token at the specified position is a anonymous function.
|
Chris@0
|
2701 *
|
Chris@0
|
2702 * @param int $stackPtr The position of the declaration token which
|
Chris@0
|
2703 * declared the class, interface or function.
|
Chris@0
|
2704 *
|
Chris@0
|
2705 * @return boolean
|
Chris@0
|
2706 * @throws PHP_CodeSniffer_Exception If the specified token is not of type
|
Chris@0
|
2707 * T_FUNCTION
|
Chris@0
|
2708 */
|
Chris@0
|
2709 public function isAnonymousFunction($stackPtr)
|
Chris@0
|
2710 {
|
Chris@0
|
2711 $tokenCode = $this->_tokens[$stackPtr]['code'];
|
Chris@0
|
2712 if ($tokenCode !== T_FUNCTION) {
|
Chris@0
|
2713 throw new PHP_CodeSniffer_Exception('Token type is not T_FUNCTION');
|
Chris@0
|
2714 }
|
Chris@0
|
2715
|
Chris@0
|
2716 if (isset($this->_tokens[$stackPtr]['parenthesis_opener']) === false) {
|
Chris@0
|
2717 // Something is not right with this function.
|
Chris@0
|
2718 return false;
|
Chris@0
|
2719 }
|
Chris@0
|
2720
|
Chris@0
|
2721 $name = false;
|
Chris@0
|
2722 for ($i = ($stackPtr + 1); $i < $this->numTokens; $i++) {
|
Chris@0
|
2723 if ($this->_tokens[$i]['code'] === T_STRING) {
|
Chris@0
|
2724 $name = $i;
|
Chris@0
|
2725 break;
|
Chris@0
|
2726 }
|
Chris@0
|
2727 }
|
Chris@0
|
2728
|
Chris@0
|
2729 if ($name === false) {
|
Chris@0
|
2730 // No name found.
|
Chris@0
|
2731 return true;
|
Chris@0
|
2732 }
|
Chris@0
|
2733
|
Chris@0
|
2734 $open = $this->_tokens[$stackPtr]['parenthesis_opener'];
|
Chris@0
|
2735 if ($name > $open) {
|
Chris@0
|
2736 return true;
|
Chris@0
|
2737 }
|
Chris@0
|
2738
|
Chris@0
|
2739 return false;
|
Chris@0
|
2740
|
Chris@0
|
2741 }//end isAnonymousFunction()
|
Chris@0
|
2742
|
Chris@0
|
2743
|
Chris@0
|
2744 /**
|
Chris@0
|
2745 * Returns the method parameters for the specified function token.
|
Chris@0
|
2746 *
|
Chris@0
|
2747 * Each parameter is in the following format:
|
Chris@0
|
2748 *
|
Chris@0
|
2749 * <code>
|
Chris@0
|
2750 * 0 => array(
|
Chris@0
|
2751 * 'token' => int, // The position of the var in the token stack.
|
Chris@0
|
2752 * 'name' => '$var', // The variable name.
|
Chris@0
|
2753 * 'content' => string, // The full content of the variable definition.
|
Chris@0
|
2754 * 'pass_by_reference' => boolean, // Is the variable passed by reference?
|
Chris@0
|
2755 * 'type_hint' => string, // The type hint for the variable.
|
Chris@0
|
2756 * 'nullable_type' => boolean, // Is the variable using a nullable type?
|
Chris@0
|
2757 * )
|
Chris@0
|
2758 * </code>
|
Chris@0
|
2759 *
|
Chris@0
|
2760 * Parameters with default values have an additional array index of
|
Chris@0
|
2761 * 'default' with the value of the default as a string.
|
Chris@0
|
2762 *
|
Chris@0
|
2763 * @param int $stackPtr The position in the stack of the function token
|
Chris@0
|
2764 * to acquire the parameters for.
|
Chris@0
|
2765 *
|
Chris@0
|
2766 * @return array
|
Chris@0
|
2767 * @throws PHP_CodeSniffer_Exception If the specified $stackPtr is not of
|
Chris@0
|
2768 * type T_FUNCTION or T_CLOSURE.
|
Chris@0
|
2769 */
|
Chris@0
|
2770 public function getMethodParameters($stackPtr)
|
Chris@0
|
2771 {
|
Chris@0
|
2772 if ($this->_tokens[$stackPtr]['code'] !== T_FUNCTION
|
Chris@0
|
2773 && $this->_tokens[$stackPtr]['code'] !== T_CLOSURE
|
Chris@0
|
2774 ) {
|
Chris@0
|
2775 throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
|
Chris@0
|
2776 }
|
Chris@0
|
2777
|
Chris@0
|
2778 $opener = $this->_tokens[$stackPtr]['parenthesis_opener'];
|
Chris@0
|
2779 $closer = $this->_tokens[$stackPtr]['parenthesis_closer'];
|
Chris@0
|
2780
|
Chris@0
|
2781 $vars = array();
|
Chris@0
|
2782 $currVar = null;
|
Chris@0
|
2783 $paramStart = ($opener + 1);
|
Chris@0
|
2784 $defaultStart = null;
|
Chris@0
|
2785 $paramCount = 0;
|
Chris@0
|
2786 $passByReference = false;
|
Chris@0
|
2787 $variableLength = false;
|
Chris@0
|
2788 $typeHint = '';
|
Chris@0
|
2789 $nullableType = false;
|
Chris@0
|
2790
|
Chris@0
|
2791 for ($i = $paramStart; $i <= $closer; $i++) {
|
Chris@0
|
2792 // Check to see if this token has a parenthesis or bracket opener. If it does
|
Chris@0
|
2793 // it's likely to be an array which might have arguments in it. This
|
Chris@0
|
2794 // could cause problems in our parsing below, so lets just skip to the
|
Chris@0
|
2795 // end of it.
|
Chris@0
|
2796 if (isset($this->_tokens[$i]['parenthesis_opener']) === true) {
|
Chris@0
|
2797 // Don't do this if it's the close parenthesis for the method.
|
Chris@0
|
2798 if ($i !== $this->_tokens[$i]['parenthesis_closer']) {
|
Chris@0
|
2799 $i = ($this->_tokens[$i]['parenthesis_closer'] + 1);
|
Chris@0
|
2800 }
|
Chris@0
|
2801 }
|
Chris@0
|
2802
|
Chris@0
|
2803 if (isset($this->_tokens[$i]['bracket_opener']) === true) {
|
Chris@0
|
2804 // Don't do this if it's the close parenthesis for the method.
|
Chris@0
|
2805 if ($i !== $this->_tokens[$i]['bracket_closer']) {
|
Chris@0
|
2806 $i = ($this->_tokens[$i]['bracket_closer'] + 1);
|
Chris@0
|
2807 }
|
Chris@0
|
2808 }
|
Chris@0
|
2809
|
Chris@0
|
2810 switch ($this->_tokens[$i]['code']) {
|
Chris@0
|
2811 case T_BITWISE_AND:
|
Chris@0
|
2812 $passByReference = true;
|
Chris@0
|
2813 break;
|
Chris@0
|
2814 case T_VARIABLE:
|
Chris@0
|
2815 $currVar = $i;
|
Chris@0
|
2816 break;
|
Chris@0
|
2817 case T_ELLIPSIS:
|
Chris@0
|
2818 $variableLength = true;
|
Chris@0
|
2819 break;
|
Chris@0
|
2820 case T_ARRAY_HINT:
|
Chris@0
|
2821 case T_CALLABLE:
|
Chris@0
|
2822 $typeHint .= $this->_tokens[$i]['content'];
|
Chris@0
|
2823 break;
|
Chris@0
|
2824 case T_SELF:
|
Chris@0
|
2825 case T_PARENT:
|
Chris@0
|
2826 case T_STATIC:
|
Chris@0
|
2827 // Self is valid, the others invalid, but were probably intended as type hints.
|
Chris@0
|
2828 if (isset($defaultStart) === false) {
|
Chris@0
|
2829 $typeHint .= $this->_tokens[$i]['content'];
|
Chris@0
|
2830 }
|
Chris@0
|
2831 break;
|
Chris@0
|
2832 case T_STRING:
|
Chris@0
|
2833 // This is a string, so it may be a type hint, but it could
|
Chris@0
|
2834 // also be a constant used as a default value.
|
Chris@0
|
2835 $prevComma = false;
|
Chris@0
|
2836 for ($t = $i; $t >= $opener; $t--) {
|
Chris@0
|
2837 if ($this->_tokens[$t]['code'] === T_COMMA) {
|
Chris@0
|
2838 $prevComma = $t;
|
Chris@0
|
2839 break;
|
Chris@0
|
2840 }
|
Chris@0
|
2841 }
|
Chris@0
|
2842
|
Chris@0
|
2843 if ($prevComma !== false) {
|
Chris@0
|
2844 $nextEquals = false;
|
Chris@0
|
2845 for ($t = $prevComma; $t < $i; $t++) {
|
Chris@0
|
2846 if ($this->_tokens[$t]['code'] === T_EQUAL) {
|
Chris@0
|
2847 $nextEquals = $t;
|
Chris@0
|
2848 break;
|
Chris@0
|
2849 }
|
Chris@0
|
2850 }
|
Chris@0
|
2851
|
Chris@0
|
2852 if ($nextEquals !== false) {
|
Chris@0
|
2853 break;
|
Chris@0
|
2854 }
|
Chris@0
|
2855 }
|
Chris@0
|
2856
|
Chris@0
|
2857 if ($defaultStart === null) {
|
Chris@0
|
2858 $typeHint .= $this->_tokens[$i]['content'];
|
Chris@0
|
2859 }
|
Chris@0
|
2860 break;
|
Chris@0
|
2861 case T_NS_SEPARATOR:
|
Chris@0
|
2862 // Part of a type hint or default value.
|
Chris@0
|
2863 if ($defaultStart === null) {
|
Chris@0
|
2864 $typeHint .= $this->_tokens[$i]['content'];
|
Chris@0
|
2865 }
|
Chris@0
|
2866 break;
|
Chris@0
|
2867 case T_NULLABLE:
|
Chris@0
|
2868 if ($defaultStart === null) {
|
Chris@0
|
2869 $nullableType = true;
|
Chris@0
|
2870 $typeHint .= $this->_tokens[$i]['content'];
|
Chris@0
|
2871 }
|
Chris@0
|
2872 break;
|
Chris@0
|
2873 case T_CLOSE_PARENTHESIS:
|
Chris@0
|
2874 case T_COMMA:
|
Chris@0
|
2875 // If it's null, then there must be no parameters for this
|
Chris@0
|
2876 // method.
|
Chris@0
|
2877 if ($currVar === null) {
|
Chris@0
|
2878 continue;
|
Chris@0
|
2879 }
|
Chris@0
|
2880
|
Chris@0
|
2881 $vars[$paramCount] = array();
|
Chris@0
|
2882 $vars[$paramCount]['token'] = $currVar;
|
Chris@0
|
2883 $vars[$paramCount]['name'] = $this->_tokens[$currVar]['content'];
|
Chris@0
|
2884 $vars[$paramCount]['content'] = trim($this->getTokensAsString($paramStart, ($i - $paramStart)));
|
Chris@0
|
2885
|
Chris@0
|
2886 if ($defaultStart !== null) {
|
Chris@0
|
2887 $vars[$paramCount]['default'] = trim($this->getTokensAsString($defaultStart, ($i - $defaultStart)));
|
Chris@0
|
2888 }
|
Chris@0
|
2889
|
Chris@0
|
2890 $vars[$paramCount]['pass_by_reference'] = $passByReference;
|
Chris@0
|
2891 $vars[$paramCount]['variable_length'] = $variableLength;
|
Chris@0
|
2892 $vars[$paramCount]['type_hint'] = $typeHint;
|
Chris@0
|
2893 $vars[$paramCount]['nullable_type'] = $nullableType;
|
Chris@0
|
2894
|
Chris@0
|
2895 // Reset the vars, as we are about to process the next parameter.
|
Chris@0
|
2896 $defaultStart = null;
|
Chris@0
|
2897 $paramStart = ($i + 1);
|
Chris@0
|
2898 $passByReference = false;
|
Chris@0
|
2899 $variableLength = false;
|
Chris@0
|
2900 $typeHint = '';
|
Chris@0
|
2901 $nullableType = false;
|
Chris@0
|
2902
|
Chris@0
|
2903 $paramCount++;
|
Chris@0
|
2904 break;
|
Chris@0
|
2905 case T_EQUAL:
|
Chris@0
|
2906 $defaultStart = ($i + 1);
|
Chris@0
|
2907 break;
|
Chris@0
|
2908 }//end switch
|
Chris@0
|
2909 }//end for
|
Chris@0
|
2910
|
Chris@0
|
2911 return $vars;
|
Chris@0
|
2912
|
Chris@0
|
2913 }//end getMethodParameters()
|
Chris@0
|
2914
|
Chris@0
|
2915
|
Chris@0
|
2916 /**
|
Chris@0
|
2917 * Returns the visibility and implementation properties of a method.
|
Chris@0
|
2918 *
|
Chris@0
|
2919 * The format of the array is:
|
Chris@0
|
2920 * <code>
|
Chris@0
|
2921 * array(
|
Chris@0
|
2922 * 'scope' => 'public', // public private or protected
|
Chris@0
|
2923 * 'scope_specified' => true, // true is scope keyword was found.
|
Chris@0
|
2924 * 'is_abstract' => false, // true if the abstract keyword was found.
|
Chris@0
|
2925 * 'is_final' => false, // true if the final keyword was found.
|
Chris@0
|
2926 * 'is_static' => false, // true if the static keyword was found.
|
Chris@0
|
2927 * 'is_closure' => false, // true if no name is found.
|
Chris@0
|
2928 * );
|
Chris@0
|
2929 * </code>
|
Chris@0
|
2930 *
|
Chris@0
|
2931 * @param int $stackPtr The position in the stack of the T_FUNCTION token to
|
Chris@0
|
2932 * acquire the properties for.
|
Chris@0
|
2933 *
|
Chris@0
|
2934 * @return array
|
Chris@0
|
2935 * @throws PHP_CodeSniffer_Exception If the specified position is not a
|
Chris@0
|
2936 * T_FUNCTION token.
|
Chris@0
|
2937 */
|
Chris@0
|
2938 public function getMethodProperties($stackPtr)
|
Chris@0
|
2939 {
|
Chris@0
|
2940 if ($this->_tokens[$stackPtr]['code'] !== T_FUNCTION) {
|
Chris@0
|
2941 throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION');
|
Chris@0
|
2942 }
|
Chris@0
|
2943
|
Chris@0
|
2944 $valid = array(
|
Chris@0
|
2945 T_PUBLIC => T_PUBLIC,
|
Chris@0
|
2946 T_PRIVATE => T_PRIVATE,
|
Chris@0
|
2947 T_PROTECTED => T_PROTECTED,
|
Chris@0
|
2948 T_STATIC => T_STATIC,
|
Chris@0
|
2949 T_FINAL => T_FINAL,
|
Chris@0
|
2950 T_ABSTRACT => T_ABSTRACT,
|
Chris@0
|
2951 T_WHITESPACE => T_WHITESPACE,
|
Chris@0
|
2952 T_COMMENT => T_COMMENT,
|
Chris@0
|
2953 T_DOC_COMMENT => T_DOC_COMMENT,
|
Chris@0
|
2954 );
|
Chris@0
|
2955
|
Chris@0
|
2956 $scope = 'public';
|
Chris@0
|
2957 $scopeSpecified = false;
|
Chris@0
|
2958 $isAbstract = false;
|
Chris@0
|
2959 $isFinal = false;
|
Chris@0
|
2960 $isStatic = false;
|
Chris@0
|
2961 $isClosure = $this->isAnonymousFunction($stackPtr);
|
Chris@0
|
2962
|
Chris@0
|
2963 for ($i = ($stackPtr - 1); $i > 0; $i--) {
|
Chris@0
|
2964 if (isset($valid[$this->_tokens[$i]['code']]) === false) {
|
Chris@0
|
2965 break;
|
Chris@0
|
2966 }
|
Chris@0
|
2967
|
Chris@0
|
2968 switch ($this->_tokens[$i]['code']) {
|
Chris@0
|
2969 case T_PUBLIC:
|
Chris@0
|
2970 $scope = 'public';
|
Chris@0
|
2971 $scopeSpecified = true;
|
Chris@0
|
2972 break;
|
Chris@0
|
2973 case T_PRIVATE:
|
Chris@0
|
2974 $scope = 'private';
|
Chris@0
|
2975 $scopeSpecified = true;
|
Chris@0
|
2976 break;
|
Chris@0
|
2977 case T_PROTECTED:
|
Chris@0
|
2978 $scope = 'protected';
|
Chris@0
|
2979 $scopeSpecified = true;
|
Chris@0
|
2980 break;
|
Chris@0
|
2981 case T_ABSTRACT:
|
Chris@0
|
2982 $isAbstract = true;
|
Chris@0
|
2983 break;
|
Chris@0
|
2984 case T_FINAL:
|
Chris@0
|
2985 $isFinal = true;
|
Chris@0
|
2986 break;
|
Chris@0
|
2987 case T_STATIC:
|
Chris@0
|
2988 $isStatic = true;
|
Chris@0
|
2989 break;
|
Chris@0
|
2990 }//end switch
|
Chris@0
|
2991 }//end for
|
Chris@0
|
2992
|
Chris@0
|
2993 return array(
|
Chris@0
|
2994 'scope' => $scope,
|
Chris@0
|
2995 'scope_specified' => $scopeSpecified,
|
Chris@0
|
2996 'is_abstract' => $isAbstract,
|
Chris@0
|
2997 'is_final' => $isFinal,
|
Chris@0
|
2998 'is_static' => $isStatic,
|
Chris@0
|
2999 'is_closure' => $isClosure,
|
Chris@0
|
3000 );
|
Chris@0
|
3001
|
Chris@0
|
3002 }//end getMethodProperties()
|
Chris@0
|
3003
|
Chris@0
|
3004
|
Chris@0
|
3005 /**
|
Chris@0
|
3006 * Returns the visibility and implementation properties of the class member
|
Chris@0
|
3007 * variable found at the specified position in the stack.
|
Chris@0
|
3008 *
|
Chris@0
|
3009 * The format of the array is:
|
Chris@0
|
3010 *
|
Chris@0
|
3011 * <code>
|
Chris@0
|
3012 * array(
|
Chris@0
|
3013 * 'scope' => 'public', // public private or protected
|
Chris@0
|
3014 * 'is_static' => false, // true if the static keyword was found.
|
Chris@0
|
3015 * );
|
Chris@0
|
3016 * </code>
|
Chris@0
|
3017 *
|
Chris@0
|
3018 * @param int $stackPtr The position in the stack of the T_VARIABLE token to
|
Chris@0
|
3019 * acquire the properties for.
|
Chris@0
|
3020 *
|
Chris@0
|
3021 * @return array
|
Chris@0
|
3022 * @throws PHP_CodeSniffer_Exception If the specified position is not a
|
Chris@0
|
3023 * T_VARIABLE token, or if the position is not
|
Chris@0
|
3024 * a class member variable.
|
Chris@0
|
3025 */
|
Chris@0
|
3026 public function getMemberProperties($stackPtr)
|
Chris@0
|
3027 {
|
Chris@0
|
3028 if ($this->_tokens[$stackPtr]['code'] !== T_VARIABLE) {
|
Chris@0
|
3029 throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_VARIABLE');
|
Chris@0
|
3030 }
|
Chris@0
|
3031
|
Chris@0
|
3032 $conditions = array_keys($this->_tokens[$stackPtr]['conditions']);
|
Chris@0
|
3033 $ptr = array_pop($conditions);
|
Chris@0
|
3034 if (isset($this->_tokens[$ptr]) === false
|
Chris@0
|
3035 || ($this->_tokens[$ptr]['code'] !== T_CLASS
|
Chris@0
|
3036 && $this->_tokens[$ptr]['code'] !== T_ANON_CLASS
|
Chris@0
|
3037 && $this->_tokens[$ptr]['code'] !== T_TRAIT)
|
Chris@0
|
3038 ) {
|
Chris@0
|
3039 if (isset($this->_tokens[$ptr]) === true
|
Chris@0
|
3040 && $this->_tokens[$ptr]['code'] === T_INTERFACE
|
Chris@0
|
3041 ) {
|
Chris@0
|
3042 // T_VARIABLEs in interfaces can actually be method arguments
|
Chris@0
|
3043 // but they wont be seen as being inside the method because there
|
Chris@0
|
3044 // are no scope openers and closers for abstract methods. If it is in
|
Chris@0
|
3045 // parentheses, we can be pretty sure it is a method argument.
|
Chris@0
|
3046 if (isset($this->_tokens[$stackPtr]['nested_parenthesis']) === false
|
Chris@0
|
3047 || empty($this->_tokens[$stackPtr]['nested_parenthesis']) === true
|
Chris@0
|
3048 ) {
|
Chris@0
|
3049 $error = 'Possible parse error: interfaces may not include member vars';
|
Chris@0
|
3050 $this->addWarning($error, $stackPtr, 'Internal.ParseError.InterfaceHasMemberVar');
|
Chris@0
|
3051 return array();
|
Chris@0
|
3052 }
|
Chris@0
|
3053 } else {
|
Chris@0
|
3054 throw new PHP_CodeSniffer_Exception('$stackPtr is not a class member var');
|
Chris@0
|
3055 }
|
Chris@0
|
3056 }
|
Chris@0
|
3057
|
Chris@0
|
3058 $valid = array(
|
Chris@0
|
3059 T_PUBLIC => T_PUBLIC,
|
Chris@0
|
3060 T_PRIVATE => T_PRIVATE,
|
Chris@0
|
3061 T_PROTECTED => T_PROTECTED,
|
Chris@0
|
3062 T_STATIC => T_STATIC,
|
Chris@0
|
3063 T_WHITESPACE => T_WHITESPACE,
|
Chris@0
|
3064 T_COMMENT => T_COMMENT,
|
Chris@0
|
3065 T_DOC_COMMENT => T_DOC_COMMENT,
|
Chris@0
|
3066 T_VARIABLE => T_VARIABLE,
|
Chris@0
|
3067 T_COMMA => T_COMMA,
|
Chris@0
|
3068 );
|
Chris@0
|
3069
|
Chris@0
|
3070 $scope = 'public';
|
Chris@0
|
3071 $scopeSpecified = false;
|
Chris@0
|
3072 $isStatic = false;
|
Chris@0
|
3073
|
Chris@0
|
3074 for ($i = ($stackPtr - 1); $i > 0; $i--) {
|
Chris@0
|
3075 if (isset($valid[$this->_tokens[$i]['code']]) === false) {
|
Chris@0
|
3076 break;
|
Chris@0
|
3077 }
|
Chris@0
|
3078
|
Chris@0
|
3079 switch ($this->_tokens[$i]['code']) {
|
Chris@0
|
3080 case T_PUBLIC:
|
Chris@0
|
3081 $scope = 'public';
|
Chris@0
|
3082 $scopeSpecified = true;
|
Chris@0
|
3083 break;
|
Chris@0
|
3084 case T_PRIVATE:
|
Chris@0
|
3085 $scope = 'private';
|
Chris@0
|
3086 $scopeSpecified = true;
|
Chris@0
|
3087 break;
|
Chris@0
|
3088 case T_PROTECTED:
|
Chris@0
|
3089 $scope = 'protected';
|
Chris@0
|
3090 $scopeSpecified = true;
|
Chris@0
|
3091 break;
|
Chris@0
|
3092 case T_STATIC:
|
Chris@0
|
3093 $isStatic = true;
|
Chris@0
|
3094 break;
|
Chris@0
|
3095 }
|
Chris@0
|
3096 }//end for
|
Chris@0
|
3097
|
Chris@0
|
3098 return array(
|
Chris@0
|
3099 'scope' => $scope,
|
Chris@0
|
3100 'scope_specified' => $scopeSpecified,
|
Chris@0
|
3101 'is_static' => $isStatic,
|
Chris@0
|
3102 );
|
Chris@0
|
3103
|
Chris@0
|
3104 }//end getMemberProperties()
|
Chris@0
|
3105
|
Chris@0
|
3106
|
Chris@0
|
3107 /**
|
Chris@0
|
3108 * Returns the visibility and implementation properties of a class.
|
Chris@0
|
3109 *
|
Chris@0
|
3110 * The format of the array is:
|
Chris@0
|
3111 * <code>
|
Chris@0
|
3112 * array(
|
Chris@0
|
3113 * 'is_abstract' => false, // true if the abstract keyword was found.
|
Chris@0
|
3114 * 'is_final' => false, // true if the final keyword was found.
|
Chris@0
|
3115 * );
|
Chris@0
|
3116 * </code>
|
Chris@0
|
3117 *
|
Chris@0
|
3118 * @param int $stackPtr The position in the stack of the T_CLASS token to
|
Chris@0
|
3119 * acquire the properties for.
|
Chris@0
|
3120 *
|
Chris@0
|
3121 * @return array
|
Chris@0
|
3122 * @throws PHP_CodeSniffer_Exception If the specified position is not a
|
Chris@0
|
3123 * T_CLASS token.
|
Chris@0
|
3124 */
|
Chris@0
|
3125 public function getClassProperties($stackPtr)
|
Chris@0
|
3126 {
|
Chris@0
|
3127 if ($this->_tokens[$stackPtr]['code'] !== T_CLASS) {
|
Chris@0
|
3128 throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_CLASS');
|
Chris@0
|
3129 }
|
Chris@0
|
3130
|
Chris@0
|
3131 $valid = array(
|
Chris@0
|
3132 T_FINAL => T_FINAL,
|
Chris@0
|
3133 T_ABSTRACT => T_ABSTRACT,
|
Chris@0
|
3134 T_WHITESPACE => T_WHITESPACE,
|
Chris@0
|
3135 T_COMMENT => T_COMMENT,
|
Chris@0
|
3136 T_DOC_COMMENT => T_DOC_COMMENT,
|
Chris@0
|
3137 );
|
Chris@0
|
3138
|
Chris@0
|
3139 $isAbstract = false;
|
Chris@0
|
3140 $isFinal = false;
|
Chris@0
|
3141
|
Chris@0
|
3142 for ($i = ($stackPtr - 1); $i > 0; $i--) {
|
Chris@0
|
3143 if (isset($valid[$this->_tokens[$i]['code']]) === false) {
|
Chris@0
|
3144 break;
|
Chris@0
|
3145 }
|
Chris@0
|
3146
|
Chris@0
|
3147 switch ($this->_tokens[$i]['code']) {
|
Chris@0
|
3148 case T_ABSTRACT:
|
Chris@0
|
3149 $isAbstract = true;
|
Chris@0
|
3150 break;
|
Chris@0
|
3151
|
Chris@0
|
3152 case T_FINAL:
|
Chris@0
|
3153 $isFinal = true;
|
Chris@0
|
3154 break;
|
Chris@0
|
3155 }
|
Chris@0
|
3156 }//end for
|
Chris@0
|
3157
|
Chris@0
|
3158 return array(
|
Chris@0
|
3159 'is_abstract' => $isAbstract,
|
Chris@0
|
3160 'is_final' => $isFinal,
|
Chris@0
|
3161 );
|
Chris@0
|
3162
|
Chris@0
|
3163 }//end getClassProperties()
|
Chris@0
|
3164
|
Chris@0
|
3165
|
Chris@0
|
3166 /**
|
Chris@0
|
3167 * Determine if the passed token is a reference operator.
|
Chris@0
|
3168 *
|
Chris@0
|
3169 * Returns true if the specified token position represents a reference.
|
Chris@0
|
3170 * Returns false if the token represents a bitwise operator.
|
Chris@0
|
3171 *
|
Chris@0
|
3172 * @param int $stackPtr The position of the T_BITWISE_AND token.
|
Chris@0
|
3173 *
|
Chris@0
|
3174 * @return boolean
|
Chris@0
|
3175 */
|
Chris@0
|
3176 public function isReference($stackPtr)
|
Chris@0
|
3177 {
|
Chris@0
|
3178 if ($this->_tokens[$stackPtr]['code'] !== T_BITWISE_AND) {
|
Chris@0
|
3179 return false;
|
Chris@0
|
3180 }
|
Chris@0
|
3181
|
Chris@0
|
3182 $tokenBefore = $this->findPrevious(
|
Chris@0
|
3183 PHP_CodeSniffer_Tokens::$emptyTokens,
|
Chris@0
|
3184 ($stackPtr - 1),
|
Chris@0
|
3185 null,
|
Chris@0
|
3186 true
|
Chris@0
|
3187 );
|
Chris@0
|
3188
|
Chris@0
|
3189 if ($this->_tokens[$tokenBefore]['code'] === T_FUNCTION) {
|
Chris@0
|
3190 // Function returns a reference.
|
Chris@0
|
3191 return true;
|
Chris@0
|
3192 }
|
Chris@0
|
3193
|
Chris@0
|
3194 if ($this->_tokens[$tokenBefore]['code'] === T_DOUBLE_ARROW) {
|
Chris@0
|
3195 // Inside a foreach loop, this is a reference.
|
Chris@0
|
3196 return true;
|
Chris@0
|
3197 }
|
Chris@0
|
3198
|
Chris@0
|
3199 if ($this->_tokens[$tokenBefore]['code'] === T_AS) {
|
Chris@0
|
3200 // Inside a foreach loop, this is a reference.
|
Chris@0
|
3201 return true;
|
Chris@0
|
3202 }
|
Chris@0
|
3203
|
Chris@0
|
3204 if ($this->_tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY) {
|
Chris@0
|
3205 // Inside an array declaration, this is a reference.
|
Chris@0
|
3206 return true;
|
Chris@0
|
3207 }
|
Chris@0
|
3208
|
Chris@0
|
3209 if (isset(PHP_CodeSniffer_Tokens::$assignmentTokens[$this->_tokens[$tokenBefore]['code']]) === true) {
|
Chris@0
|
3210 // This is directly after an assignment. It's a reference. Even if
|
Chris@0
|
3211 // it is part of an operation, the other tests will handle it.
|
Chris@0
|
3212 return true;
|
Chris@0
|
3213 }
|
Chris@0
|
3214
|
Chris@0
|
3215 if (isset($this->_tokens[$stackPtr]['nested_parenthesis']) === true) {
|
Chris@0
|
3216 $brackets = $this->_tokens[$stackPtr]['nested_parenthesis'];
|
Chris@0
|
3217 $lastBracket = array_pop($brackets);
|
Chris@0
|
3218 if (isset($this->_tokens[$lastBracket]['parenthesis_owner']) === true) {
|
Chris@0
|
3219 $owner = $this->_tokens[$this->_tokens[$lastBracket]['parenthesis_owner']];
|
Chris@0
|
3220 if ($owner['code'] === T_FUNCTION
|
Chris@0
|
3221 || $owner['code'] === T_CLOSURE
|
Chris@0
|
3222 || $owner['code'] === T_ARRAY
|
Chris@0
|
3223 ) {
|
Chris@0
|
3224 // Inside a function or array declaration, this is a reference.
|
Chris@0
|
3225 return true;
|
Chris@0
|
3226 }
|
Chris@0
|
3227 } else {
|
Chris@0
|
3228 $prev = false;
|
Chris@0
|
3229 for ($t = ($this->_tokens[$lastBracket]['parenthesis_opener'] - 1); $t >= 0; $t--) {
|
Chris@0
|
3230 if ($this->_tokens[$t]['code'] !== T_WHITESPACE) {
|
Chris@0
|
3231 $prev = $t;
|
Chris@0
|
3232 break;
|
Chris@0
|
3233 }
|
Chris@0
|
3234 }
|
Chris@0
|
3235
|
Chris@0
|
3236 if ($prev !== false && $this->_tokens[$prev]['code'] === T_USE) {
|
Chris@0
|
3237 return true;
|
Chris@0
|
3238 }
|
Chris@0
|
3239 }//end if
|
Chris@0
|
3240 }//end if
|
Chris@0
|
3241
|
Chris@0
|
3242 $tokenAfter = $this->findNext(
|
Chris@0
|
3243 PHP_CodeSniffer_Tokens::$emptyTokens,
|
Chris@0
|
3244 ($stackPtr + 1),
|
Chris@0
|
3245 null,
|
Chris@0
|
3246 true
|
Chris@0
|
3247 );
|
Chris@0
|
3248
|
Chris@0
|
3249 if ($this->_tokens[$tokenAfter]['code'] === T_VARIABLE
|
Chris@0
|
3250 && ($this->_tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS
|
Chris@0
|
3251 || $this->_tokens[$tokenBefore]['code'] === T_COMMA)
|
Chris@0
|
3252 ) {
|
Chris@0
|
3253 return true;
|
Chris@0
|
3254 }
|
Chris@0
|
3255
|
Chris@0
|
3256 return false;
|
Chris@0
|
3257
|
Chris@0
|
3258 }//end isReference()
|
Chris@0
|
3259
|
Chris@0
|
3260
|
Chris@0
|
3261 /**
|
Chris@0
|
3262 * Returns the content of the tokens from the specified start position in
|
Chris@0
|
3263 * the token stack for the specified length.
|
Chris@0
|
3264 *
|
Chris@0
|
3265 * @param int $start The position to start from in the token stack.
|
Chris@0
|
3266 * @param int $length The length of tokens to traverse from the start pos.
|
Chris@0
|
3267 *
|
Chris@0
|
3268 * @return string The token contents.
|
Chris@0
|
3269 */
|
Chris@0
|
3270 public function getTokensAsString($start, $length)
|
Chris@0
|
3271 {
|
Chris@0
|
3272 $str = '';
|
Chris@0
|
3273 $end = ($start + $length);
|
Chris@0
|
3274 if ($end > $this->numTokens) {
|
Chris@0
|
3275 $end = $this->numTokens;
|
Chris@0
|
3276 }
|
Chris@0
|
3277
|
Chris@0
|
3278 for ($i = $start; $i < $end; $i++) {
|
Chris@0
|
3279 $str .= $this->_tokens[$i]['content'];
|
Chris@0
|
3280 }
|
Chris@0
|
3281
|
Chris@0
|
3282 return $str;
|
Chris@0
|
3283
|
Chris@0
|
3284 }//end getTokensAsString()
|
Chris@0
|
3285
|
Chris@0
|
3286
|
Chris@0
|
3287 /**
|
Chris@0
|
3288 * Returns the position of the previous specified token(s).
|
Chris@0
|
3289 *
|
Chris@0
|
3290 * If a value is specified, the previous token of the specified type(s)
|
Chris@0
|
3291 * containing the specified value will be returned.
|
Chris@0
|
3292 *
|
Chris@0
|
3293 * Returns false if no token can be found.
|
Chris@0
|
3294 *
|
Chris@0
|
3295 * @param int|array $types The type(s) of tokens to search for.
|
Chris@0
|
3296 * @param int $start The position to start searching from in the
|
Chris@0
|
3297 * token stack.
|
Chris@0
|
3298 * @param int $end The end position to fail if no token is found.
|
Chris@0
|
3299 * if not specified or null, end will default to
|
Chris@0
|
3300 * the start of the token stack.
|
Chris@0
|
3301 * @param bool $exclude If true, find the previous token that are NOT of
|
Chris@0
|
3302 * the types specified in $types.
|
Chris@0
|
3303 * @param string $value The value that the token(s) must be equal to.
|
Chris@0
|
3304 * If value is omitted, tokens with any value will
|
Chris@0
|
3305 * be returned.
|
Chris@0
|
3306 * @param bool $local If true, tokens outside the current statement
|
Chris@0
|
3307 * will not be checked. IE. checking will stop
|
Chris@0
|
3308 * at the previous semi-colon found.
|
Chris@0
|
3309 *
|
Chris@0
|
3310 * @return int|bool
|
Chris@0
|
3311 * @see findNext()
|
Chris@0
|
3312 */
|
Chris@0
|
3313 public function findPrevious(
|
Chris@0
|
3314 $types,
|
Chris@0
|
3315 $start,
|
Chris@0
|
3316 $end=null,
|
Chris@0
|
3317 $exclude=false,
|
Chris@0
|
3318 $value=null,
|
Chris@0
|
3319 $local=false
|
Chris@0
|
3320 ) {
|
Chris@0
|
3321 $types = (array) $types;
|
Chris@0
|
3322
|
Chris@0
|
3323 if ($end === null) {
|
Chris@0
|
3324 $end = 0;
|
Chris@0
|
3325 }
|
Chris@0
|
3326
|
Chris@0
|
3327 for ($i = $start; $i >= $end; $i--) {
|
Chris@0
|
3328 $found = (bool) $exclude;
|
Chris@0
|
3329 foreach ($types as $type) {
|
Chris@0
|
3330 if ($this->_tokens[$i]['code'] === $type) {
|
Chris@0
|
3331 $found = !$exclude;
|
Chris@0
|
3332 break;
|
Chris@0
|
3333 }
|
Chris@0
|
3334 }
|
Chris@0
|
3335
|
Chris@0
|
3336 if ($found === true) {
|
Chris@0
|
3337 if ($value === null) {
|
Chris@0
|
3338 return $i;
|
Chris@0
|
3339 } else if ($this->_tokens[$i]['content'] === $value) {
|
Chris@0
|
3340 return $i;
|
Chris@0
|
3341 }
|
Chris@0
|
3342 }
|
Chris@0
|
3343
|
Chris@0
|
3344 if ($local === true) {
|
Chris@0
|
3345 if (isset($this->_tokens[$i]['scope_opener']) === true
|
Chris@0
|
3346 && $i === $this->_tokens[$i]['scope_closer']
|
Chris@0
|
3347 ) {
|
Chris@0
|
3348 $i = $this->_tokens[$i]['scope_opener'];
|
Chris@0
|
3349 } else if (isset($this->_tokens[$i]['bracket_opener']) === true
|
Chris@0
|
3350 && $i === $this->_tokens[$i]['bracket_closer']
|
Chris@0
|
3351 ) {
|
Chris@0
|
3352 $i = $this->_tokens[$i]['bracket_opener'];
|
Chris@0
|
3353 } else if (isset($this->_tokens[$i]['parenthesis_opener']) === true
|
Chris@0
|
3354 && $i === $this->_tokens[$i]['parenthesis_closer']
|
Chris@0
|
3355 ) {
|
Chris@0
|
3356 $i = $this->_tokens[$i]['parenthesis_opener'];
|
Chris@0
|
3357 } else if ($this->_tokens[$i]['code'] === T_SEMICOLON) {
|
Chris@0
|
3358 break;
|
Chris@0
|
3359 }
|
Chris@0
|
3360 }
|
Chris@0
|
3361 }//end for
|
Chris@0
|
3362
|
Chris@0
|
3363 return false;
|
Chris@0
|
3364
|
Chris@0
|
3365 }//end findPrevious()
|
Chris@0
|
3366
|
Chris@0
|
3367
|
Chris@0
|
3368 /**
|
Chris@0
|
3369 * Returns the position of the next specified token(s).
|
Chris@0
|
3370 *
|
Chris@0
|
3371 * If a value is specified, the next token of the specified type(s)
|
Chris@0
|
3372 * containing the specified value will be returned.
|
Chris@0
|
3373 *
|
Chris@0
|
3374 * Returns false if no token can be found.
|
Chris@0
|
3375 *
|
Chris@0
|
3376 * @param int|array $types The type(s) of tokens to search for.
|
Chris@0
|
3377 * @param int $start The position to start searching from in the
|
Chris@0
|
3378 * token stack.
|
Chris@0
|
3379 * @param int $end The end position to fail if no token is found.
|
Chris@0
|
3380 * if not specified or null, end will default to
|
Chris@0
|
3381 * the end of the token stack.
|
Chris@0
|
3382 * @param bool $exclude If true, find the next token that is NOT of
|
Chris@0
|
3383 * a type specified in $types.
|
Chris@0
|
3384 * @param string $value The value that the token(s) must be equal to.
|
Chris@0
|
3385 * If value is omitted, tokens with any value will
|
Chris@0
|
3386 * be returned.
|
Chris@0
|
3387 * @param bool $local If true, tokens outside the current statement
|
Chris@0
|
3388 * will not be checked. i.e., checking will stop
|
Chris@0
|
3389 * at the next semi-colon found.
|
Chris@0
|
3390 *
|
Chris@0
|
3391 * @return int|bool
|
Chris@0
|
3392 * @see findPrevious()
|
Chris@0
|
3393 */
|
Chris@0
|
3394 public function findNext(
|
Chris@0
|
3395 $types,
|
Chris@0
|
3396 $start,
|
Chris@0
|
3397 $end=null,
|
Chris@0
|
3398 $exclude=false,
|
Chris@0
|
3399 $value=null,
|
Chris@0
|
3400 $local=false
|
Chris@0
|
3401 ) {
|
Chris@0
|
3402 $types = (array) $types;
|
Chris@0
|
3403
|
Chris@0
|
3404 if ($end === null || $end > $this->numTokens) {
|
Chris@0
|
3405 $end = $this->numTokens;
|
Chris@0
|
3406 }
|
Chris@0
|
3407
|
Chris@0
|
3408 for ($i = $start; $i < $end; $i++) {
|
Chris@0
|
3409 $found = (bool) $exclude;
|
Chris@0
|
3410 foreach ($types as $type) {
|
Chris@0
|
3411 if ($this->_tokens[$i]['code'] === $type) {
|
Chris@0
|
3412 $found = !$exclude;
|
Chris@0
|
3413 break;
|
Chris@0
|
3414 }
|
Chris@0
|
3415 }
|
Chris@0
|
3416
|
Chris@0
|
3417 if ($found === true) {
|
Chris@0
|
3418 if ($value === null) {
|
Chris@0
|
3419 return $i;
|
Chris@0
|
3420 } else if ($this->_tokens[$i]['content'] === $value) {
|
Chris@0
|
3421 return $i;
|
Chris@0
|
3422 }
|
Chris@0
|
3423 }
|
Chris@0
|
3424
|
Chris@0
|
3425 if ($local === true && $this->_tokens[$i]['code'] === T_SEMICOLON) {
|
Chris@0
|
3426 break;
|
Chris@0
|
3427 }
|
Chris@0
|
3428 }//end for
|
Chris@0
|
3429
|
Chris@0
|
3430 return false;
|
Chris@0
|
3431
|
Chris@0
|
3432 }//end findNext()
|
Chris@0
|
3433
|
Chris@0
|
3434
|
Chris@0
|
3435 /**
|
Chris@0
|
3436 * Returns the position of the first non-whitespace token in a statement.
|
Chris@0
|
3437 *
|
Chris@0
|
3438 * @param int $start The position to start searching from in the token stack.
|
Chris@0
|
3439 * @param int|array $ignore Token types that should not be considered stop points.
|
Chris@0
|
3440 *
|
Chris@0
|
3441 * @return int
|
Chris@0
|
3442 */
|
Chris@0
|
3443 public function findStartOfStatement($start, $ignore=null)
|
Chris@0
|
3444 {
|
Chris@0
|
3445 $endTokens = PHP_CodeSniffer_Tokens::$blockOpeners;
|
Chris@0
|
3446
|
Chris@0
|
3447 $endTokens[T_COLON] = true;
|
Chris@0
|
3448 $endTokens[T_COMMA] = true;
|
Chris@0
|
3449 $endTokens[T_DOUBLE_ARROW] = true;
|
Chris@0
|
3450 $endTokens[T_SEMICOLON] = true;
|
Chris@0
|
3451 $endTokens[T_OPEN_TAG] = true;
|
Chris@0
|
3452 $endTokens[T_CLOSE_TAG] = true;
|
Chris@0
|
3453 $endTokens[T_OPEN_SHORT_ARRAY] = true;
|
Chris@0
|
3454
|
Chris@0
|
3455 if ($ignore !== null) {
|
Chris@0
|
3456 $ignore = (array) $ignore;
|
Chris@0
|
3457 foreach ($ignore as $code) {
|
Chris@0
|
3458 if (isset($endTokens[$code]) === true) {
|
Chris@0
|
3459 unset($endTokens[$code]);
|
Chris@0
|
3460 }
|
Chris@0
|
3461 }
|
Chris@0
|
3462 }
|
Chris@0
|
3463
|
Chris@0
|
3464 $lastNotEmpty = $start;
|
Chris@0
|
3465
|
Chris@0
|
3466 for ($i = $start; $i >= 0; $i--) {
|
Chris@0
|
3467 if (isset($endTokens[$this->_tokens[$i]['code']]) === true) {
|
Chris@0
|
3468 // Found the end of the previous statement.
|
Chris@0
|
3469 return $lastNotEmpty;
|
Chris@0
|
3470 }
|
Chris@0
|
3471
|
Chris@0
|
3472 if (isset($this->_tokens[$i]['scope_opener']) === true
|
Chris@0
|
3473 && $i === $this->_tokens[$i]['scope_closer']
|
Chris@0
|
3474 ) {
|
Chris@0
|
3475 // Found the end of the previous scope block.
|
Chris@0
|
3476 return $lastNotEmpty;
|
Chris@0
|
3477 }
|
Chris@0
|
3478
|
Chris@0
|
3479 // Skip nested statements.
|
Chris@0
|
3480 if (isset($this->_tokens[$i]['bracket_opener']) === true
|
Chris@0
|
3481 && $i === $this->_tokens[$i]['bracket_closer']
|
Chris@0
|
3482 ) {
|
Chris@0
|
3483 $i = $this->_tokens[$i]['bracket_opener'];
|
Chris@0
|
3484 } else if (isset($this->_tokens[$i]['parenthesis_opener']) === true
|
Chris@0
|
3485 && $i === $this->_tokens[$i]['parenthesis_closer']
|
Chris@0
|
3486 ) {
|
Chris@0
|
3487 $i = $this->_tokens[$i]['parenthesis_opener'];
|
Chris@0
|
3488 }
|
Chris@0
|
3489
|
Chris@0
|
3490 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$this->_tokens[$i]['code']]) === false) {
|
Chris@0
|
3491 $lastNotEmpty = $i;
|
Chris@0
|
3492 }
|
Chris@0
|
3493 }//end for
|
Chris@0
|
3494
|
Chris@0
|
3495 return 0;
|
Chris@0
|
3496
|
Chris@0
|
3497 }//end findStartOfStatement()
|
Chris@0
|
3498
|
Chris@0
|
3499
|
Chris@0
|
3500 /**
|
Chris@0
|
3501 * Returns the position of the last non-whitespace token in a statement.
|
Chris@0
|
3502 *
|
Chris@0
|
3503 * @param int $start The position to start searching from in the token stack.
|
Chris@0
|
3504 * @param int|array $ignore Token types that should not be considered stop points.
|
Chris@0
|
3505 *
|
Chris@0
|
3506 * @return int
|
Chris@0
|
3507 */
|
Chris@0
|
3508 public function findEndOfStatement($start, $ignore=null)
|
Chris@0
|
3509 {
|
Chris@0
|
3510 $endTokens = array(
|
Chris@0
|
3511 T_COLON => true,
|
Chris@0
|
3512 T_COMMA => true,
|
Chris@0
|
3513 T_DOUBLE_ARROW => true,
|
Chris@0
|
3514 T_SEMICOLON => true,
|
Chris@0
|
3515 T_CLOSE_PARENTHESIS => true,
|
Chris@0
|
3516 T_CLOSE_SQUARE_BRACKET => true,
|
Chris@0
|
3517 T_CLOSE_CURLY_BRACKET => true,
|
Chris@0
|
3518 T_CLOSE_SHORT_ARRAY => true,
|
Chris@0
|
3519 T_OPEN_TAG => true,
|
Chris@0
|
3520 T_CLOSE_TAG => true,
|
Chris@0
|
3521 );
|
Chris@0
|
3522
|
Chris@0
|
3523 if ($ignore !== null) {
|
Chris@0
|
3524 $ignore = (array) $ignore;
|
Chris@0
|
3525 foreach ($ignore as $code) {
|
Chris@0
|
3526 if (isset($endTokens[$code]) === true) {
|
Chris@0
|
3527 unset($endTokens[$code]);
|
Chris@0
|
3528 }
|
Chris@0
|
3529 }
|
Chris@0
|
3530 }
|
Chris@0
|
3531
|
Chris@0
|
3532 $lastNotEmpty = $start;
|
Chris@0
|
3533
|
Chris@0
|
3534 for ($i = $start; $i < $this->numTokens; $i++) {
|
Chris@0
|
3535 if ($i !== $start && isset($endTokens[$this->_tokens[$i]['code']]) === true) {
|
Chris@0
|
3536 // Found the end of the statement.
|
Chris@0
|
3537 if ($this->_tokens[$i]['code'] === T_CLOSE_PARENTHESIS
|
Chris@0
|
3538 || $this->_tokens[$i]['code'] === T_CLOSE_SQUARE_BRACKET
|
Chris@0
|
3539 || $this->_tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
|
Chris@0
|
3540 || $this->_tokens[$i]['code'] === T_CLOSE_SHORT_ARRAY
|
Chris@0
|
3541 || $this->_tokens[$i]['code'] === T_OPEN_TAG
|
Chris@0
|
3542 || $this->_tokens[$i]['code'] === T_CLOSE_TAG
|
Chris@0
|
3543 ) {
|
Chris@0
|
3544 return $lastNotEmpty;
|
Chris@0
|
3545 }
|
Chris@0
|
3546
|
Chris@0
|
3547 return $i;
|
Chris@0
|
3548 }
|
Chris@0
|
3549
|
Chris@0
|
3550 // Skip nested statements.
|
Chris@0
|
3551 if (isset($this->_tokens[$i]['scope_closer']) === true
|
Chris@0
|
3552 && ($i === $this->_tokens[$i]['scope_opener']
|
Chris@0
|
3553 || $i === $this->_tokens[$i]['scope_condition'])
|
Chris@0
|
3554 ) {
|
Chris@0
|
3555 $i = $this->_tokens[$i]['scope_closer'];
|
Chris@0
|
3556 } else if (isset($this->_tokens[$i]['bracket_closer']) === true
|
Chris@0
|
3557 && $i === $this->_tokens[$i]['bracket_opener']
|
Chris@0
|
3558 ) {
|
Chris@0
|
3559 $i = $this->_tokens[$i]['bracket_closer'];
|
Chris@0
|
3560 } else if (isset($this->_tokens[$i]['parenthesis_closer']) === true
|
Chris@0
|
3561 && $i === $this->_tokens[$i]['parenthesis_opener']
|
Chris@0
|
3562 ) {
|
Chris@0
|
3563 $i = $this->_tokens[$i]['parenthesis_closer'];
|
Chris@0
|
3564 }
|
Chris@0
|
3565
|
Chris@0
|
3566 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$this->_tokens[$i]['code']]) === false) {
|
Chris@0
|
3567 $lastNotEmpty = $i;
|
Chris@0
|
3568 }
|
Chris@0
|
3569 }//end for
|
Chris@0
|
3570
|
Chris@0
|
3571 return ($this->numTokens - 1);
|
Chris@0
|
3572
|
Chris@0
|
3573 }//end findEndOfStatement()
|
Chris@0
|
3574
|
Chris@0
|
3575
|
Chris@0
|
3576 /**
|
Chris@0
|
3577 * Returns the position of the first token on a line, matching given type.
|
Chris@0
|
3578 *
|
Chris@0
|
3579 * Returns false if no token can be found.
|
Chris@0
|
3580 *
|
Chris@0
|
3581 * @param int|array $types The type(s) of tokens to search for.
|
Chris@0
|
3582 * @param int $start The position to start searching from in the
|
Chris@0
|
3583 * token stack. The first token matching on
|
Chris@0
|
3584 * this line before this token will be returned.
|
Chris@0
|
3585 * @param bool $exclude If true, find the token that is NOT of
|
Chris@0
|
3586 * the types specified in $types.
|
Chris@0
|
3587 * @param string $value The value that the token must be equal to.
|
Chris@0
|
3588 * If value is omitted, tokens with any value will
|
Chris@0
|
3589 * be returned.
|
Chris@0
|
3590 *
|
Chris@0
|
3591 * @return int | bool
|
Chris@0
|
3592 */
|
Chris@0
|
3593 public function findFirstOnLine($types, $start, $exclude=false, $value=null)
|
Chris@0
|
3594 {
|
Chris@0
|
3595 if (is_array($types) === false) {
|
Chris@0
|
3596 $types = array($types);
|
Chris@0
|
3597 }
|
Chris@0
|
3598
|
Chris@0
|
3599 $foundToken = false;
|
Chris@0
|
3600
|
Chris@0
|
3601 for ($i = $start; $i >= 0; $i--) {
|
Chris@0
|
3602 if ($this->_tokens[$i]['line'] < $this->_tokens[$start]['line']) {
|
Chris@0
|
3603 break;
|
Chris@0
|
3604 }
|
Chris@0
|
3605
|
Chris@0
|
3606 $found = $exclude;
|
Chris@0
|
3607 foreach ($types as $type) {
|
Chris@0
|
3608 if ($exclude === false) {
|
Chris@0
|
3609 if ($this->_tokens[$i]['code'] === $type) {
|
Chris@0
|
3610 $found = true;
|
Chris@0
|
3611 break;
|
Chris@0
|
3612 }
|
Chris@0
|
3613 } else {
|
Chris@0
|
3614 if ($this->_tokens[$i]['code'] === $type) {
|
Chris@0
|
3615 $found = false;
|
Chris@0
|
3616 break;
|
Chris@0
|
3617 }
|
Chris@0
|
3618 }
|
Chris@0
|
3619 }
|
Chris@0
|
3620
|
Chris@0
|
3621 if ($found === true) {
|
Chris@0
|
3622 if ($value === null) {
|
Chris@0
|
3623 $foundToken = $i;
|
Chris@0
|
3624 } else if ($this->_tokens[$i]['content'] === $value) {
|
Chris@0
|
3625 $foundToken = $i;
|
Chris@0
|
3626 }
|
Chris@0
|
3627 }
|
Chris@0
|
3628 }//end for
|
Chris@0
|
3629
|
Chris@0
|
3630 return $foundToken;
|
Chris@0
|
3631
|
Chris@0
|
3632 }//end findFirstOnLine()
|
Chris@0
|
3633
|
Chris@0
|
3634
|
Chris@0
|
3635 /**
|
Chris@0
|
3636 * Determine if the passed token has a condition of one of the passed types.
|
Chris@0
|
3637 *
|
Chris@0
|
3638 * @param int $stackPtr The position of the token we are checking.
|
Chris@0
|
3639 * @param int|array $types The type(s) of tokens to search for.
|
Chris@0
|
3640 *
|
Chris@0
|
3641 * @return boolean
|
Chris@0
|
3642 */
|
Chris@0
|
3643 public function hasCondition($stackPtr, $types)
|
Chris@0
|
3644 {
|
Chris@0
|
3645 // Check for the existence of the token.
|
Chris@0
|
3646 if (isset($this->_tokens[$stackPtr]) === false) {
|
Chris@0
|
3647 return false;
|
Chris@0
|
3648 }
|
Chris@0
|
3649
|
Chris@0
|
3650 // Make sure the token has conditions.
|
Chris@0
|
3651 if (isset($this->_tokens[$stackPtr]['conditions']) === false) {
|
Chris@0
|
3652 return false;
|
Chris@0
|
3653 }
|
Chris@0
|
3654
|
Chris@0
|
3655 $types = (array) $types;
|
Chris@0
|
3656 $conditions = $this->_tokens[$stackPtr]['conditions'];
|
Chris@0
|
3657
|
Chris@0
|
3658 foreach ($types as $type) {
|
Chris@0
|
3659 if (in_array($type, $conditions) === true) {
|
Chris@0
|
3660 // We found a token with the required type.
|
Chris@0
|
3661 return true;
|
Chris@0
|
3662 }
|
Chris@0
|
3663 }
|
Chris@0
|
3664
|
Chris@0
|
3665 return false;
|
Chris@0
|
3666
|
Chris@0
|
3667 }//end hasCondition()
|
Chris@0
|
3668
|
Chris@0
|
3669
|
Chris@0
|
3670 /**
|
Chris@0
|
3671 * Return the position of the condition for the passed token.
|
Chris@0
|
3672 *
|
Chris@0
|
3673 * Returns FALSE if the token does not have the condition.
|
Chris@0
|
3674 *
|
Chris@0
|
3675 * @param int $stackPtr The position of the token we are checking.
|
Chris@0
|
3676 * @param int $type The type of token to search for.
|
Chris@0
|
3677 *
|
Chris@0
|
3678 * @return int
|
Chris@0
|
3679 */
|
Chris@0
|
3680 public function getCondition($stackPtr, $type)
|
Chris@0
|
3681 {
|
Chris@0
|
3682 // Check for the existence of the token.
|
Chris@0
|
3683 if (isset($this->_tokens[$stackPtr]) === false) {
|
Chris@0
|
3684 return false;
|
Chris@0
|
3685 }
|
Chris@0
|
3686
|
Chris@0
|
3687 // Make sure the token has conditions.
|
Chris@0
|
3688 if (isset($this->_tokens[$stackPtr]['conditions']) === false) {
|
Chris@0
|
3689 return false;
|
Chris@0
|
3690 }
|
Chris@0
|
3691
|
Chris@0
|
3692 $conditions = $this->_tokens[$stackPtr]['conditions'];
|
Chris@0
|
3693 foreach ($conditions as $token => $condition) {
|
Chris@0
|
3694 if ($condition === $type) {
|
Chris@0
|
3695 return $token;
|
Chris@0
|
3696 }
|
Chris@0
|
3697 }
|
Chris@0
|
3698
|
Chris@0
|
3699 return false;
|
Chris@0
|
3700
|
Chris@0
|
3701 }//end getCondition()
|
Chris@0
|
3702
|
Chris@0
|
3703
|
Chris@0
|
3704 /**
|
Chris@0
|
3705 * Returns the name of the class that the specified class extends.
|
Chris@0
|
3706 *
|
Chris@0
|
3707 * Returns FALSE on error or if there is no extended class name.
|
Chris@0
|
3708 *
|
Chris@0
|
3709 * @param int $stackPtr The stack position of the class.
|
Chris@0
|
3710 *
|
Chris@0
|
3711 * @return string
|
Chris@0
|
3712 */
|
Chris@0
|
3713 public function findExtendedClassName($stackPtr)
|
Chris@0
|
3714 {
|
Chris@0
|
3715 // Check for the existence of the token.
|
Chris@0
|
3716 if (isset($this->_tokens[$stackPtr]) === false) {
|
Chris@0
|
3717 return false;
|
Chris@0
|
3718 }
|
Chris@0
|
3719
|
Chris@0
|
3720 if ($this->_tokens[$stackPtr]['code'] !== T_CLASS
|
Chris@0
|
3721 && $this->_tokens[$stackPtr]['code'] !== T_ANON_CLASS
|
Chris@0
|
3722 ) {
|
Chris@0
|
3723 return false;
|
Chris@0
|
3724 }
|
Chris@0
|
3725
|
Chris@0
|
3726 if (isset($this->_tokens[$stackPtr]['scope_closer']) === false) {
|
Chris@0
|
3727 return false;
|
Chris@0
|
3728 }
|
Chris@0
|
3729
|
Chris@0
|
3730 $classCloserIndex = $this->_tokens[$stackPtr]['scope_closer'];
|
Chris@0
|
3731 $extendsIndex = $this->findNext(T_EXTENDS, $stackPtr, $classCloserIndex);
|
Chris@0
|
3732 if (false === $extendsIndex) {
|
Chris@0
|
3733 return false;
|
Chris@0
|
3734 }
|
Chris@0
|
3735
|
Chris@0
|
3736 $find = array(
|
Chris@0
|
3737 T_NS_SEPARATOR,
|
Chris@0
|
3738 T_STRING,
|
Chris@0
|
3739 T_WHITESPACE,
|
Chris@0
|
3740 );
|
Chris@0
|
3741
|
Chris@0
|
3742 $end = $this->findNext($find, ($extendsIndex + 1), $classCloserIndex, true);
|
Chris@0
|
3743 $name = $this->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1));
|
Chris@0
|
3744 $name = trim($name);
|
Chris@0
|
3745
|
Chris@0
|
3746 if ($name === '') {
|
Chris@0
|
3747 return false;
|
Chris@0
|
3748 }
|
Chris@0
|
3749
|
Chris@0
|
3750 return $name;
|
Chris@0
|
3751
|
Chris@0
|
3752 }//end findExtendedClassName()
|
Chris@0
|
3753
|
Chris@0
|
3754
|
Chris@0
|
3755 /**
|
Chris@0
|
3756 * Returns the name(s) of the interface(s) that the specified class implements.
|
Chris@0
|
3757 *
|
Chris@0
|
3758 * Returns FALSE on error or if there are no implemented interface names.
|
Chris@0
|
3759 *
|
Chris@0
|
3760 * @param int $stackPtr The stack position of the class.
|
Chris@0
|
3761 *
|
Chris@0
|
3762 * @return array|false
|
Chris@0
|
3763 */
|
Chris@0
|
3764 public function findImplementedInterfaceNames($stackPtr)
|
Chris@0
|
3765 {
|
Chris@0
|
3766 // Check for the existence of the token.
|
Chris@0
|
3767 if (isset($this->_tokens[$stackPtr]) === false) {
|
Chris@0
|
3768 return false;
|
Chris@0
|
3769 }
|
Chris@0
|
3770
|
Chris@0
|
3771 if ($this->_tokens[$stackPtr]['code'] !== T_CLASS
|
Chris@0
|
3772 && $this->_tokens[$stackPtr]['code'] !== T_ANON_CLASS
|
Chris@0
|
3773 ) {
|
Chris@0
|
3774 return false;
|
Chris@0
|
3775 }
|
Chris@0
|
3776
|
Chris@0
|
3777 if (isset($this->_tokens[$stackPtr]['scope_closer']) === false) {
|
Chris@0
|
3778 return false;
|
Chris@0
|
3779 }
|
Chris@0
|
3780
|
Chris@0
|
3781 $classOpenerIndex = $this->_tokens[$stackPtr]['scope_opener'];
|
Chris@0
|
3782 $implementsIndex = $this->findNext(T_IMPLEMENTS, $stackPtr, $classOpenerIndex);
|
Chris@0
|
3783 if ($implementsIndex === false) {
|
Chris@0
|
3784 return false;
|
Chris@0
|
3785 }
|
Chris@0
|
3786
|
Chris@0
|
3787 $find = array(
|
Chris@0
|
3788 T_NS_SEPARATOR,
|
Chris@0
|
3789 T_STRING,
|
Chris@0
|
3790 T_WHITESPACE,
|
Chris@0
|
3791 T_COMMA,
|
Chris@0
|
3792 );
|
Chris@0
|
3793
|
Chris@0
|
3794 $end = $this->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
|
Chris@0
|
3795 $name = $this->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));
|
Chris@0
|
3796 $name = trim($name);
|
Chris@0
|
3797
|
Chris@0
|
3798 if ($name === '') {
|
Chris@0
|
3799 return false;
|
Chris@0
|
3800 } else {
|
Chris@0
|
3801 $names = explode(',', $name);
|
Chris@0
|
3802 $names = array_map('trim', $names);
|
Chris@0
|
3803 return $names;
|
Chris@0
|
3804 }
|
Chris@0
|
3805
|
Chris@0
|
3806 }//end findImplementedInterfaceNames()
|
Chris@0
|
3807
|
Chris@0
|
3808
|
Chris@0
|
3809 }//end class
|