Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@0
|
3 * Processes pattern strings and checks that the code conforms to the pattern.
|
Chris@0
|
4 *
|
Chris@0
|
5 * PHP version 5
|
Chris@0
|
6 *
|
Chris@0
|
7 * @category PHP
|
Chris@0
|
8 * @package PHP_CodeSniffer
|
Chris@0
|
9 * @author Greg Sherwood <gsherwood@squiz.net>
|
Chris@0
|
10 * @author Marc McIntyre <mmcintyre@squiz.net>
|
Chris@0
|
11 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
|
Chris@0
|
12 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
|
Chris@0
|
13 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
14 */
|
Chris@0
|
15
|
Chris@0
|
16 if (class_exists('PHP_CodeSniffer_Standards_IncorrectPatternException', true) === false) {
|
Chris@0
|
17 $error = 'Class PHP_CodeSniffer_Standards_IncorrectPatternException not found';
|
Chris@0
|
18 throw new PHP_CodeSniffer_Exception($error);
|
Chris@0
|
19 }
|
Chris@0
|
20
|
Chris@0
|
21 /**
|
Chris@0
|
22 * Processes pattern strings and checks that the code conforms to the pattern.
|
Chris@0
|
23 *
|
Chris@0
|
24 * This test essentially checks that code is correctly formatted with whitespace.
|
Chris@0
|
25 *
|
Chris@0
|
26 * @category PHP
|
Chris@0
|
27 * @package PHP_CodeSniffer
|
Chris@0
|
28 * @author Greg Sherwood <gsherwood@squiz.net>
|
Chris@0
|
29 * @author Marc McIntyre <mmcintyre@squiz.net>
|
Chris@0
|
30 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
|
Chris@0
|
31 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
|
Chris@0
|
32 * @version Release: @package_version@
|
Chris@0
|
33 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
34 */
|
Chris@0
|
35 abstract class PHP_CodeSniffer_Standards_AbstractPatternSniff implements PHP_CodeSniffer_Sniff
|
Chris@0
|
36 {
|
Chris@0
|
37
|
Chris@0
|
38 /**
|
Chris@0
|
39 * If true, comments will be ignored if they are found in the code.
|
Chris@0
|
40 *
|
Chris@0
|
41 * @var boolean
|
Chris@0
|
42 */
|
Chris@0
|
43 public $ignoreComments = false;
|
Chris@0
|
44
|
Chris@0
|
45 /**
|
Chris@0
|
46 * The current file being checked.
|
Chris@0
|
47 *
|
Chris@0
|
48 * @var string
|
Chris@0
|
49 */
|
Chris@0
|
50 protected $currFile = '';
|
Chris@0
|
51
|
Chris@0
|
52 /**
|
Chris@0
|
53 * The parsed patterns array.
|
Chris@0
|
54 *
|
Chris@0
|
55 * @var array
|
Chris@0
|
56 */
|
Chris@0
|
57 private $_parsedPatterns = array();
|
Chris@0
|
58
|
Chris@0
|
59 /**
|
Chris@0
|
60 * Tokens that this sniff wishes to process outside of the patterns.
|
Chris@0
|
61 *
|
Chris@0
|
62 * @var array(int)
|
Chris@0
|
63 * @see registerSupplementary()
|
Chris@0
|
64 * @see processSupplementary()
|
Chris@0
|
65 */
|
Chris@0
|
66 private $_supplementaryTokens = array();
|
Chris@0
|
67
|
Chris@0
|
68 /**
|
Chris@0
|
69 * Positions in the stack where errors have occurred.
|
Chris@0
|
70 *
|
Chris@0
|
71 * @var array()
|
Chris@0
|
72 */
|
Chris@0
|
73 private $_errorPos = array();
|
Chris@0
|
74
|
Chris@0
|
75
|
Chris@0
|
76 /**
|
Chris@0
|
77 * Constructs a PHP_CodeSniffer_Standards_AbstractPatternSniff.
|
Chris@0
|
78 *
|
Chris@0
|
79 * @param boolean $ignoreComments If true, comments will be ignored.
|
Chris@0
|
80 */
|
Chris@0
|
81 public function __construct($ignoreComments=null)
|
Chris@0
|
82 {
|
Chris@0
|
83 // This is here for backwards compatibility.
|
Chris@0
|
84 if ($ignoreComments !== null) {
|
Chris@0
|
85 $this->ignoreComments = $ignoreComments;
|
Chris@0
|
86 }
|
Chris@0
|
87
|
Chris@0
|
88 $this->_supplementaryTokens = $this->registerSupplementary();
|
Chris@0
|
89
|
Chris@0
|
90 }//end __construct()
|
Chris@0
|
91
|
Chris@0
|
92
|
Chris@0
|
93 /**
|
Chris@0
|
94 * Registers the tokens to listen to.
|
Chris@0
|
95 *
|
Chris@0
|
96 * Classes extending <i>AbstractPatternTest</i> should implement the
|
Chris@0
|
97 * <i>getPatterns()</i> method to register the patterns they wish to test.
|
Chris@0
|
98 *
|
Chris@0
|
99 * @return int[]
|
Chris@0
|
100 * @see process()
|
Chris@0
|
101 */
|
Chris@0
|
102 public final function register()
|
Chris@0
|
103 {
|
Chris@0
|
104 $listenTypes = array();
|
Chris@0
|
105 $patterns = $this->getPatterns();
|
Chris@0
|
106
|
Chris@0
|
107 foreach ($patterns as $pattern) {
|
Chris@0
|
108 $parsedPattern = $this->_parse($pattern);
|
Chris@0
|
109
|
Chris@0
|
110 // Find a token position in the pattern that we can use
|
Chris@0
|
111 // for a listener token.
|
Chris@0
|
112 $pos = $this->_getListenerTokenPos($parsedPattern);
|
Chris@0
|
113 $tokenType = $parsedPattern[$pos]['token'];
|
Chris@0
|
114 $listenTypes[] = $tokenType;
|
Chris@0
|
115
|
Chris@0
|
116 $patternArray = array(
|
Chris@0
|
117 'listen_pos' => $pos,
|
Chris@0
|
118 'pattern' => $parsedPattern,
|
Chris@0
|
119 'pattern_code' => $pattern,
|
Chris@0
|
120 );
|
Chris@0
|
121
|
Chris@0
|
122 if (isset($this->_parsedPatterns[$tokenType]) === false) {
|
Chris@0
|
123 $this->_parsedPatterns[$tokenType] = array();
|
Chris@0
|
124 }
|
Chris@0
|
125
|
Chris@0
|
126 $this->_parsedPatterns[$tokenType][] = $patternArray;
|
Chris@0
|
127 }//end foreach
|
Chris@0
|
128
|
Chris@0
|
129 return array_unique(array_merge($listenTypes, $this->_supplementaryTokens));
|
Chris@0
|
130
|
Chris@0
|
131 }//end register()
|
Chris@0
|
132
|
Chris@0
|
133
|
Chris@0
|
134 /**
|
Chris@0
|
135 * Returns the token types that the specified pattern is checking for.
|
Chris@0
|
136 *
|
Chris@0
|
137 * Returned array is in the format:
|
Chris@0
|
138 * <code>
|
Chris@0
|
139 * array(
|
Chris@0
|
140 * T_WHITESPACE => 0, // 0 is the position where the T_WHITESPACE token
|
Chris@0
|
141 * // should occur in the pattern.
|
Chris@0
|
142 * );
|
Chris@0
|
143 * </code>
|
Chris@0
|
144 *
|
Chris@0
|
145 * @param array $pattern The parsed pattern to find the acquire the token
|
Chris@0
|
146 * types from.
|
Chris@0
|
147 *
|
Chris@0
|
148 * @return array<int, int>
|
Chris@0
|
149 */
|
Chris@0
|
150 private function _getPatternTokenTypes($pattern)
|
Chris@0
|
151 {
|
Chris@0
|
152 $tokenTypes = array();
|
Chris@0
|
153 foreach ($pattern as $pos => $patternInfo) {
|
Chris@0
|
154 if ($patternInfo['type'] === 'token') {
|
Chris@0
|
155 if (isset($tokenTypes[$patternInfo['token']]) === false) {
|
Chris@0
|
156 $tokenTypes[$patternInfo['token']] = $pos;
|
Chris@0
|
157 }
|
Chris@0
|
158 }
|
Chris@0
|
159 }
|
Chris@0
|
160
|
Chris@0
|
161 return $tokenTypes;
|
Chris@0
|
162
|
Chris@0
|
163 }//end _getPatternTokenTypes()
|
Chris@0
|
164
|
Chris@0
|
165
|
Chris@0
|
166 /**
|
Chris@0
|
167 * Returns the position in the pattern that this test should register as
|
Chris@0
|
168 * a listener for the pattern.
|
Chris@0
|
169 *
|
Chris@0
|
170 * @param array $pattern The pattern to acquire the listener for.
|
Chris@0
|
171 *
|
Chris@0
|
172 * @return int The position in the pattern that this test should register
|
Chris@0
|
173 * as the listener.
|
Chris@0
|
174 * @throws PHP_CodeSniffer_Exception If we could not determine a token
|
Chris@0
|
175 * to listen for.
|
Chris@0
|
176 */
|
Chris@0
|
177 private function _getListenerTokenPos($pattern)
|
Chris@0
|
178 {
|
Chris@0
|
179 $tokenTypes = $this->_getPatternTokenTypes($pattern);
|
Chris@0
|
180 $tokenCodes = array_keys($tokenTypes);
|
Chris@0
|
181 $token = PHP_CodeSniffer_Tokens::getHighestWeightedToken($tokenCodes);
|
Chris@0
|
182
|
Chris@0
|
183 // If we could not get a token.
|
Chris@0
|
184 if ($token === false) {
|
Chris@0
|
185 $error = 'Could not determine a token to listen for';
|
Chris@0
|
186 throw new PHP_CodeSniffer_Exception($error);
|
Chris@0
|
187 }
|
Chris@0
|
188
|
Chris@0
|
189 return $tokenTypes[$token];
|
Chris@0
|
190
|
Chris@0
|
191 }//end _getListenerTokenPos()
|
Chris@0
|
192
|
Chris@0
|
193
|
Chris@0
|
194 /**
|
Chris@0
|
195 * Processes the test.
|
Chris@0
|
196 *
|
Chris@0
|
197 * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where the
|
Chris@0
|
198 * token occurred.
|
Chris@0
|
199 * @param int $stackPtr The position in the tokens stack
|
Chris@0
|
200 * where the listening token type was
|
Chris@0
|
201 * found.
|
Chris@0
|
202 *
|
Chris@0
|
203 * @return void
|
Chris@0
|
204 * @see register()
|
Chris@0
|
205 */
|
Chris@0
|
206 public final function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
|
Chris@0
|
207 {
|
Chris@0
|
208 $file = $phpcsFile->getFilename();
|
Chris@0
|
209 if ($this->currFile !== $file) {
|
Chris@0
|
210 // We have changed files, so clean up.
|
Chris@0
|
211 $this->_errorPos = array();
|
Chris@0
|
212 $this->currFile = $file;
|
Chris@0
|
213 }
|
Chris@0
|
214
|
Chris@0
|
215 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
216
|
Chris@0
|
217 if (in_array($tokens[$stackPtr]['code'], $this->_supplementaryTokens) === true) {
|
Chris@0
|
218 $this->processSupplementary($phpcsFile, $stackPtr);
|
Chris@0
|
219 }
|
Chris@0
|
220
|
Chris@0
|
221 $type = $tokens[$stackPtr]['code'];
|
Chris@0
|
222
|
Chris@0
|
223 // If the type is not set, then it must have been a token registered
|
Chris@0
|
224 // with registerSupplementary().
|
Chris@0
|
225 if (isset($this->_parsedPatterns[$type]) === false) {
|
Chris@0
|
226 return;
|
Chris@0
|
227 }
|
Chris@0
|
228
|
Chris@0
|
229 $allErrors = array();
|
Chris@0
|
230
|
Chris@0
|
231 // Loop over each pattern that is listening to the current token type
|
Chris@0
|
232 // that we are processing.
|
Chris@0
|
233 foreach ($this->_parsedPatterns[$type] as $patternInfo) {
|
Chris@0
|
234 // If processPattern returns false, then the pattern that we are
|
Chris@0
|
235 // checking the code with must not be designed to check that code.
|
Chris@0
|
236 $errors = $this->processPattern($patternInfo, $phpcsFile, $stackPtr);
|
Chris@0
|
237 if ($errors === false) {
|
Chris@0
|
238 // The pattern didn't match.
|
Chris@0
|
239 continue;
|
Chris@0
|
240 } else if (empty($errors) === true) {
|
Chris@0
|
241 // The pattern matched, but there were no errors.
|
Chris@0
|
242 break;
|
Chris@0
|
243 }
|
Chris@0
|
244
|
Chris@0
|
245 foreach ($errors as $stackPtr => $error) {
|
Chris@0
|
246 if (isset($this->_errorPos[$stackPtr]) === false) {
|
Chris@0
|
247 $this->_errorPos[$stackPtr] = true;
|
Chris@0
|
248 $allErrors[$stackPtr] = $error;
|
Chris@0
|
249 }
|
Chris@0
|
250 }
|
Chris@0
|
251 }
|
Chris@0
|
252
|
Chris@0
|
253 foreach ($allErrors as $stackPtr => $error) {
|
Chris@0
|
254 $phpcsFile->addError($error, $stackPtr);
|
Chris@0
|
255 }
|
Chris@0
|
256
|
Chris@0
|
257 }//end process()
|
Chris@0
|
258
|
Chris@0
|
259
|
Chris@0
|
260 /**
|
Chris@0
|
261 * Processes the pattern and verifies the code at $stackPtr.
|
Chris@0
|
262 *
|
Chris@0
|
263 * @param array $patternInfo Information about the pattern used
|
Chris@0
|
264 * for checking, which includes are
|
Chris@0
|
265 * parsed token representation of the
|
Chris@0
|
266 * pattern.
|
Chris@0
|
267 * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where the
|
Chris@0
|
268 * token occurred.
|
Chris@0
|
269 * @param int $stackPtr The position in the tokens stack where
|
Chris@0
|
270 * the listening token type was found.
|
Chris@0
|
271 *
|
Chris@0
|
272 * @return array
|
Chris@0
|
273 */
|
Chris@0
|
274 protected function processPattern(
|
Chris@0
|
275 $patternInfo,
|
Chris@0
|
276 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
277 $stackPtr
|
Chris@0
|
278 ) {
|
Chris@0
|
279 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
280 $pattern = $patternInfo['pattern'];
|
Chris@0
|
281 $patternCode = $patternInfo['pattern_code'];
|
Chris@0
|
282 $errors = array();
|
Chris@0
|
283 $found = '';
|
Chris@0
|
284
|
Chris@0
|
285 $ignoreTokens = array(T_WHITESPACE);
|
Chris@0
|
286 if ($this->ignoreComments === true) {
|
Chris@0
|
287 $ignoreTokens
|
Chris@0
|
288 = array_merge($ignoreTokens, PHP_CodeSniffer_Tokens::$commentTokens);
|
Chris@0
|
289 }
|
Chris@0
|
290
|
Chris@0
|
291 $origStackPtr = $stackPtr;
|
Chris@0
|
292 $hasError = false;
|
Chris@0
|
293
|
Chris@0
|
294 if ($patternInfo['listen_pos'] > 0) {
|
Chris@0
|
295 $stackPtr--;
|
Chris@0
|
296
|
Chris@0
|
297 for ($i = ($patternInfo['listen_pos'] - 1); $i >= 0; $i--) {
|
Chris@0
|
298 if ($pattern[$i]['type'] === 'token') {
|
Chris@0
|
299 if ($pattern[$i]['token'] === T_WHITESPACE) {
|
Chris@0
|
300 if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
|
Chris@0
|
301 $found = $tokens[$stackPtr]['content'].$found;
|
Chris@0
|
302 }
|
Chris@0
|
303
|
Chris@0
|
304 // Only check the size of the whitespace if this is not
|
Chris@0
|
305 // the first token. We don't care about the size of
|
Chris@0
|
306 // leading whitespace, just that there is some.
|
Chris@0
|
307 if ($i !== 0) {
|
Chris@0
|
308 if ($tokens[$stackPtr]['content'] !== $pattern[$i]['value']) {
|
Chris@0
|
309 $hasError = true;
|
Chris@0
|
310 }
|
Chris@0
|
311 }
|
Chris@0
|
312 } else {
|
Chris@0
|
313 // Check to see if this important token is the same as the
|
Chris@0
|
314 // previous important token in the pattern. If it is not,
|
Chris@0
|
315 // then the pattern cannot be for this piece of code.
|
Chris@0
|
316 $prev = $phpcsFile->findPrevious(
|
Chris@0
|
317 $ignoreTokens,
|
Chris@0
|
318 $stackPtr,
|
Chris@0
|
319 null,
|
Chris@0
|
320 true
|
Chris@0
|
321 );
|
Chris@0
|
322
|
Chris@0
|
323 if ($prev === false
|
Chris@0
|
324 || $tokens[$prev]['code'] !== $pattern[$i]['token']
|
Chris@0
|
325 ) {
|
Chris@0
|
326 return false;
|
Chris@0
|
327 }
|
Chris@0
|
328
|
Chris@0
|
329 // If we skipped past some whitespace tokens, then add them
|
Chris@0
|
330 // to the found string.
|
Chris@0
|
331 $tokenContent = $phpcsFile->getTokensAsString(
|
Chris@0
|
332 ($prev + 1),
|
Chris@0
|
333 ($stackPtr - $prev - 1)
|
Chris@0
|
334 );
|
Chris@0
|
335
|
Chris@0
|
336 $found = $tokens[$prev]['content'].$tokenContent.$found;
|
Chris@0
|
337
|
Chris@0
|
338 if (isset($pattern[($i - 1)]) === true
|
Chris@0
|
339 && $pattern[($i - 1)]['type'] === 'skip'
|
Chris@0
|
340 ) {
|
Chris@0
|
341 $stackPtr = $prev;
|
Chris@0
|
342 } else {
|
Chris@0
|
343 $stackPtr = ($prev - 1);
|
Chris@0
|
344 }
|
Chris@0
|
345 }//end if
|
Chris@0
|
346 } else if ($pattern[$i]['type'] === 'skip') {
|
Chris@0
|
347 // Skip to next piece of relevant code.
|
Chris@0
|
348 if ($pattern[$i]['to'] === 'parenthesis_closer') {
|
Chris@0
|
349 $to = 'parenthesis_opener';
|
Chris@0
|
350 } else {
|
Chris@0
|
351 $to = 'scope_opener';
|
Chris@0
|
352 }
|
Chris@0
|
353
|
Chris@0
|
354 // Find the previous opener.
|
Chris@0
|
355 $next = $phpcsFile->findPrevious(
|
Chris@0
|
356 $ignoreTokens,
|
Chris@0
|
357 $stackPtr,
|
Chris@0
|
358 null,
|
Chris@0
|
359 true
|
Chris@0
|
360 );
|
Chris@0
|
361
|
Chris@0
|
362 if ($next === false || isset($tokens[$next][$to]) === false) {
|
Chris@0
|
363 // If there was not opener, then we must be
|
Chris@0
|
364 // using the wrong pattern.
|
Chris@0
|
365 return false;
|
Chris@0
|
366 }
|
Chris@0
|
367
|
Chris@0
|
368 if ($to === 'parenthesis_opener') {
|
Chris@0
|
369 $found = '{'.$found;
|
Chris@0
|
370 } else {
|
Chris@0
|
371 $found = '('.$found;
|
Chris@0
|
372 }
|
Chris@0
|
373
|
Chris@0
|
374 $found = '...'.$found;
|
Chris@0
|
375
|
Chris@0
|
376 // Skip to the opening token.
|
Chris@0
|
377 $stackPtr = ($tokens[$next][$to] - 1);
|
Chris@0
|
378 } else if ($pattern[$i]['type'] === 'string') {
|
Chris@0
|
379 $found = 'abc';
|
Chris@0
|
380 } else if ($pattern[$i]['type'] === 'newline') {
|
Chris@0
|
381 if ($this->ignoreComments === true
|
Chris@0
|
382 && isset(PHP_CodeSniffer_Tokens::$commentTokens[$tokens[$stackPtr]['code']]) === true
|
Chris@0
|
383 ) {
|
Chris@0
|
384 $startComment = $phpcsFile->findPrevious(
|
Chris@0
|
385 PHP_CodeSniffer_Tokens::$commentTokens,
|
Chris@0
|
386 ($stackPtr - 1),
|
Chris@0
|
387 null,
|
Chris@0
|
388 true
|
Chris@0
|
389 );
|
Chris@0
|
390
|
Chris@0
|
391 if ($tokens[$startComment]['line'] !== $tokens[($startComment + 1)]['line']) {
|
Chris@0
|
392 $startComment++;
|
Chris@0
|
393 }
|
Chris@0
|
394
|
Chris@0
|
395 $tokenContent = $phpcsFile->getTokensAsString(
|
Chris@0
|
396 $startComment,
|
Chris@0
|
397 ($stackPtr - $startComment + 1)
|
Chris@0
|
398 );
|
Chris@0
|
399
|
Chris@0
|
400 $found = $tokenContent.$found;
|
Chris@0
|
401 $stackPtr = ($startComment - 1);
|
Chris@0
|
402 }
|
Chris@0
|
403
|
Chris@0
|
404 if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
|
Chris@0
|
405 if ($tokens[$stackPtr]['content'] !== $phpcsFile->eolChar) {
|
Chris@0
|
406 $found = $tokens[$stackPtr]['content'].$found;
|
Chris@0
|
407
|
Chris@0
|
408 // This may just be an indent that comes after a newline
|
Chris@0
|
409 // so check the token before to make sure. If it is a newline, we
|
Chris@0
|
410 // can ignore the error here.
|
Chris@0
|
411 if (($tokens[($stackPtr - 1)]['content'] !== $phpcsFile->eolChar)
|
Chris@0
|
412 && ($this->ignoreComments === true
|
Chris@0
|
413 && isset(PHP_CodeSniffer_Tokens::$commentTokens[$tokens[($stackPtr - 1)]['code']]) === false)
|
Chris@0
|
414 ) {
|
Chris@0
|
415 $hasError = true;
|
Chris@0
|
416 } else {
|
Chris@0
|
417 $stackPtr--;
|
Chris@0
|
418 }
|
Chris@0
|
419 } else {
|
Chris@0
|
420 $found = 'EOL'.$found;
|
Chris@0
|
421 }
|
Chris@0
|
422 } else {
|
Chris@0
|
423 $found = $tokens[$stackPtr]['content'].$found;
|
Chris@0
|
424 $hasError = true;
|
Chris@0
|
425 }//end if
|
Chris@0
|
426
|
Chris@0
|
427 if ($hasError === false && $pattern[($i - 1)]['type'] !== 'newline') {
|
Chris@0
|
428 // Make sure they only have 1 newline.
|
Chris@0
|
429 $prev = $phpcsFile->findPrevious($ignoreTokens, ($stackPtr - 1), null, true);
|
Chris@0
|
430 if ($prev !== false && $tokens[$prev]['line'] !== $tokens[$stackPtr]['line']) {
|
Chris@0
|
431 $hasError = true;
|
Chris@0
|
432 }
|
Chris@0
|
433 }
|
Chris@0
|
434 }//end if
|
Chris@0
|
435 }//end for
|
Chris@0
|
436 }//end if
|
Chris@0
|
437
|
Chris@0
|
438 $stackPtr = $origStackPtr;
|
Chris@0
|
439 $lastAddedStackPtr = null;
|
Chris@0
|
440 $patternLen = count($pattern);
|
Chris@0
|
441
|
Chris@0
|
442 for ($i = $patternInfo['listen_pos']; $i < $patternLen; $i++) {
|
Chris@0
|
443 if (isset($tokens[$stackPtr]) === false) {
|
Chris@0
|
444 break;
|
Chris@0
|
445 }
|
Chris@0
|
446
|
Chris@0
|
447 if ($pattern[$i]['type'] === 'token') {
|
Chris@0
|
448 if ($pattern[$i]['token'] === T_WHITESPACE) {
|
Chris@0
|
449 if ($this->ignoreComments === true) {
|
Chris@0
|
450 // If we are ignoring comments, check to see if this current
|
Chris@0
|
451 // token is a comment. If so skip it.
|
Chris@0
|
452 if (isset(PHP_CodeSniffer_Tokens::$commentTokens[$tokens[$stackPtr]['code']]) === true) {
|
Chris@0
|
453 continue;
|
Chris@0
|
454 }
|
Chris@0
|
455
|
Chris@0
|
456 // If the next token is a comment, the we need to skip the
|
Chris@0
|
457 // current token as we should allow a space before a
|
Chris@0
|
458 // comment for readability.
|
Chris@0
|
459 if (isset($tokens[($stackPtr + 1)]) === true
|
Chris@0
|
460 && isset(PHP_CodeSniffer_Tokens::$commentTokens[$tokens[($stackPtr + 1)]['code']]) === true
|
Chris@0
|
461 ) {
|
Chris@0
|
462 continue;
|
Chris@0
|
463 }
|
Chris@0
|
464 }
|
Chris@0
|
465
|
Chris@0
|
466 $tokenContent = '';
|
Chris@0
|
467 if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
|
Chris@0
|
468 if (isset($pattern[($i + 1)]) === false) {
|
Chris@0
|
469 // This is the last token in the pattern, so just compare
|
Chris@0
|
470 // the next token of content.
|
Chris@0
|
471 $tokenContent = $tokens[$stackPtr]['content'];
|
Chris@0
|
472 } else {
|
Chris@0
|
473 // Get all the whitespace to the next token.
|
Chris@0
|
474 $next = $phpcsFile->findNext(
|
Chris@0
|
475 PHP_CodeSniffer_Tokens::$emptyTokens,
|
Chris@0
|
476 $stackPtr,
|
Chris@0
|
477 null,
|
Chris@0
|
478 true
|
Chris@0
|
479 );
|
Chris@0
|
480
|
Chris@0
|
481 $tokenContent = $phpcsFile->getTokensAsString(
|
Chris@0
|
482 $stackPtr,
|
Chris@0
|
483 ($next - $stackPtr)
|
Chris@0
|
484 );
|
Chris@0
|
485
|
Chris@0
|
486 $lastAddedStackPtr = $stackPtr;
|
Chris@0
|
487 $stackPtr = $next;
|
Chris@0
|
488 }//end if
|
Chris@0
|
489
|
Chris@0
|
490 if ($stackPtr !== $lastAddedStackPtr) {
|
Chris@0
|
491 $found .= $tokenContent;
|
Chris@0
|
492 }
|
Chris@0
|
493 } else {
|
Chris@0
|
494 if ($stackPtr !== $lastAddedStackPtr) {
|
Chris@0
|
495 $found .= $tokens[$stackPtr]['content'];
|
Chris@0
|
496 $lastAddedStackPtr = $stackPtr;
|
Chris@0
|
497 }
|
Chris@0
|
498 }//end if
|
Chris@0
|
499
|
Chris@0
|
500 if (isset($pattern[($i + 1)]) === true
|
Chris@0
|
501 && $pattern[($i + 1)]['type'] === 'skip'
|
Chris@0
|
502 ) {
|
Chris@0
|
503 // The next token is a skip token, so we just need to make
|
Chris@0
|
504 // sure the whitespace we found has *at least* the
|
Chris@0
|
505 // whitespace required.
|
Chris@0
|
506 if (strpos($tokenContent, $pattern[$i]['value']) !== 0) {
|
Chris@0
|
507 $hasError = true;
|
Chris@0
|
508 }
|
Chris@0
|
509 } else {
|
Chris@0
|
510 if ($tokenContent !== $pattern[$i]['value']) {
|
Chris@0
|
511 $hasError = true;
|
Chris@0
|
512 }
|
Chris@0
|
513 }
|
Chris@0
|
514 } else {
|
Chris@0
|
515 // Check to see if this important token is the same as the
|
Chris@0
|
516 // next important token in the pattern. If it is not, then
|
Chris@0
|
517 // the pattern cannot be for this piece of code.
|
Chris@0
|
518 $next = $phpcsFile->findNext(
|
Chris@0
|
519 $ignoreTokens,
|
Chris@0
|
520 $stackPtr,
|
Chris@0
|
521 null,
|
Chris@0
|
522 true
|
Chris@0
|
523 );
|
Chris@0
|
524
|
Chris@0
|
525 if ($next === false
|
Chris@0
|
526 || $tokens[$next]['code'] !== $pattern[$i]['token']
|
Chris@0
|
527 ) {
|
Chris@0
|
528 // The next important token did not match the pattern.
|
Chris@0
|
529 return false;
|
Chris@0
|
530 }
|
Chris@0
|
531
|
Chris@0
|
532 if ($lastAddedStackPtr !== null) {
|
Chris@0
|
533 if (($tokens[$next]['code'] === T_OPEN_CURLY_BRACKET
|
Chris@0
|
534 || $tokens[$next]['code'] === T_CLOSE_CURLY_BRACKET)
|
Chris@0
|
535 && isset($tokens[$next]['scope_condition']) === true
|
Chris@0
|
536 && $tokens[$next]['scope_condition'] > $lastAddedStackPtr
|
Chris@0
|
537 ) {
|
Chris@0
|
538 // This is a brace, but the owner of it is after the current
|
Chris@0
|
539 // token, which means it does not belong to any token in
|
Chris@0
|
540 // our pattern. This means the pattern is not for us.
|
Chris@0
|
541 return false;
|
Chris@0
|
542 }
|
Chris@0
|
543
|
Chris@0
|
544 if (($tokens[$next]['code'] === T_OPEN_PARENTHESIS
|
Chris@0
|
545 || $tokens[$next]['code'] === T_CLOSE_PARENTHESIS)
|
Chris@0
|
546 && isset($tokens[$next]['parenthesis_owner']) === true
|
Chris@0
|
547 && $tokens[$next]['parenthesis_owner'] > $lastAddedStackPtr
|
Chris@0
|
548 ) {
|
Chris@0
|
549 // This is a bracket, but the owner of it is after the current
|
Chris@0
|
550 // token, which means it does not belong to any token in
|
Chris@0
|
551 // our pattern. This means the pattern is not for us.
|
Chris@0
|
552 return false;
|
Chris@0
|
553 }
|
Chris@0
|
554 }//end if
|
Chris@0
|
555
|
Chris@0
|
556 // If we skipped past some whitespace tokens, then add them
|
Chris@0
|
557 // to the found string.
|
Chris@0
|
558 if (($next - $stackPtr) > 0) {
|
Chris@0
|
559 $hasComment = false;
|
Chris@0
|
560 for ($j = $stackPtr; $j < $next; $j++) {
|
Chris@0
|
561 $found .= $tokens[$j]['content'];
|
Chris@0
|
562 if (isset(PHP_CodeSniffer_Tokens::$commentTokens[$tokens[$j]['code']]) === true) {
|
Chris@0
|
563 $hasComment = true;
|
Chris@0
|
564 }
|
Chris@0
|
565 }
|
Chris@0
|
566
|
Chris@0
|
567 // If we are not ignoring comments, this additional
|
Chris@0
|
568 // whitespace or comment is not allowed. If we are
|
Chris@0
|
569 // ignoring comments, there needs to be at least one
|
Chris@0
|
570 // comment for this to be allowed.
|
Chris@0
|
571 if ($this->ignoreComments === false
|
Chris@0
|
572 || ($this->ignoreComments === true
|
Chris@0
|
573 && $hasComment === false)
|
Chris@0
|
574 ) {
|
Chris@0
|
575 $hasError = true;
|
Chris@0
|
576 }
|
Chris@0
|
577
|
Chris@0
|
578 // Even when ignoring comments, we are not allowed to include
|
Chris@0
|
579 // newlines without the pattern specifying them, so
|
Chris@0
|
580 // everything should be on the same line.
|
Chris@0
|
581 if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
|
Chris@0
|
582 $hasError = true;
|
Chris@0
|
583 }
|
Chris@0
|
584 }//end if
|
Chris@0
|
585
|
Chris@0
|
586 if ($next !== $lastAddedStackPtr) {
|
Chris@0
|
587 $found .= $tokens[$next]['content'];
|
Chris@0
|
588 $lastAddedStackPtr = $next;
|
Chris@0
|
589 }
|
Chris@0
|
590
|
Chris@0
|
591 if (isset($pattern[($i + 1)]) === true
|
Chris@0
|
592 && $pattern[($i + 1)]['type'] === 'skip'
|
Chris@0
|
593 ) {
|
Chris@0
|
594 $stackPtr = $next;
|
Chris@0
|
595 } else {
|
Chris@0
|
596 $stackPtr = ($next + 1);
|
Chris@0
|
597 }
|
Chris@0
|
598 }//end if
|
Chris@0
|
599 } else if ($pattern[$i]['type'] === 'skip') {
|
Chris@0
|
600 if ($pattern[$i]['to'] === 'unknown') {
|
Chris@0
|
601 $next = $phpcsFile->findNext(
|
Chris@0
|
602 $pattern[($i + 1)]['token'],
|
Chris@0
|
603 $stackPtr
|
Chris@0
|
604 );
|
Chris@0
|
605
|
Chris@0
|
606 if ($next === false) {
|
Chris@0
|
607 // Couldn't find the next token, so we must
|
Chris@0
|
608 // be using the wrong pattern.
|
Chris@0
|
609 return false;
|
Chris@0
|
610 }
|
Chris@0
|
611
|
Chris@0
|
612 $found .= '...';
|
Chris@0
|
613 $stackPtr = $next;
|
Chris@0
|
614 } else {
|
Chris@0
|
615 // Find the previous opener.
|
Chris@0
|
616 $next = $phpcsFile->findPrevious(
|
Chris@0
|
617 PHP_CodeSniffer_Tokens::$blockOpeners,
|
Chris@0
|
618 $stackPtr
|
Chris@0
|
619 );
|
Chris@0
|
620
|
Chris@0
|
621 if ($next === false
|
Chris@0
|
622 || isset($tokens[$next][$pattern[$i]['to']]) === false
|
Chris@0
|
623 ) {
|
Chris@0
|
624 // If there was not opener, then we must
|
Chris@0
|
625 // be using the wrong pattern.
|
Chris@0
|
626 return false;
|
Chris@0
|
627 }
|
Chris@0
|
628
|
Chris@0
|
629 $found .= '...';
|
Chris@0
|
630 if ($pattern[$i]['to'] === 'parenthesis_closer') {
|
Chris@0
|
631 $found .= ')';
|
Chris@0
|
632 } else {
|
Chris@0
|
633 $found .= '}';
|
Chris@0
|
634 }
|
Chris@0
|
635
|
Chris@0
|
636 // Skip to the closing token.
|
Chris@0
|
637 $stackPtr = ($tokens[$next][$pattern[$i]['to']] + 1);
|
Chris@0
|
638 }//end if
|
Chris@0
|
639 } else if ($pattern[$i]['type'] === 'string') {
|
Chris@0
|
640 if ($tokens[$stackPtr]['code'] !== T_STRING) {
|
Chris@0
|
641 $hasError = true;
|
Chris@0
|
642 }
|
Chris@0
|
643
|
Chris@0
|
644 if ($stackPtr !== $lastAddedStackPtr) {
|
Chris@0
|
645 $found .= 'abc';
|
Chris@0
|
646 $lastAddedStackPtr = $stackPtr;
|
Chris@0
|
647 }
|
Chris@0
|
648
|
Chris@0
|
649 $stackPtr++;
|
Chris@0
|
650 } else if ($pattern[$i]['type'] === 'newline') {
|
Chris@0
|
651 // Find the next token that contains a newline character.
|
Chris@0
|
652 $newline = 0;
|
Chris@0
|
653 for ($j = $stackPtr; $j < $phpcsFile->numTokens; $j++) {
|
Chris@0
|
654 if (strpos($tokens[$j]['content'], $phpcsFile->eolChar) !== false) {
|
Chris@0
|
655 $newline = $j;
|
Chris@0
|
656 break;
|
Chris@0
|
657 }
|
Chris@0
|
658 }
|
Chris@0
|
659
|
Chris@0
|
660 if ($newline === 0) {
|
Chris@0
|
661 // We didn't find a newline character in the rest of the file.
|
Chris@0
|
662 $next = ($phpcsFile->numTokens - 1);
|
Chris@0
|
663 $hasError = true;
|
Chris@0
|
664 } else {
|
Chris@0
|
665 if ($this->ignoreComments === false) {
|
Chris@0
|
666 // The newline character cannot be part of a comment.
|
Chris@0
|
667 if (isset(PHP_CodeSniffer_Tokens::$commentTokens[$tokens[$newline]['code']]) === true) {
|
Chris@0
|
668 $hasError = true;
|
Chris@0
|
669 }
|
Chris@0
|
670 }
|
Chris@0
|
671
|
Chris@0
|
672 if ($newline === $stackPtr) {
|
Chris@0
|
673 $next = ($stackPtr + 1);
|
Chris@0
|
674 } else {
|
Chris@0
|
675 // Check that there were no significant tokens that we
|
Chris@0
|
676 // skipped over to find our newline character.
|
Chris@0
|
677 $next = $phpcsFile->findNext(
|
Chris@0
|
678 $ignoreTokens,
|
Chris@0
|
679 $stackPtr,
|
Chris@0
|
680 null,
|
Chris@0
|
681 true
|
Chris@0
|
682 );
|
Chris@0
|
683
|
Chris@0
|
684 if ($next < $newline) {
|
Chris@0
|
685 // We skipped a non-ignored token.
|
Chris@0
|
686 $hasError = true;
|
Chris@0
|
687 } else {
|
Chris@0
|
688 $next = ($newline + 1);
|
Chris@0
|
689 }
|
Chris@0
|
690 }
|
Chris@0
|
691 }//end if
|
Chris@0
|
692
|
Chris@0
|
693 if ($stackPtr !== $lastAddedStackPtr) {
|
Chris@0
|
694 $found .= $phpcsFile->getTokensAsString(
|
Chris@0
|
695 $stackPtr,
|
Chris@0
|
696 ($next - $stackPtr)
|
Chris@0
|
697 );
|
Chris@0
|
698
|
Chris@0
|
699 $diff = ($next - $stackPtr);
|
Chris@0
|
700 $lastAddedStackPtr = ($next - 1);
|
Chris@0
|
701 }
|
Chris@0
|
702
|
Chris@0
|
703 $stackPtr = $next;
|
Chris@0
|
704 }//end if
|
Chris@0
|
705 }//end for
|
Chris@0
|
706
|
Chris@0
|
707 if ($hasError === true) {
|
Chris@0
|
708 $error = $this->prepareError($found, $patternCode);
|
Chris@0
|
709 $errors[$origStackPtr] = $error;
|
Chris@0
|
710 }
|
Chris@0
|
711
|
Chris@0
|
712 return $errors;
|
Chris@0
|
713
|
Chris@0
|
714 }//end processPattern()
|
Chris@0
|
715
|
Chris@0
|
716
|
Chris@0
|
717 /**
|
Chris@0
|
718 * Prepares an error for the specified patternCode.
|
Chris@0
|
719 *
|
Chris@0
|
720 * @param string $found The actual found string in the code.
|
Chris@0
|
721 * @param string $patternCode The expected pattern code.
|
Chris@0
|
722 *
|
Chris@0
|
723 * @return string The error message.
|
Chris@0
|
724 */
|
Chris@0
|
725 protected function prepareError($found, $patternCode)
|
Chris@0
|
726 {
|
Chris@0
|
727 $found = str_replace("\r\n", '\n', $found);
|
Chris@0
|
728 $found = str_replace("\n", '\n', $found);
|
Chris@0
|
729 $found = str_replace("\r", '\n', $found);
|
Chris@0
|
730 $found = str_replace("\t", '\t', $found);
|
Chris@0
|
731 $found = str_replace('EOL', '\n', $found);
|
Chris@0
|
732 $expected = str_replace('EOL', '\n', $patternCode);
|
Chris@0
|
733
|
Chris@0
|
734 $error = "Expected \"$expected\"; found \"$found\"";
|
Chris@0
|
735
|
Chris@0
|
736 return $error;
|
Chris@0
|
737
|
Chris@0
|
738 }//end prepareError()
|
Chris@0
|
739
|
Chris@0
|
740
|
Chris@0
|
741 /**
|
Chris@0
|
742 * Returns the patterns that should be checked.
|
Chris@0
|
743 *
|
Chris@0
|
744 * @return string[]
|
Chris@0
|
745 */
|
Chris@0
|
746 protected abstract function getPatterns();
|
Chris@0
|
747
|
Chris@0
|
748
|
Chris@0
|
749 /**
|
Chris@0
|
750 * Registers any supplementary tokens that this test might wish to process.
|
Chris@0
|
751 *
|
Chris@0
|
752 * A sniff may wish to register supplementary tests when it wishes to group
|
Chris@0
|
753 * an arbitrary validation that cannot be performed using a pattern, with
|
Chris@0
|
754 * other pattern tests.
|
Chris@0
|
755 *
|
Chris@0
|
756 * @return int[]
|
Chris@0
|
757 * @see processSupplementary()
|
Chris@0
|
758 */
|
Chris@0
|
759 protected function registerSupplementary()
|
Chris@0
|
760 {
|
Chris@0
|
761 return array();
|
Chris@0
|
762
|
Chris@0
|
763 }//end registerSupplementary()
|
Chris@0
|
764
|
Chris@0
|
765
|
Chris@0
|
766 /**
|
Chris@0
|
767 * Processes any tokens registered with registerSupplementary().
|
Chris@0
|
768 *
|
Chris@0
|
769 * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where to
|
Chris@0
|
770 * process the skip.
|
Chris@0
|
771 * @param int $stackPtr The position in the tokens stack to
|
Chris@0
|
772 * process.
|
Chris@0
|
773 *
|
Chris@0
|
774 * @return void
|
Chris@0
|
775 * @see registerSupplementary()
|
Chris@0
|
776 */
|
Chris@0
|
777 protected function processSupplementary(
|
Chris@0
|
778 PHP_CodeSniffer_File $phpcsFile,
|
Chris@0
|
779 $stackPtr
|
Chris@0
|
780 ) {
|
Chris@0
|
781
|
Chris@0
|
782 }//end processSupplementary()
|
Chris@0
|
783
|
Chris@0
|
784
|
Chris@0
|
785 /**
|
Chris@0
|
786 * Parses a pattern string into an array of pattern steps.
|
Chris@0
|
787 *
|
Chris@0
|
788 * @param string $pattern The pattern to parse.
|
Chris@0
|
789 *
|
Chris@0
|
790 * @return array The parsed pattern array.
|
Chris@0
|
791 * @see _createSkipPattern()
|
Chris@0
|
792 * @see _createTokenPattern()
|
Chris@0
|
793 */
|
Chris@0
|
794 private function _parse($pattern)
|
Chris@0
|
795 {
|
Chris@0
|
796 $patterns = array();
|
Chris@0
|
797 $length = strlen($pattern);
|
Chris@0
|
798 $lastToken = 0;
|
Chris@0
|
799 $firstToken = 0;
|
Chris@0
|
800
|
Chris@0
|
801 for ($i = 0; $i < $length; $i++) {
|
Chris@0
|
802 $specialPattern = false;
|
Chris@0
|
803 $isLastChar = ($i === ($length - 1));
|
Chris@0
|
804 $oldFirstToken = $firstToken;
|
Chris@0
|
805
|
Chris@0
|
806 if (substr($pattern, $i, 3) === '...') {
|
Chris@0
|
807 // It's a skip pattern. The skip pattern requires the
|
Chris@0
|
808 // content of the token in the "from" position and the token
|
Chris@0
|
809 // to skip to.
|
Chris@0
|
810 $specialPattern = $this->_createSkipPattern($pattern, ($i - 1));
|
Chris@0
|
811 $lastToken = ($i - $firstToken);
|
Chris@0
|
812 $firstToken = ($i + 3);
|
Chris@0
|
813 $i = ($i + 2);
|
Chris@0
|
814
|
Chris@0
|
815 if ($specialPattern['to'] !== 'unknown') {
|
Chris@0
|
816 $firstToken++;
|
Chris@0
|
817 }
|
Chris@0
|
818 } else if (substr($pattern, $i, 3) === 'abc') {
|
Chris@0
|
819 $specialPattern = array('type' => 'string');
|
Chris@0
|
820 $lastToken = ($i - $firstToken);
|
Chris@0
|
821 $firstToken = ($i + 3);
|
Chris@0
|
822 $i = ($i + 2);
|
Chris@0
|
823 } else if (substr($pattern, $i, 3) === 'EOL') {
|
Chris@0
|
824 $specialPattern = array('type' => 'newline');
|
Chris@0
|
825 $lastToken = ($i - $firstToken);
|
Chris@0
|
826 $firstToken = ($i + 3);
|
Chris@0
|
827 $i = ($i + 2);
|
Chris@0
|
828 }//end if
|
Chris@0
|
829
|
Chris@0
|
830 if ($specialPattern !== false || $isLastChar === true) {
|
Chris@0
|
831 // If we are at the end of the string, don't worry about a limit.
|
Chris@0
|
832 if ($isLastChar === true) {
|
Chris@0
|
833 // Get the string from the end of the last skip pattern, if any,
|
Chris@0
|
834 // to the end of the pattern string.
|
Chris@0
|
835 $str = substr($pattern, $oldFirstToken);
|
Chris@0
|
836 } else {
|
Chris@0
|
837 // Get the string from the end of the last special pattern,
|
Chris@0
|
838 // if any, to the start of this special pattern.
|
Chris@0
|
839 if ($lastToken === 0) {
|
Chris@0
|
840 // Note that if the last special token was zero characters ago,
|
Chris@0
|
841 // there will be nothing to process so we can skip this bit.
|
Chris@0
|
842 // This happens if you have something like: EOL... in your pattern.
|
Chris@0
|
843 $str = '';
|
Chris@0
|
844 } else {
|
Chris@0
|
845 $str = substr($pattern, $oldFirstToken, $lastToken);
|
Chris@0
|
846 }
|
Chris@0
|
847 }
|
Chris@0
|
848
|
Chris@0
|
849 if ($str !== '') {
|
Chris@0
|
850 $tokenPatterns = $this->_createTokenPattern($str);
|
Chris@0
|
851 foreach ($tokenPatterns as $tokenPattern) {
|
Chris@0
|
852 $patterns[] = $tokenPattern;
|
Chris@0
|
853 }
|
Chris@0
|
854 }
|
Chris@0
|
855
|
Chris@0
|
856 // Make sure we don't skip the last token.
|
Chris@0
|
857 if ($isLastChar === false && $i === ($length - 1)) {
|
Chris@0
|
858 $i--;
|
Chris@0
|
859 }
|
Chris@0
|
860 }//end if
|
Chris@0
|
861
|
Chris@0
|
862 // Add the skip pattern *after* we have processed
|
Chris@0
|
863 // all the tokens from the end of the last skip pattern
|
Chris@0
|
864 // to the start of this skip pattern.
|
Chris@0
|
865 if ($specialPattern !== false) {
|
Chris@0
|
866 $patterns[] = $specialPattern;
|
Chris@0
|
867 }
|
Chris@0
|
868 }//end for
|
Chris@0
|
869
|
Chris@0
|
870 return $patterns;
|
Chris@0
|
871
|
Chris@0
|
872 }//end _parse()
|
Chris@0
|
873
|
Chris@0
|
874
|
Chris@0
|
875 /**
|
Chris@0
|
876 * Creates a skip pattern.
|
Chris@0
|
877 *
|
Chris@0
|
878 * @param string $pattern The pattern being parsed.
|
Chris@0
|
879 * @param string $from The token content that the skip pattern starts from.
|
Chris@0
|
880 *
|
Chris@0
|
881 * @return array The pattern step.
|
Chris@0
|
882 * @see _createTokenPattern()
|
Chris@0
|
883 * @see _parse()
|
Chris@0
|
884 */
|
Chris@0
|
885 private function _createSkipPattern($pattern, $from)
|
Chris@0
|
886 {
|
Chris@0
|
887 $skip = array('type' => 'skip');
|
Chris@0
|
888
|
Chris@0
|
889 $nestedParenthesis = 0;
|
Chris@0
|
890 $nestedBraces = 0;
|
Chris@0
|
891 for ($start = $from; $start >= 0; $start--) {
|
Chris@0
|
892 switch ($pattern[$start]) {
|
Chris@0
|
893 case '(':
|
Chris@0
|
894 if ($nestedParenthesis === 0) {
|
Chris@0
|
895 $skip['to'] = 'parenthesis_closer';
|
Chris@0
|
896 }
|
Chris@0
|
897
|
Chris@0
|
898 $nestedParenthesis--;
|
Chris@0
|
899 break;
|
Chris@0
|
900 case '{':
|
Chris@0
|
901 if ($nestedBraces === 0) {
|
Chris@0
|
902 $skip['to'] = 'scope_closer';
|
Chris@0
|
903 }
|
Chris@0
|
904
|
Chris@0
|
905 $nestedBraces--;
|
Chris@0
|
906 break;
|
Chris@0
|
907 case '}':
|
Chris@0
|
908 $nestedBraces++;
|
Chris@0
|
909 break;
|
Chris@0
|
910 case ')':
|
Chris@0
|
911 $nestedParenthesis++;
|
Chris@0
|
912 break;
|
Chris@0
|
913 }//end switch
|
Chris@0
|
914
|
Chris@0
|
915 if (isset($skip['to']) === true) {
|
Chris@0
|
916 break;
|
Chris@0
|
917 }
|
Chris@0
|
918 }//end for
|
Chris@0
|
919
|
Chris@0
|
920 if (isset($skip['to']) === false) {
|
Chris@0
|
921 $skip['to'] = 'unknown';
|
Chris@0
|
922 }
|
Chris@0
|
923
|
Chris@0
|
924 return $skip;
|
Chris@0
|
925
|
Chris@0
|
926 }//end _createSkipPattern()
|
Chris@0
|
927
|
Chris@0
|
928
|
Chris@0
|
929 /**
|
Chris@0
|
930 * Creates a token pattern.
|
Chris@0
|
931 *
|
Chris@0
|
932 * @param string $str The tokens string that the pattern should match.
|
Chris@0
|
933 *
|
Chris@0
|
934 * @return array The pattern step.
|
Chris@0
|
935 * @see _createSkipPattern()
|
Chris@0
|
936 * @see _parse()
|
Chris@0
|
937 */
|
Chris@0
|
938 private function _createTokenPattern($str)
|
Chris@0
|
939 {
|
Chris@0
|
940 // Don't add a space after the closing php tag as it will add a new
|
Chris@0
|
941 // whitespace token.
|
Chris@0
|
942 $tokenizer = new PHP_CodeSniffer_Tokenizers_PHP();
|
Chris@0
|
943 $tokens = $tokenizer->tokenizeString('<?php '.$str.'?>');
|
Chris@0
|
944
|
Chris@0
|
945 // Remove the <?php tag from the front and the end php tag from the back.
|
Chris@0
|
946 $tokens = array_slice($tokens, 1, (count($tokens) - 2));
|
Chris@0
|
947
|
Chris@0
|
948 $patterns = array();
|
Chris@0
|
949 foreach ($tokens as $patternInfo) {
|
Chris@0
|
950 $patterns[] = array(
|
Chris@0
|
951 'type' => 'token',
|
Chris@0
|
952 'token' => $patternInfo['code'],
|
Chris@0
|
953 'value' => $patternInfo['content'],
|
Chris@0
|
954 );
|
Chris@0
|
955 }
|
Chris@0
|
956
|
Chris@0
|
957 return $patterns;
|
Chris@0
|
958
|
Chris@0
|
959 }//end _createTokenPattern()
|
Chris@0
|
960
|
Chris@0
|
961
|
Chris@0
|
962 }//end class
|