PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
← All changes | src/extra/php-codesniffer-php-token/Tokenizers/Tokenizer.php +1422 -1431 3.1.0 → 6.3.0 View file →
@@ -11,1672 +11,1663 @@
11 11
12 12 use PHP_CodeSniffer\Exceptions\TokenizerException;
13 13 use PHP_CodeSniffer\Util;
14 14
15 -abstract class Tokenizer
16 -{
15 +abstract class Tokenizer {
17 16
18 - /**
19 - * The config data for the run.
20 - *
21 - * @var \PHP_CodeSniffer\Config
22 - */
23 - protected $config = null;
17 + /**
18 + * The config data for the run.
19 + *
20 + * @var \PHP_CodeSniffer\Config
21 + */
22 + protected $config = null;
24 23
25 - /**
26 - * The EOL char used in the content.
27 - *
28 - * @var string
29 - */
30 - protected $eolChar = [];
24 + /**
25 + * The EOL char used in the content.
26 + *
27 + * @var string
28 + */
29 + protected $eolChar = [];
31 30
32 - /**
33 - * A token-based representation of the content.
34 - *
35 - * @var array
36 - */
37 - protected $tokens = [];
31 + /**
32 + * A token-based representation of the content.
33 + *
34 + * @var array
35 + */
36 + protected $tokens = [];
38 37
39 - /**
40 - * The number of tokens in the tokens array.
41 - *
42 - * @var integer
43 - */
44 - protected $numTokens = 0;
38 + /**
39 + * The number of tokens in the tokens array.
40 + *
41 + * @var integer
42 + */
43 + protected $numTokens = 0;
45 44
46 - /**
47 - * A list of tokens that are allowed to open a scope.
48 - *
49 - * @var array
50 - */
51 - public $scopeOpeners = [];
45 + /**
46 + * A list of tokens that are allowed to open a scope.
47 + *
48 + * @var array
49 + */
50 + public $scopeOpeners = [];
52 51
53 - /**
54 - * A list of tokens that end the scope.
55 - *
56 - * @var array
57 - */
58 - public $endScopeTokens = [];
52 + /**
53 + * A list of tokens that end the scope.
54 + *
55 + * @var array
56 + */
57 + public $endScopeTokens = [];
59 58
60 - /**
61 - * Known lengths of tokens.
62 - *
63 - * @var array<int, int>
64 - */
65 - public $knownLengths = [];
59 + /**
60 + * Known lengths of tokens.
61 + *
62 + * @var array<int, int>
63 + */
64 + public $knownLengths = [];
66 65
67 - /**
68 - * A list of lines being ignored due to error suppression comments.
69 - *
70 - * @var array
71 - */
72 - public $ignoredLines = [];
66 + /**
67 + * A list of lines being ignored due to error suppression comments.
68 + *
69 + * @var array
70 + */
71 + public $ignoredLines = [];
73 72
74 73
75 - /**
76 - * Initialise and run the tokenizer.
77 - *
78 - * @param string $content The content to tokenize,
79 - * @param \PHP_CodeSniffer\Config | null $config The config data for the run.
80 - * @param string $eolChar The EOL char used in the content.
81 - *
82 - * @return void
83 - * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the file appears to be minified.
84 - */
85 - public function __construct($content, $config, $eolChar='\n')
86 - {
87 - $this->eolChar = $eolChar;
74 + /**
75 + * Initialise and run the tokenizer.
76 + *
77 + * @param string $content The content to tokenize,
78 + * @param \PHP_CodeSniffer\Config | null $config The config data for the run.
79 + * @param string $eolChar The EOL char used in the content.
80 + *
81 + * @return void
82 + * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the file appears to be minified.
83 + */
84 + public function __construct( $content, $config, $eolChar = '\n' ) {
85 + $this->eolChar = $eolChar;
88 86
89 - $this->config = $config;
90 - $this->tokens = $this->tokenize($content);
87 + $this->config = $config;
88 + $this->tokens = $this->tokenize( $content );
91 89
92 - if ($config === null) {
93 - return;
94 - }
90 + if ( $config === null ) {
91 + return;
92 + }
95 93
96 - $this->createPositionMap();
97 - $this->createTokenMap();
98 - $this->createParenthesisNestingMap();
99 - $this->createScopeMap();
100 - $this->createLevelMap();
94 + $this->createPositionMap();
95 + $this->createTokenMap();
96 + $this->createParenthesisNestingMap();
97 + $this->createScopeMap();
98 + $this->createLevelMap();
101 99
102 - // Allow the tokenizer to do additional processing if required.
103 - $this->processAdditional();
100 + // Allow the tokenizer to do additional processing if required.
101 + $this->processAdditional();
104 102
105 - }//end __construct()
103 + }//end __construct()
106 104
107 105
108 - /**
109 - * Checks the content to see if it looks minified.
110 - *
111 - * @param string $content The content to tokenize.
112 - * @param string $eolChar The EOL char used in the content.
113 - *
114 - * @return boolean
115 - */
116 - protected function isMinifiedContent($content, $eolChar='\n')
117 - {
118 - // Minified files often have a very large number of characters per line
119 - // and cause issues when tokenizing.
120 - $numChars = strlen($content);
121 - $numLines = (substr_count($content, $eolChar) + 1);
122 - $average = ($numChars / $numLines);
123 - if ($average > 100) {
124 - return true;
125 - }
106 + /**
107 + * Checks the content to see if it looks minified.
108 + *
109 + * @param string $content The content to tokenize.
110 + * @param string $eolChar The EOL char used in the content.
111 + *
112 + * @return boolean
113 + */
114 + protected function isMinifiedContent( $content, $eolChar = '\n' ) {
115 + // Minified files often have a very large number of characters per line
116 + // and cause issues when tokenizing.
117 + $numChars = strlen( $content );
118 + $numLines = ( substr_count( $content, $eolChar ) + 1 );
119 + $average = ( $numChars / $numLines );
120 + if ( $average > 100 ) {
121 + return true;
122 + }
126 123
127 - return false;
124 + return false;
128 125
129 - }//end isMinifiedContent()
126 + }//end isMinifiedContent()
130 127
131 128
132 - /**
133 - * Gets the array of tokens.
134 - *
135 - * @return array
136 - */
137 - public function getTokens()
138 - {
139 - return $this->tokens;
129 + /**
130 + * Gets the array of tokens.
131 + *
132 + * @return array
133 + */
134 + public function getTokens() {
135 + return $this->tokens;
140 136
141 - }//end getTokens()
137 + }//end getTokens()
142 138
143 139
144 - /**
145 - * Creates an array of tokens when given some content.
146 - *
147 - * @param string $string The string to tokenize.
148 - *
149 - * @return array
150 - */
151 - abstract protected function tokenize($string);
140 + /**
141 + * Creates an array of tokens when given some content.
142 + *
143 + * @param string $string The string to tokenize.
144 + *
145 + * @return array
146 + */
147 + abstract protected function tokenize( $string );
152 148
153 149
154 - /**
155 - * Performs additional processing after main tokenizing.
156 - *
157 - * @return void
158 - */
159 - abstract protected function processAdditional();
150 + /**
151 + * Performs additional processing after main tokenizing.
152 + *
153 + * @return void
154 + */
155 + abstract protected function processAdditional();
160 156
161 157
162 - /**
163 - * Sets token position information.
164 - *
165 - * Can also convert tabs into spaces. Each tab can represent between
166 - * 1 and $width spaces, so this cannot be a straight string replace.
167 - *
168 - * @return void
169 - */
170 - private function createPositionMap()
171 - {
172 - $currColumn = 1;
173 - $lineNumber = 1;
174 - $eolLen = strlen($this->eolChar);
175 - $ignoring = null;
176 - $inTests = defined('PHP_CODESNIFFER_IN_TESTS');
158 + /**
159 + * Sets token position information.
160 + *
161 + * Can also convert tabs into spaces. Each tab can represent between
162 + * 1 and $width spaces, so this cannot be a straight string replace.
163 + *
164 + * @return void
165 + */
166 + private function createPositionMap() {
167 + $currColumn = 1;
168 + $lineNumber = 1;
169 + $eolLen = strlen( $this->eolChar );
170 + $ignoring = null;
171 + $inTests = defined( 'PHP_CODESNIFFER_IN_TESTS' );
177 172
178 - $checkEncoding = false;
179 - if (function_exists('iconv_strlen') === true) {
180 - $checkEncoding = true;
181 - }
173 + $checkEncoding = false;
174 + if ( function_exists( 'iconv_strlen' ) === true ) {
175 + $checkEncoding = true;
176 + }
182 177
183 - $checkAnnotations = $this->config->annotations;
184 - $encoding = $this->config->encoding;
185 - $tabWidth = $this->config->tabWidth;
178 + $checkAnnotations = $this->config->annotations;
179 + $encoding = $this->config->encoding;
180 + $tabWidth = $this->config->tabWidth;
186 181
187 - $tokensWithTabs = [
188 - T_WHITESPACE => true,
189 - T_COMMENT => true,
190 - T_DOC_COMMENT => true,
191 - T_DOC_COMMENT_WHITESPACE => true,
192 - T_DOC_COMMENT_STRING => true,
193 - T_CONSTANT_ENCAPSED_STRING => true,
194 - T_DOUBLE_QUOTED_STRING => true,
195 - T_HEREDOC => true,
196 - T_NOWDOC => true,
197 - T_INLINE_HTML => true,
198 - ];
182 + $tokensWithTabs = [
183 + T_WHITESPACE => true,
184 + T_COMMENT => true,
185 + T_DOC_COMMENT => true,
186 + T_DOC_COMMENT_WHITESPACE => true,
187 + T_DOC_COMMENT_STRING => true,
188 + T_CONSTANT_ENCAPSED_STRING => true,
189 + T_DOUBLE_QUOTED_STRING => true,
190 + T_HEREDOC => true,
191 + T_NOWDOC => true,
192 + T_INLINE_HTML => true,
193 + ];
199 194
200 - $this->numTokens = count($this->tokens);
201 - for ($i = 0; $i < $this->numTokens; $i++) {
202 - $this->tokens[$i]['line'] = $lineNumber;
203 - $this->tokens[$i]['column'] = $currColumn;
195 + $this->numTokens = count( $this->tokens );
196 + for ( $i = 0; $i < $this->numTokens; $i ++ ) {
197 + $this->tokens[ $i ]['line'] = $lineNumber;
198 + $this->tokens[ $i ]['column'] = $currColumn;
204 199
205 - if (isset($this->knownLengths[$this->tokens[$i]['code']]) === true) {
206 - // There are no tabs in the tokens we know the length of.
207 - $length = $this->knownLengths[$this->tokens[$i]['code']];
208 - $currColumn += $length;
209 - } else if ($tabWidth === 0
210 - || isset($tokensWithTabs[$this->tokens[$i]['code']]) === false
211 - || strpos($this->tokens[$i]['content'], "\t") === false
212 - ) {
213 - // There are no tabs in this content, or we aren't replacing them.
214 - if ($checkEncoding === true) {
215 - // Not using the default encoding, so take a bit more care.
216 - $oldLevel = error_reporting();
217 - error_reporting(0);
218 - $length = iconv_strlen($this->tokens[$i]['content'], $encoding);
219 - error_reporting($oldLevel);
200 + if ( isset( $this->knownLengths[ $this->tokens[ $i ]['code'] ] ) === true ) {
201 + // There are no tabs in the tokens we know the length of.
202 + $length = $this->knownLengths[ $this->tokens[ $i ]['code'] ];
203 + $currColumn += $length;
204 + } else if ( $tabWidth === 0
205 + || isset( $tokensWithTabs[ $this->tokens[ $i ]['code'] ] ) === false
206 + || strpos( $this->tokens[ $i ]['content'], "\t" ) === false
207 + ) {
208 + // There are no tabs in this content, or we aren't replacing them.
209 + if ( $checkEncoding === true ) {
210 + // Not using the default encoding, so take a bit more care.
211 + $oldLevel = error_reporting();
212 + error_reporting( 0 );
213 + $length = iconv_strlen( $this->tokens[ $i ]['content'], $encoding );
214 + error_reporting( $oldLevel );
220 215
221 - if ($length === false) {
222 - // String contained invalid characters, so revert to default.
223 - $length = strlen($this->tokens[$i]['content']);
224 - }
225 - } else {
226 - $length = strlen($this->tokens[$i]['content']);
227 - }
216 + if ( $length === false ) {
217 + // String contained invalid characters, so revert to default.
218 + $length = strlen( $this->tokens[ $i ]['content'] );
219 + }
220 + } else {
221 + $length = strlen( $this->tokens[ $i ]['content'] );
222 + }
228 223
229 - $currColumn += $length;
230 - } else {
231 - $this->replaceTabsInToken($this->tokens[$i]);
232 - $length = $this->tokens[$i]['length'];
233 - $currColumn += $length;
234 - }//end if
224 + $currColumn += $length;
225 + } else {
226 + $this->replaceTabsInToken( $this->tokens[ $i ] );
227 + $length = $this->tokens[ $i ]['length'];
228 + $currColumn += $length;
229 + }//end if
235 230
236 - $this->tokens[$i]['length'] = $length;
231 + $this->tokens[ $i ]['length'] = $length;
237 232
238 - if (isset($this->knownLengths[$this->tokens[$i]['code']]) === false
239 - && strpos($this->tokens[$i]['content'], $this->eolChar) !== false
240 - ) {
241 - $lineNumber++;
242 - $currColumn = 1;
233 + if ( isset( $this->knownLengths[ $this->tokens[ $i ]['code'] ] ) === false
234 + && strpos( $this->tokens[ $i ]['content'], $this->eolChar ) !== false
235 + ) {
236 + $lineNumber ++;
237 + $currColumn = 1;
243 238
244 - // Newline chars are not counted in the token length.
245 - $this->tokens[$i]['length'] -= $eolLen;
246 - }
239 + // Newline chars are not counted in the token length.
240 + $this->tokens[ $i ]['length'] -= $eolLen;
241 + }
247 242
248 - if ($this->tokens[$i]['code'] === T_COMMENT
249 - || $this->tokens[$i]['code'] === T_DOC_COMMENT_STRING
250 - || $this->tokens[$i]['code'] === T_DOC_COMMENT_TAG
251 - || ($inTests === true && $this->tokens[$i]['code'] === T_INLINE_HTML)
252 - ) {
253 - $commentText = ltrim($this->tokens[$i]['content'], " \t/*");
254 - $commentText = rtrim($commentText, " */\t\r\n");
255 - $commentTextLower = strtolower($commentText);
256 - if (strpos($commentText, '@codingStandards') !== false) {
257 - // If this comment is the only thing on the line, it tells us
258 - // to ignore the following line. If the line contains other content
259 - // then we are just ignoring this one single line.
260 - $ownLine = false;
261 - if ($i > 0) {
262 - for ($prev = ($i - 1); $prev >= 0; $prev--) {
263 - if ($this->tokens[$prev]['code'] === T_WHITESPACE) {
264 - continue;
265 - }
243 + if ( $this->tokens[ $i ]['code'] === T_COMMENT
244 + || $this->tokens[ $i ]['code'] === T_DOC_COMMENT_STRING
245 + || $this->tokens[ $i ]['code'] === T_DOC_COMMENT_TAG
246 + || ( $inTests === true && $this->tokens[ $i ]['code'] === T_INLINE_HTML )
247 + ) {
248 + $commentText = ltrim( $this->tokens[ $i ]['content'], " \t/*" );
249 + $commentText = rtrim( $commentText, " */\t\r\n" );
250 + $commentTextLower = strtolower( $commentText );
251 + if ( strpos( $commentText, '@codingStandards' ) !== false ) {
252 + // If this comment is the only thing on the line, it tells us
253 + // to ignore the following line. If the line contains other content
254 + // then we are just ignoring this one single line.
255 + $ownLine = false;
256 + if ( $i > 0 ) {
257 + for ( $prev = ( $i - 1 ); $prev >= 0; $prev -- ) {
258 + if ( $this->tokens[ $prev ]['code'] === T_WHITESPACE ) {
259 + continue;
260 + }
266 261
267 - break;
268 - }
262 + break;
263 + }
269 264
270 - if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
271 - $ownLine = true;
272 - }
273 - }
265 + if ( $this->tokens[ $prev ]['line'] !== $this->tokens[ $i ]['line'] ) {
266 + $ownLine = true;
267 + }
268 + }
274 269
275 - if ($ignoring === null
276 - && strpos($commentText, '@codingStandardsIgnoreStart') !== false
277 - ) {
278 - $ignoring = ['.all' => true];
279 - if ($ownLine === true) {
280 - $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
281 - }
282 - } else if ($ignoring !== null
283 - && strpos($commentText, '@codingStandardsIgnoreEnd') !== false
284 - ) {
285 - if ($ownLine === true) {
286 - $this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
287 - } else {
288 - $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
289 - }
270 + if ( $ignoring === null
271 + && strpos( $commentText, '@codingStandardsIgnoreStart' ) !== false
272 + ) {
273 + $ignoring = [ '.all' => true ];
274 + if ( $ownLine === true ) {
275 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = $ignoring;
276 + }
277 + } else if ( $ignoring !== null
278 + && strpos( $commentText, '@codingStandardsIgnoreEnd' ) !== false
279 + ) {
280 + if ( $ownLine === true ) {
281 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = [ '.all' => true ];
282 + } else {
283 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = $ignoring;
284 + }
290 285
291 - $ignoring = null;
292 - } else if ($ignoring === null
293 - && strpos($commentText, '@codingStandardsIgnoreLine') !== false
294 - ) {
295 - $ignoring = ['.all' => true];
296 - if ($ownLine === true) {
297 - $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
298 - $this->ignoredLines[($this->tokens[$i]['line'] + 1)] = $ignoring;
299 - } else {
300 - $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
301 - }
286 + $ignoring = null;
287 + } else if ( $ignoring === null
288 + && strpos( $commentText, '@codingStandardsIgnoreLine' ) !== false
289 + ) {
290 + $ignoring = [ '.all' => true ];
291 + if ( $ownLine === true ) {
292 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = $ignoring;
293 + $this->ignoredLines[ ( $this->tokens[ $i ]['line'] + 1 ) ] = $ignoring;
294 + } else {
295 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = $ignoring;
296 + }
302 297
303 - $ignoring = null;
304 - }//end if
305 - } else if (substr($commentTextLower, 0, 6) === 'phpcs:'
306 - || substr($commentTextLower, 0, 7) === '@phpcs:'
307 - ) {
308 - // If the @phpcs: syntax is being used, strip the @ to make
309 - // comparisons easier.
310 - if ($commentText[0] === '@') {
311 - $commentText = substr($commentText, 1);
312 - $commentTextLower = strtolower($commentText);
313 - }
298 + $ignoring = null;
299 + }//end if
300 + } else if ( substr( $commentTextLower, 0, 6 ) === 'phpcs:'
301 + || substr( $commentTextLower, 0, 7 ) === '@phpcs:'
302 + ) {
303 + // If the @phpcs: syntax is being used, strip the @ to make
304 + // comparisons easier.
305 + if ( $commentText[0] === '@' ) {
306 + $commentText = substr( $commentText, 1 );
307 + $commentTextLower = strtolower( $commentText );
308 + }
314 309
315 - // If there is a comment on the end, strip it off.
316 - $commentStart = strpos($commentTextLower, ' --');
317 - if ($commentStart !== false) {
318 - $commentText = substr($commentText, 0, $commentStart);
319 - $commentTextLower = strtolower($commentText);
320 - }
310 + // If there is a comment on the end, strip it off.
311 + $commentStart = strpos( $commentTextLower, ' --' );
312 + if ( $commentStart !== false ) {
313 + $commentText = substr( $commentText, 0, $commentStart );
314 + $commentTextLower = strtolower( $commentText );
315 + }
321 316
322 - // If this comment is the only thing on the line, it tells us
323 - // to ignore the following line. If the line contains other content
324 - // then we are just ignoring this one single line.
325 - $lineHasOtherContent = false;
326 - $lineHasOtherTokens = false;
327 - if ($i > 0) {
328 - for ($prev = ($i - 1); $prev > 0; $prev--) {
329 - if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
330 - // Changed lines.
331 - break;
332 - }
317 + // If this comment is the only thing on the line, it tells us
318 + // to ignore the following line. If the line contains other content
319 + // then we are just ignoring this one single line.
320 + $lineHasOtherContent = false;
321 + $lineHasOtherTokens = false;
322 + if ( $i > 0 ) {
323 + for ( $prev = ( $i - 1 ); $prev > 0; $prev -- ) {
324 + if ( $this->tokens[ $prev ]['line'] !== $this->tokens[ $i ]['line'] ) {
325 + // Changed lines.
326 + break;
327 + }
333 328
334 - if ($this->tokens[$prev]['code'] === T_WHITESPACE
335 - || ($this->tokens[$prev]['code'] === T_INLINE_HTML
336 - && trim($this->tokens[$prev]['content']) === '')
337 - ) {
338 - continue;
339 - }
329 + if ( $this->tokens[ $prev ]['code'] === T_WHITESPACE
330 + || ( $this->tokens[ $prev ]['code'] === T_INLINE_HTML
331 + && trim( $this->tokens[ $prev ]['content'] ) === '' )
332 + ) {
333 + continue;
334 + }
340 335
341 - $lineHasOtherTokens = true;
336 + $lineHasOtherTokens = true;
342 337
343 - if ($this->tokens[$prev]['code'] === T_OPEN_TAG) {
344 - continue;
345 - }
338 + if ( $this->tokens[ $prev ]['code'] === T_OPEN_TAG ) {
339 + continue;
340 + }
346 341
347 - $lineHasOtherContent = true;
348 - break;
349 - }//end for
342 + $lineHasOtherContent = true;
343 + break;
344 + }//end for
350 345
351 - $changedLines = false;
352 - for ($next = $i; $next < $this->numTokens; $next++) {
353 - if ($changedLines === true) {
354 - // Changed lines.
355 - break;
356 - }
346 + $changedLines = false;
347 + for ( $next = $i; $next < $this->numTokens; $next ++ ) {
348 + if ( $changedLines === true ) {
349 + // Changed lines.
350 + break;
351 + }
357 352
358 - if (isset($this->knownLengths[$this->tokens[$next]['code']]) === false
359 - && strpos($this->tokens[$next]['content'], $this->eolChar) !== false
360 - ) {
361 - // Last token on the current line.
362 - $changedLines = true;
363 - }
353 + if ( isset( $this->knownLengths[ $this->tokens[ $next ]['code'] ] ) === false
354 + && strpos( $this->tokens[ $next ]['content'], $this->eolChar ) !== false
355 + ) {
356 + // Last token on the current line.
357 + $changedLines = true;
358 + }
364 359
365 - if ($next === $i) {
366 - continue;
367 - }
360 + if ( $next === $i ) {
361 + continue;
362 + }
368 363
369 - if ($this->tokens[$next]['code'] === T_WHITESPACE
370 - || ($this->tokens[$next]['code'] === T_INLINE_HTML
371 - && trim($this->tokens[$next]['content']) === '')
372 - ) {
373 - continue;
374 - }
364 + if ( $this->tokens[ $next ]['code'] === T_WHITESPACE
365 + || ( $this->tokens[ $next ]['code'] === T_INLINE_HTML
366 + && trim( $this->tokens[ $next ]['content'] ) === '' )
367 + ) {
368 + continue;
369 + }
375 370
376 - $lineHasOtherTokens = true;
371 + $lineHasOtherTokens = true;
377 372
378 - if ($this->tokens[$next]['code'] === T_CLOSE_TAG) {
379 - continue;
380 - }
373 + if ( $this->tokens[ $next ]['code'] === T_CLOSE_TAG ) {
374 + continue;
375 + }
381 376
382 - $lineHasOtherContent = true;
383 - break;
384 - }//end for
385 - }//end if
377 + $lineHasOtherContent = true;
378 + break;
379 + }//end for
380 + }//end if
386 381
387 - if (substr($commentTextLower, 0, 9) === 'phpcs:set') {
388 - // Ignore standards for complete lines that change sniff settings.
389 - if ($lineHasOtherTokens === false) {
390 - $this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
391 - }
382 + if ( substr( $commentTextLower, 0, 9 ) === 'phpcs:set' ) {
383 + // Ignore standards for complete lines that change sniff settings.
384 + if ( $lineHasOtherTokens === false ) {
385 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = [ '.all' => true ];
386 + }
392 387
393 - // Need to maintain case here, to get the correct sniff code.
394 - $parts = explode(' ', substr($commentText, 10));
395 - if (count($parts) >= 2) {
396 - $sniffParts = explode('.', $parts[0]);
397 - if (count($sniffParts) >= 3) {
398 - $this->tokens[$i]['sniffCode'] = array_shift($parts);
399 - $this->tokens[$i]['sniffProperty'] = array_shift($parts);
400 - $this->tokens[$i]['sniffPropertyValue'] = rtrim(implode(' ', $parts), " */\r\n");
401 - }
402 - }
388 + // Need to maintain case here, to get the correct sniff code.
389 + $parts = explode( ' ', substr( $commentText, 10 ) );
390 + if ( count( $parts ) >= 2 ) {
391 + $sniffParts = explode( '.', $parts[0] );
392 + if ( count( $sniffParts ) >= 3 ) {
393 + $this->tokens[ $i ]['sniffCode'] = array_shift( $parts );
394 + $this->tokens[ $i ]['sniffProperty'] = array_shift( $parts );
395 + $this->tokens[ $i ]['sniffPropertyValue'] = rtrim( implode( ' ', $parts ), " */\r\n" );
396 + }
397 + }
403 398
404 - $this->tokens[$i]['code'] = T_PHPCS_SET;
405 - $this->tokens[$i]['type'] = 'T_PHPCS_SET';
406 - } else if (substr($commentTextLower, 0, 16) === 'phpcs:ignorefile') {
407 - // The whole file will be ignored, but at least set the correct token.
408 - $this->tokens[$i]['code'] = T_PHPCS_IGNORE_FILE;
409 - $this->tokens[$i]['type'] = 'T_PHPCS_IGNORE_FILE';
410 - } else if (substr($commentTextLower, 0, 13) === 'phpcs:disable') {
411 - if ($lineHasOtherContent === false) {
412 - // Completely ignore the comment line.
413 - $this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
414 - }
399 + $this->tokens[ $i ]['code'] = T_PHPCS_SET;
400 + $this->tokens[ $i ]['type'] = 'T_PHPCS_SET';
401 + } else if ( substr( $commentTextLower, 0, 16 ) === 'phpcs:ignorefile' ) {
402 + // The whole file will be ignored, but at least set the correct token.
403 + $this->tokens[ $i ]['code'] = T_PHPCS_IGNORE_FILE;
404 + $this->tokens[ $i ]['type'] = 'T_PHPCS_IGNORE_FILE';
405 + } else if ( substr( $commentTextLower, 0, 13 ) === 'phpcs:disable' ) {
406 + if ( $lineHasOtherContent === false ) {
407 + // Completely ignore the comment line.
408 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = [ '.all' => true ];
409 + }
415 410
416 - if ($ignoring === null) {
417 - $ignoring = [];
418 - }
411 + if ( $ignoring === null ) {
412 + $ignoring = [];
413 + }
419 414
420 - $disabledSniffs = [];
415 + $disabledSniffs = [];
421 416
422 - $additionalText = substr($commentText, 14);
423 - if ($additionalText === false) {
424 - $ignoring = ['.all' => true];
425 - } else {
426 - $parts = explode(',', substr($commentText, 13));
427 - foreach ($parts as $sniffCode) {
428 - $sniffCode = trim($sniffCode);
429 - $disabledSniffs[$sniffCode] = true;
430 - $ignoring[$sniffCode] = true;
417 + $additionalText = substr( $commentText, 14 );
418 + if ( $additionalText === false ) {
419 + $ignoring = [ '.all' => true ];
420 + } else {
421 + $parts = explode( ',', substr( $commentText, 13 ) );
422 + foreach ( $parts as $sniffCode ) {
423 + $sniffCode = trim( $sniffCode );
424 + $disabledSniffs[ $sniffCode ] = true;
425 + $ignoring[ $sniffCode ] = true;
431 426
432 - // This newly disabled sniff might be disabling an existing
433 - // enabled exception that we are tracking.
434 - if (isset($ignoring['.except']) === true) {
435 - foreach (array_keys($ignoring['.except']) as $ignoredSniffCode) {
436 - if ($ignoredSniffCode === $sniffCode
437 - || strpos($ignoredSniffCode, $sniffCode.'.') === 0
438 - ) {
439 - unset($ignoring['.except'][$ignoredSniffCode]);
440 - }
441 - }
427 + // This newly disabled sniff might be disabling an existing
428 + // enabled exception that we are tracking.
429 + if ( isset( $ignoring['.except'] ) === true ) {
430 + foreach ( array_keys( $ignoring['.except'] ) as $ignoredSniffCode ) {
431 + if ( $ignoredSniffCode === $sniffCode
432 + || strpos( $ignoredSniffCode, $sniffCode . '.' ) === 0
433 + ) {
434 + unset( $ignoring['.except'][ $ignoredSniffCode ] );
435 + }
436 + }
442 437
443 - if (empty($ignoring['.except']) === true) {
444 - unset($ignoring['.except']);
445 - }
446 - }
447 - }//end foreach
448 - }//end if
438 + if ( empty( $ignoring['.except'] ) === true ) {
439 + unset( $ignoring['.except'] );
440 + }
441 + }
442 + }//end foreach
443 + }//end if
449 444
450 - $this->tokens[$i]['code'] = T_PHPCS_DISABLE;
451 - $this->tokens[$i]['type'] = 'T_PHPCS_DISABLE';
452 - $this->tokens[$i]['sniffCodes'] = $disabledSniffs;
453 - } else if (substr($commentTextLower, 0, 12) === 'phpcs:enable') {
454 - if ($ignoring !== null) {
455 - $enabledSniffs = [];
445 + $this->tokens[ $i ]['code'] = T_PHPCS_DISABLE;
446 + $this->tokens[ $i ]['type'] = 'T_PHPCS_DISABLE';
447 + $this->tokens[ $i ]['sniffCodes'] = $disabledSniffs;
448 + } else if ( substr( $commentTextLower, 0, 12 ) === 'phpcs:enable' ) {
449 + if ( $ignoring !== null ) {
450 + $enabledSniffs = [];
456 451
457 - $additionalText = substr($commentText, 13);
458 - if ($additionalText === false) {
459 - $ignoring = null;
460 - } else {
461 - $parts = explode(',', substr($commentText, 13));
462 - foreach ($parts as $sniffCode) {
463 - $sniffCode = trim($sniffCode);
464 - $enabledSniffs[$sniffCode] = true;
452 + $additionalText = substr( $commentText, 13 );
453 + if ( $additionalText === false ) {
454 + $ignoring = null;
455 + } else {
456 + $parts = explode( ',', substr( $commentText, 13 ) );
457 + foreach ( $parts as $sniffCode ) {
458 + $sniffCode = trim( $sniffCode );
459 + $enabledSniffs[ $sniffCode ] = true;
465 460
466 - // This new enabled sniff might remove previously disabled
467 - // sniffs if it is actually a standard or category of sniffs.
468 - foreach (array_keys($ignoring) as $ignoredSniffCode) {
469 - if ($ignoredSniffCode === $sniffCode
470 - || strpos($ignoredSniffCode, $sniffCode.'.') === 0
471 - ) {
472 - unset($ignoring[$ignoredSniffCode]);
473 - }
474 - }
461 + // This new enabled sniff might remove previously disabled
462 + // sniffs if it is actually a standard or category of sniffs.
463 + foreach ( array_keys( $ignoring ) as $ignoredSniffCode ) {
464 + if ( $ignoredSniffCode === $sniffCode
465 + || strpos( $ignoredSniffCode, $sniffCode . '.' ) === 0
466 + ) {
467 + unset( $ignoring[ $ignoredSniffCode ] );
468 + }
469 + }
475 470
476 - // This new enabled sniff might be able to clear up
477 - // previously enabled sniffs if it is actually a standard or
478 - // category of sniffs.
479 - if (isset($ignoring['.except']) === true) {
480 - foreach (array_keys($ignoring['.except']) as $ignoredSniffCode) {
481 - if ($ignoredSniffCode === $sniffCode
482 - || strpos($ignoredSniffCode, $sniffCode.'.') === 0
483 - ) {
484 - unset($ignoring['.except'][$ignoredSniffCode]);
485 - }
486 - }
487 - }
488 - }//end foreach
471 + // This new enabled sniff might be able to clear up
472 + // previously enabled sniffs if it is actually a standard or
473 + // category of sniffs.
474 + if ( isset( $ignoring['.except'] ) === true ) {
475 + foreach ( array_keys( $ignoring['.except'] ) as $ignoredSniffCode ) {
476 + if ( $ignoredSniffCode === $sniffCode
477 + || strpos( $ignoredSniffCode, $sniffCode . '.' ) === 0
478 + ) {
479 + unset( $ignoring['.except'][ $ignoredSniffCode ] );
480 + }
481 + }
482 + }
483 + }//end foreach
489 484
490 - if (empty($ignoring) === true) {
491 - $ignoring = null;
492 - } else {
493 - if (isset($ignoring['.except']) === true) {
494 - $ignoring['.except'] += $enabledSniffs;
495 - } else {
496 - $ignoring['.except'] = $enabledSniffs;
497 - }
498 - }
499 - }//end if
485 + if ( empty( $ignoring ) === true ) {
486 + $ignoring = null;
487 + } else {
488 + if ( isset( $ignoring['.except'] ) === true ) {
489 + $ignoring['.except'] += $enabledSniffs;
490 + } else {
491 + $ignoring['.except'] = $enabledSniffs;
492 + }
493 + }
494 + }//end if
500 495
501 - if ($lineHasOtherContent === false) {
502 - // Completely ignore the comment line.
503 - $this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
504 - } else {
505 - // The comment is on the same line as the code it is ignoring,
506 - // so respect the new ignore rules.
507 - $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
508 - }
496 + if ( $lineHasOtherContent === false ) {
497 + // Completely ignore the comment line.
498 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = [ '.all' => true ];
499 + } else {
500 + // The comment is on the same line as the code it is ignoring,
501 + // so respect the new ignore rules.
502 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = $ignoring;
503 + }
509 504
510 - $this->tokens[$i]['sniffCodes'] = $enabledSniffs;
511 - }//end if
505 + $this->tokens[ $i ]['sniffCodes'] = $enabledSniffs;
506 + }//end if
512 507
513 - $this->tokens[$i]['code'] = T_PHPCS_ENABLE;
514 - $this->tokens[$i]['type'] = 'T_PHPCS_ENABLE';
515 - } else if (substr($commentTextLower, 0, 12) === 'phpcs:ignore') {
516 - $ignoreRules = [];
508 + $this->tokens[ $i ]['code'] = T_PHPCS_ENABLE;
509 + $this->tokens[ $i ]['type'] = 'T_PHPCS_ENABLE';
510 + } else if ( substr( $commentTextLower, 0, 12 ) === 'phpcs:ignore' ) {
511 + $ignoreRules = [];
517 512
518 - $additionalText = substr($commentText, 13);
519 - if ($additionalText === false) {
520 - $ignoreRules = ['.all' => true];
521 - } else {
522 - $parts = explode(',', substr($commentText, 13));
523 - foreach ($parts as $sniffCode) {
524 - $ignoreRules[trim($sniffCode)] = true;
525 - }
526 - }
513 + $additionalText = substr( $commentText, 13 );
514 + if ( $additionalText === false ) {
515 + $ignoreRules = [ '.all' => true ];
516 + } else {
517 + $parts = explode( ',', substr( $commentText, 13 ) );
518 + foreach ( $parts as $sniffCode ) {
519 + $ignoreRules[ trim( $sniffCode ) ] = true;
520 + }
521 + }
527 522
528 - $this->tokens[$i]['code'] = T_PHPCS_IGNORE;
529 - $this->tokens[$i]['type'] = 'T_PHPCS_IGNORE';
530 - $this->tokens[$i]['sniffCodes'] = $ignoreRules;
523 + $this->tokens[ $i ]['code'] = T_PHPCS_IGNORE;
524 + $this->tokens[ $i ]['type'] = 'T_PHPCS_IGNORE';
525 + $this->tokens[ $i ]['sniffCodes'] = $ignoreRules;
531 526
532 - if ($ignoring !== null) {
533 - $ignoreRules += $ignoring;
534 - }
527 + if ( $ignoring !== null ) {
528 + $ignoreRules += $ignoring;
529 + }
535 530
536 - if ($lineHasOtherContent === false) {
537 - // Completely ignore the comment line, and set the following
538 - // line to include the ignore rules we've set.
539 - $this->ignoredLines[$this->tokens[$i]['line']] = ['.all' => true];
540 - $this->ignoredLines[($this->tokens[$i]['line'] + 1)] = $ignoreRules;
541 - } else {
542 - // The comment is on the same line as the code it is ignoring,
543 - // so respect the ignore rules it set.
544 - $this->ignoredLines[$this->tokens[$i]['line']] = $ignoreRules;
545 - }
546 - }//end if
547 - }//end if
548 - }//end if
531 + if ( $lineHasOtherContent === false ) {
532 + // Completely ignore the comment line, and set the following
533 + // line to include the ignore rules we've set.
534 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = [ '.all' => true ];
535 + $this->ignoredLines[ ( $this->tokens[ $i ]['line'] + 1 ) ] = $ignoreRules;
536 + } else {
537 + // The comment is on the same line as the code it is ignoring,
538 + // so respect the ignore rules it set.
539 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = $ignoreRules;
540 + }
541 + }//end if
542 + }//end if
543 + }//end if
549 544
550 - if ($ignoring !== null && isset($this->ignoredLines[$this->tokens[$i]['line']]) === false) {
551 - $this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
552 - }
553 - }//end for
545 + if ( $ignoring !== null && isset( $this->ignoredLines[ $this->tokens[ $i ]['line'] ] ) === false ) {
546 + $this->ignoredLines[ $this->tokens[ $i ]['line'] ] = $ignoring;
547 + }
548 + }//end for
554 549
555 - // If annotations are being ignored, we clear out all the ignore rules
556 - // but leave the annotations tokenized as normal.
557 - if ($checkAnnotations === false) {
558 - $this->ignoredLines = [];
559 - }
550 + // If annotations are being ignored, we clear out all the ignore rules
551 + // but leave the annotations tokenized as normal.
552 + if ( $checkAnnotations === false ) {
553 + $this->ignoredLines = [];
554 + }
560 555
561 - }//end createPositionMap()
556 + }//end createPositionMap()
562 557
563 558
564 - /**
565 - * Replaces tabs in original token content with spaces.
566 - *
567 - * Each tab can represent between 1 and $config->tabWidth spaces,
568 - * so this cannot be a straight string replace. The original content
569 - * is placed into an orig_content index and the new token length is also
570 - * set in the length index.
571 - *
572 - * @param array $token The token to replace tabs inside.
573 - * @param string $prefix The character to use to represent the start of a tab.
574 - * @param string $padding The character to use to represent the end of a tab.
575 - * @param int $tabWidth The number of spaces each tab represents.
576 - *
577 - * @return void
578 - */
579 - public function replaceTabsInToken(&$token, $prefix=' ', $padding=' ', $tabWidth=null)
580 - {
581 - $checkEncoding = false;
582 - if (function_exists('iconv_strlen') === true) {
583 - $checkEncoding = true;
584 - }
559 + /**
560 + * Replaces tabs in original token content with spaces.
561 + *
562 + * Each tab can represent between 1 and $config->tabWidth spaces,
563 + * so this cannot be a straight string replace. The original content
564 + * is placed into an orig_content index and the new token length is also
565 + * set in the length index.
566 + *
567 + * @param array $token The token to replace tabs inside.
568 + * @param string $prefix The character to use to represent the start of a tab.
569 + * @param string $padding The character to use to represent the end of a tab.
570 + * @param int $tabWidth The number of spaces each tab represents.
571 + *
572 + * @return void
573 + */
574 + public function replaceTabsInToken( &$token, $prefix = ' ', $padding = ' ', $tabWidth = null ) {
575 + $checkEncoding = false;
576 + if ( function_exists( 'iconv_strlen' ) === true ) {
577 + $checkEncoding = true;
578 + }
585 579
586 - $currColumn = $token['column'];
587 - if ($tabWidth === null) {
588 - $tabWidth = $this->config->tabWidth;
589 - if ($tabWidth === 0) {
590 - $tabWidth = 1;
591 - }
592 - }
580 + $currColumn = $token['column'];
581 + if ( $tabWidth === null ) {
582 + $tabWidth = $this->config->tabWidth;
583 + if ( $tabWidth === 0 ) {
584 + $tabWidth = 1;
585 + }
586 + }
593 587
594 - if (rtrim($token['content'], "\t") === '') {
595 - // String only contains tabs, so we can shortcut the process.
596 - $numTabs = strlen($token['content']);
588 + if ( rtrim( $token['content'], "\t" ) === '' ) {
589 + // String only contains tabs, so we can shortcut the process.
590 + $numTabs = strlen( $token['content'] );
597 591
598 - $firstTabSize = ($tabWidth - (($currColumn - 1) % $tabWidth));
599 - $length = ($firstTabSize + ($tabWidth * ($numTabs - 1)));
600 - $newContent = $prefix.str_repeat($padding, ($length - 1));
601 - } else {
602 - // We need to determine the length of each tab.
603 - $tabs = explode("\t", $token['content']);
592 + $firstTabSize = ( $tabWidth - ( ( $currColumn - 1 ) % $tabWidth ) );
593 + $length = ( $firstTabSize + ( $tabWidth * ( $numTabs - 1 ) ) );
594 + $newContent = $prefix . str_repeat( $padding, ( $length - 1 ) );
595 + } else {
596 + // We need to determine the length of each tab.
597 + $tabs = explode( "\t", $token['content'] );
604 598
605 - $numTabs = (count($tabs) - 1);
606 - $tabNum = 0;
607 - $newContent = '';
608 - $length = 0;
599 + $numTabs = ( count( $tabs ) - 1 );
600 + $tabNum = 0;
601 + $newContent = '';
602 + $length = 0;
609 603
610 - foreach ($tabs as $content) {
611 - if ($content !== '') {
612 - $newContent .= $content;
613 - if ($checkEncoding === true) {
614 - // Not using the default encoding, so take a bit more care.
615 - $oldLevel = error_reporting();
616 - error_reporting(0);
617 - $contentLength = iconv_strlen($content, $this->config->encoding);
618 - error_reporting($oldLevel);
619 - if ($contentLength === false) {
620 - // String contained invalid characters, so revert to default.
621 - $contentLength = strlen($content);
622 - }
623 - } else {
624 - $contentLength = strlen($content);
625 - }
604 + foreach ( $tabs as $content ) {
605 + if ( $content !== '' ) {
606 + $newContent .= $content;
607 + if ( $checkEncoding === true ) {
608 + // Not using the default encoding, so take a bit more care.
609 + $oldLevel = error_reporting();
610 + error_reporting( 0 );
611 + $contentLength = iconv_strlen( $content, $this->config->encoding );
612 + error_reporting( $oldLevel );
613 + if ( $contentLength === false ) {
614 + // String contained invalid characters, so revert to default.
615 + $contentLength = strlen( $content );
616 + }
617 + } else {
618 + $contentLength = strlen( $content );
619 + }
626 620
627 - $currColumn += $contentLength;
628 - $length += $contentLength;
629 - }
621 + $currColumn += $contentLength;
622 + $length += $contentLength;
623 + }
630 624
631 - // The last piece of content does not have a tab after it.
632 - if ($tabNum === $numTabs) {
633 - break;
634 - }
625 + // The last piece of content does not have a tab after it.
626 + if ( $tabNum === $numTabs ) {
627 + break;
628 + }
635 629
636 - // Process the tab that comes after the content.
637 - $lastCurrColumn = $currColumn;
638 - $tabNum++;
630 + // Process the tab that comes after the content.
631 + $lastCurrColumn = $currColumn;
632 + $tabNum ++;
639 633
640 - // Move the pointer to the next tab stop.
641 - if (($currColumn % $tabWidth) === 0) {
642 - // This is the first tab, and we are already at a
643 - // tab stop, so this tab counts as a single space.
644 - $currColumn++;
645 - } else {
646 - $currColumn++;
647 - while (($currColumn % $tabWidth) !== 0) {
648 - $currColumn++;
649 - }
634 + // Move the pointer to the next tab stop.
635 + if ( ( $currColumn % $tabWidth ) === 0 ) {
636 + // This is the first tab, and we are already at a
637 + // tab stop, so this tab counts as a single space.
638 + $currColumn ++;
639 + } else {
640 + $currColumn ++;
641 + while ( ( $currColumn % $tabWidth ) !== 0 ) {
642 + $currColumn ++;
643 + }
650 644
651 - $currColumn++;
652 - }
645 + $currColumn ++;
646 + }
653 647
654 - $length += ($currColumn - $lastCurrColumn);
655 - $newContent .= $prefix.str_repeat($padding, ($currColumn - $lastCurrColumn - 1));
656 - }//end foreach
657 - }//end if
648 + $length += ( $currColumn - $lastCurrColumn );
649 + $newContent .= $prefix . str_repeat( $padding, ( $currColumn - $lastCurrColumn - 1 ) );
650 + }//end foreach
651 + }//end if
658 652
659 - $token['orig_content'] = $token['content'];
660 - $token['content'] = $newContent;
661 - $token['length'] = $length;
653 + $token['orig_content'] = $token['content'];
654 + $token['content'] = $newContent;
655 + $token['length'] = $length;
662 656
663 - }//end replaceTabsInToken()
657 + }//end replaceTabsInToken()
664 658
665 659
666 - /**
667 - * Creates a map of brackets positions.
668 - *
669 - * @return void
670 - */
671 - private function createTokenMap()
672 - {
673 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
674 - echo "\t*** START TOKEN MAP ***".PHP_EOL;
675 - }
660 + /**
661 + * Creates a map of brackets positions.
662 + *
663 + * @return void
664 + */
665 + private function createTokenMap() {
666 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
667 + echo "\t*** START TOKEN MAP ***" . PHP_EOL;
668 + }
676 669
677 - $squareOpeners = [];
678 - $curlyOpeners = [];
679 - $this->numTokens = count($this->tokens);
670 + $squareOpeners = [];
671 + $curlyOpeners = [];
672 + $this->numTokens = count( $this->tokens );
680 673
681 - $openers = [];
682 - $openOwner = null;
674 + $openers = [];
675 + $openOwner = null;
683 676
684 - for ($i = 0; $i < $this->numTokens; $i++) {
685 - /*
677 + for ( $i = 0; $i < $this->numTokens; $i ++ ) {
678 + /*
686 679 Parenthesis mapping.
687 680 */
688 681
689 - if (isset(Util\Tokens::$parenthesisOpeners[$this->tokens[$i]['code']]) === true) {
690 - $this->tokens[$i]['parenthesis_opener'] = null;
691 - $this->tokens[$i]['parenthesis_closer'] = null;
692 - $this->tokens[$i]['parenthesis_owner'] = $i;
693 - $openOwner = $i;
682 + if ( isset( Util\Tokens::$parenthesisOpeners[ $this->tokens[ $i ]['code'] ] ) === true ) {
683 + $this->tokens[ $i ]['parenthesis_opener'] = null;
684 + $this->tokens[ $i ]['parenthesis_closer'] = null;
685 + $this->tokens[ $i ]['parenthesis_owner'] = $i;
686 + $openOwner = $i;
694 687
695 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
696 - echo str_repeat("\t", (count($openers) + 1));
697 - echo "=> Found parenthesis owner at $i".PHP_EOL;
698 - }
699 - } else if ($this->tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
700 - $openers[] = $i;
701 - $this->tokens[$i]['parenthesis_opener'] = $i;
702 - if ($openOwner !== null) {
703 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
704 - echo str_repeat("\t", count($openers));
705 - echo "=> Found parenthesis opener at $i for $openOwner".PHP_EOL;
706 - }
688 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
689 + echo str_repeat( "\t", ( count( $openers ) + 1 ) );
690 + echo "=> Found parenthesis owner at $i" . PHP_EOL;
691 + }
692 + } else if ( $this->tokens[ $i ]['code'] === T_OPEN_PARENTHESIS ) {
693 + $openers[] = $i;
694 + $this->tokens[ $i ]['parenthesis_opener'] = $i;
695 + if ( $openOwner !== null ) {
696 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
697 + echo str_repeat( "\t", count( $openers ) );
698 + echo "=> Found parenthesis opener at $i for $openOwner" . PHP_EOL;
699 + }
707 700
708 - $this->tokens[$openOwner]['parenthesis_opener'] = $i;
709 - $this->tokens[$i]['parenthesis_owner'] = $openOwner;
710 - $openOwner = null;
711 - } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
712 - echo str_repeat("\t", count($openers));
713 - echo "=> Found unowned parenthesis opener at $i".PHP_EOL;
714 - }
715 - } else if ($this->tokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
716 - // Did we set an owner for this set of parenthesis?
717 - $numOpeners = count($openers);
718 - if ($numOpeners !== 0) {
719 - $opener = array_pop($openers);
720 - if (isset($this->tokens[$opener]['parenthesis_owner']) === true) {
721 - $owner = $this->tokens[$opener]['parenthesis_owner'];
701 + $this->tokens[ $openOwner ]['parenthesis_opener'] = $i;
702 + $this->tokens[ $i ]['parenthesis_owner'] = $openOwner;
703 + $openOwner = null;
704 + } else if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
705 + echo str_repeat( "\t", count( $openers ) );
706 + echo "=> Found unowned parenthesis opener at $i" . PHP_EOL;
707 + }
708 + } else if ( $this->tokens[ $i ]['code'] === T_CLOSE_PARENTHESIS ) {
709 + // Did we set an owner for this set of parenthesis?
710 + $numOpeners = count( $openers );
711 + if ( $numOpeners !== 0 ) {
712 + $opener = array_pop( $openers );
713 + if ( isset( $this->tokens[ $opener ]['parenthesis_owner'] ) === true ) {
714 + $owner = $this->tokens[ $opener ]['parenthesis_owner'];
722 715
723 - $this->tokens[$owner]['parenthesis_closer'] = $i;
724 - $this->tokens[$i]['parenthesis_owner'] = $owner;
716 + $this->tokens[ $owner ]['parenthesis_closer'] = $i;
717 + $this->tokens[ $i ]['parenthesis_owner'] = $owner;
725 718
726 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
727 - echo str_repeat("\t", (count($openers) + 1));
728 - echo "=> Found parenthesis closer at $i for $owner".PHP_EOL;
729 - }
730 - } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
731 - echo str_repeat("\t", (count($openers) + 1));
732 - echo "=> Found unowned parenthesis closer at $i for $opener".PHP_EOL;
733 - }
719 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
720 + echo str_repeat( "\t", ( count( $openers ) + 1 ) );
721 + echo "=> Found parenthesis closer at $i for $owner" . PHP_EOL;
722 + }
723 + } else if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
724 + echo str_repeat( "\t", ( count( $openers ) + 1 ) );
725 + echo "=> Found unowned parenthesis closer at $i for $opener" . PHP_EOL;
726 + }
734 727
735 - $this->tokens[$i]['parenthesis_opener'] = $opener;
736 - $this->tokens[$i]['parenthesis_closer'] = $i;
737 - $this->tokens[$opener]['parenthesis_closer'] = $i;
738 - }//end if
739 - }//end if
728 + $this->tokens[ $i ]['parenthesis_opener'] = $opener;
729 + $this->tokens[ $i ]['parenthesis_closer'] = $i;
730 + $this->tokens[ $opener ]['parenthesis_closer'] = $i;
731 + }//end if
732 + }//end if
740 733
741 - /*
734 + /*
742 735 Bracket mapping.
743 736 */
744 737
745 - switch ($this->tokens[$i]['code']) {
746 - case T_OPEN_SQUARE_BRACKET:
747 - $squareOpeners[] = $i;
738 + switch ( $this->tokens[ $i ]['code'] ) {
739 + case T_OPEN_SQUARE_BRACKET:
740 + $squareOpeners[] = $i;
748 741
749 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
750 - echo str_repeat("\t", count($squareOpeners));
751 - echo str_repeat("\t", count($curlyOpeners));
752 - echo "=> Found square bracket opener at $i".PHP_EOL;
753 - }
754 - break;
755 - case T_OPEN_CURLY_BRACKET:
756 - if (isset($this->tokens[$i]['scope_closer']) === false) {
757 - $curlyOpeners[] = $i;
742 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
743 + echo str_repeat( "\t", count( $squareOpeners ) );
744 + echo str_repeat( "\t", count( $curlyOpeners ) );
745 + echo "=> Found square bracket opener at $i" . PHP_EOL;
746 + }
747 + break;
748 + case T_OPEN_CURLY_BRACKET:
749 + if ( isset( $this->tokens[ $i ]['scope_closer'] ) === false ) {
750 + $curlyOpeners[] = $i;
758 751
759 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
760 - echo str_repeat("\t", count($squareOpeners));
761 - echo str_repeat("\t", count($curlyOpeners));
762 - echo "=> Found curly bracket opener at $i".PHP_EOL;
763 - }
764 - }
765 - break;
766 - case T_CLOSE_SQUARE_BRACKET:
767 - if (empty($squareOpeners) === false) {
768 - $opener = array_pop($squareOpeners);
769 - $this->tokens[$i]['bracket_opener'] = $opener;
770 - $this->tokens[$i]['bracket_closer'] = $i;
771 - $this->tokens[$opener]['bracket_opener'] = $opener;
772 - $this->tokens[$opener]['bracket_closer'] = $i;
752 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
753 + echo str_repeat( "\t", count( $squareOpeners ) );
754 + echo str_repeat( "\t", count( $curlyOpeners ) );
755 + echo "=> Found curly bracket opener at $i" . PHP_EOL;
756 + }
757 + }
758 + break;
759 + case T_CLOSE_SQUARE_BRACKET:
760 + if ( empty( $squareOpeners ) === false ) {
761 + $opener = array_pop( $squareOpeners );
762 + $this->tokens[ $i ]['bracket_opener'] = $opener;
763 + $this->tokens[ $i ]['bracket_closer'] = $i;
764 + $this->tokens[ $opener ]['bracket_opener'] = $opener;
765 + $this->tokens[ $opener ]['bracket_closer'] = $i;
773 766
774 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
775 - echo str_repeat("\t", count($squareOpeners));
776 - echo str_repeat("\t", count($curlyOpeners));
777 - echo "\t=> Found square bracket closer at $i for $opener".PHP_EOL;
778 - }
779 - }
780 - break;
781 - case T_CLOSE_CURLY_BRACKET:
782 - if (empty($curlyOpeners) === false
783 - && isset($this->tokens[$i]['scope_opener']) === false
784 - ) {
785 - $opener = array_pop($curlyOpeners);
786 - $this->tokens[$i]['bracket_opener'] = $opener;
787 - $this->tokens[$i]['bracket_closer'] = $i;
788 - $this->tokens[$opener]['bracket_opener'] = $opener;
789 - $this->tokens[$opener]['bracket_closer'] = $i;
767 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
768 + echo str_repeat( "\t", count( $squareOpeners ) );
769 + echo str_repeat( "\t", count( $curlyOpeners ) );
770 + echo "\t=> Found square bracket closer at $i for $opener" . PHP_EOL;
771 + }
772 + }
773 + break;
774 + case T_CLOSE_CURLY_BRACKET:
775 + if ( empty( $curlyOpeners ) === false
776 + && isset( $this->tokens[ $i ]['scope_opener'] ) === false
777 + ) {
778 + $opener = array_pop( $curlyOpeners );
779 + $this->tokens[ $i ]['bracket_opener'] = $opener;
780 + $this->tokens[ $i ]['bracket_closer'] = $i;
781 + $this->tokens[ $opener ]['bracket_opener'] = $opener;
782 + $this->tokens[ $opener ]['bracket_closer'] = $i;
790 783
791 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
792 - echo str_repeat("\t", count($squareOpeners));
793 - echo str_repeat("\t", count($curlyOpeners));
794 - echo "\t=> Found curly bracket closer at $i for $opener".PHP_EOL;
795 - }
796 - }
797 - break;
798 - default:
799 - continue 2;
800 - }//end switch
801 - }//end for
784 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
785 + echo str_repeat( "\t", count( $squareOpeners ) );
786 + echo str_repeat( "\t", count( $curlyOpeners ) );
787 + echo "\t=> Found curly bracket closer at $i for $opener" . PHP_EOL;
788 + }
789 + }
790 + break;
791 + default:
792 + continue 2;
793 + }//end switch
794 + }//end for
802 795
803 - // Cleanup for any openers that we didn't find closers for.
804 - // This typically means there was a syntax error breaking things.
805 - foreach ($openers as $opener) {
806 - unset($this->tokens[$opener]['parenthesis_opener']);
807 - unset($this->tokens[$opener]['parenthesis_owner']);
808 - }
796 + // Cleanup for any openers that we didn't find closers for.
797 + // This typically means there was a syntax error breaking things.
798 + foreach ( $openers as $opener ) {
799 + unset( $this->tokens[ $opener ]['parenthesis_opener'] );
800 + unset( $this->tokens[ $opener ]['parenthesis_owner'] );
801 + }
809 802
810 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
811 - echo "\t*** END TOKEN MAP ***".PHP_EOL;
812 - }
803 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
804 + echo "\t*** END TOKEN MAP ***" . PHP_EOL;
805 + }
813 806
814 - }//end createTokenMap()
807 + }//end createTokenMap()
815 808
816 809
817 - /**
818 - * Creates a map for the parenthesis tokens that surround other tokens.
819 - *
820 - * @return void
821 - */
822 - private function createParenthesisNestingMap()
823 - {
824 - $map = [];
825 - for ($i = 0; $i < $this->numTokens; $i++) {
826 - if (isset($this->tokens[$i]['parenthesis_opener']) === true
827 - && $i === $this->tokens[$i]['parenthesis_opener']
828 - ) {
829 - if (empty($map) === false) {
830 - $this->tokens[$i]['nested_parenthesis'] = $map;
831 - }
810 + /**
811 + * Creates a map for the parenthesis tokens that surround other tokens.
812 + *
813 + * @return void
814 + */
815 + private function createParenthesisNestingMap() {
816 + $map = [];
817 + for ( $i = 0; $i < $this->numTokens; $i ++ ) {
818 + if ( isset( $this->tokens[ $i ]['parenthesis_opener'] ) === true
819 + && $i === $this->tokens[ $i ]['parenthesis_opener']
820 + ) {
821 + if ( empty( $map ) === false ) {
822 + $this->tokens[ $i ]['nested_parenthesis'] = $map;
823 + }
832 824
833 - if (isset($this->tokens[$i]['parenthesis_closer']) === true) {
834 - $map[$this->tokens[$i]['parenthesis_opener']]
835 - = $this->tokens[$i]['parenthesis_closer'];
836 - }
837 - } else if (isset($this->tokens[$i]['parenthesis_closer']) === true
838 - && $i === $this->tokens[$i]['parenthesis_closer']
839 - ) {
840 - array_pop($map);
841 - if (empty($map) === false) {
842 - $this->tokens[$i]['nested_parenthesis'] = $map;
843 - }
844 - } else {
845 - if (empty($map) === false) {
846 - $this->tokens[$i]['nested_parenthesis'] = $map;
847 - }
848 - }//end if
849 - }//end for
825 + if ( isset( $this->tokens[ $i ]['parenthesis_closer'] ) === true ) {
826 + $map[ $this->tokens[ $i ]['parenthesis_opener'] ]
827 + = $this->tokens[ $i ]['parenthesis_closer'];
828 + }
829 + } else if ( isset( $this->tokens[ $i ]['parenthesis_closer'] ) === true
830 + && $i === $this->tokens[ $i ]['parenthesis_closer']
831 + ) {
832 + array_pop( $map );
833 + if ( empty( $map ) === false ) {
834 + $this->tokens[ $i ]['nested_parenthesis'] = $map;
835 + }
836 + } else {
837 + if ( empty( $map ) === false ) {
838 + $this->tokens[ $i ]['nested_parenthesis'] = $map;
839 + }
840 + }//end if
841 + }//end for
850 842
851 - }//end createParenthesisNestingMap()
843 + }//end createParenthesisNestingMap()
852 844
853 845
854 - /**
855 - * Creates a scope map of tokens that open scopes.
856 - *
857 - * @return void
858 - * @see recurseScopeMap()
859 - */
860 - private function createScopeMap()
861 - {
862 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
863 - echo "\t*** START SCOPE MAP ***".PHP_EOL;
864 - }
846 + /**
847 + * Creates a scope map of tokens that open scopes.
848 + *
849 + * @return void
850 + * @see recurseScopeMap()
851 + */
852 + private function createScopeMap() {
853 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
854 + echo "\t*** START SCOPE MAP ***" . PHP_EOL;
855 + }
865 856
866 - for ($i = 0; $i < $this->numTokens; $i++) {
867 - // Check to see if the current token starts a new scope.
868 - if (isset($this->scopeOpeners[$this->tokens[$i]['code']]) === true) {
869 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
870 - $type = $this->tokens[$i]['type'];
871 - $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
872 - echo "\tStart scope map at $i:$type => $content".PHP_EOL;
873 - }
857 + for ( $i = 0; $i < $this->numTokens; $i ++ ) {
858 + // Check to see if the current token starts a new scope.
859 + if ( isset( $this->scopeOpeners[ $this->tokens[ $i ]['code'] ] ) === true ) {
860 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
861 + $type = $this->tokens[ $i ]['type'];
862 + $content = Util\Common::prepareForOutput( $this->tokens[ $i ]['content'] );
863 + echo "\tStart scope map at $i:$type => $content" . PHP_EOL;
864 + }
874 865
875 - if (isset($this->tokens[$i]['scope_condition']) === true) {
876 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
877 - echo "\t* already processed, skipping *".PHP_EOL;
878 - }
866 + if ( isset( $this->tokens[ $i ]['scope_condition'] ) === true ) {
867 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
868 + echo "\t* already processed, skipping *" . PHP_EOL;
869 + }
879 870
880 - continue;
881 - }
871 + continue;
872 + }
882 873
883 - $i = $this->recurseScopeMap($i);
884 - }//end if
885 - }//end for
874 + $i = $this->recurseScopeMap( $i );
875 + }//end if
876 + }//end for
886 877
887 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
888 - echo "\t*** END SCOPE MAP ***".PHP_EOL;
889 - }
878 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
879 + echo "\t*** END SCOPE MAP ***" . PHP_EOL;
880 + }
890 881
891 - }//end createScopeMap()
882 + }//end createScopeMap()
892 883
893 884
894 - /**
895 - * Recurses though the scope openers to build a scope map.
896 - *
897 - * @param int $stackPtr The position in the stack of the token that
898 - * opened the scope (eg. an IF token or FOR token).
899 - * @param int $depth How many scope levels down we are.
900 - * @param int $ignore How many curly braces we are ignoring.
901 - *
902 - * @return int The position in the stack that closed the scope.
903 - * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the nesting level gets too deep.
904 - */
905 - private function recurseScopeMap($stackPtr, $depth=1, &$ignore=0)
906 - {
907 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
908 - echo str_repeat("\t", $depth);
909 - echo "=> Begin scope map recursion at token $stackPtr with depth $depth".PHP_EOL;
910 - }
885 + /**
886 + * Recurses though the scope openers to build a scope map.
887 + *
888 + * @param int $stackPtr The position in the stack of the token that
889 + * opened the scope (eg. an IF token or FOR token).
890 + * @param int $depth How many scope levels down we are.
891 + * @param int $ignore How many curly braces we are ignoring.
892 + *
893 + * @return int The position in the stack that closed the scope.
894 + * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the nesting level gets too deep.
895 + */
896 + private function recurseScopeMap( $stackPtr, $depth = 1, &$ignore = 0 ) {
897 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
898 + echo str_repeat( "\t", $depth );
899 + echo "=> Begin scope map recursion at token $stackPtr with depth $depth" . PHP_EOL;
900 + }
911 901
912 - $opener = null;
913 - $currType = $this->tokens[$stackPtr]['code'];
914 - $startLine = $this->tokens[$stackPtr]['line'];
902 + $opener = null;
903 + $currType = $this->tokens[ $stackPtr ]['code'];
904 + $startLine = $this->tokens[ $stackPtr ]['line'];
915 905
916 - // We will need this to restore the value if we end up
917 - // returning a token ID that causes our calling function to go back
918 - // over already ignored braces.
919 - $originalIgnore = $ignore;
906 + // We will need this to restore the value if we end up
907 + // returning a token ID that causes our calling function to go back
908 + // over already ignored braces.
909 + $originalIgnore = $ignore;
920 910
921 - // If the start token for this scope opener is the same as
922 - // the scope token, we have already found our opener.
923 - if (isset($this->scopeOpeners[$currType]['start'][$currType]) === true) {
924 - $opener = $stackPtr;
925 - }
911 + // If the start token for this scope opener is the same as
912 + // the scope token, we have already found our opener.
913 + if ( isset( $this->scopeOpeners[ $currType ]['start'][ $currType ] ) === true ) {
914 + $opener = $stackPtr;
915 + }
926 916
927 - for ($i = ($stackPtr + 1); $i < $this->numTokens; $i++) {
928 - $tokenType = $this->tokens[$i]['code'];
917 + for ( $i = ( $stackPtr + 1 ); $i < $this->numTokens; $i ++ ) {
918 + $tokenType = $this->tokens[ $i ]['code'];
929 919
930 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
931 - $type = $this->tokens[$i]['type'];
932 - $line = $this->tokens[$i]['line'];
933 - $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
920 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
921 + $type = $this->tokens[ $i ]['type'];
922 + $line = $this->tokens[ $i ]['line'];
923 + $content = Util\Common::prepareForOutput( $this->tokens[ $i ]['content'] );
934 924
935 - echo str_repeat("\t", $depth);
936 - echo "Process token $i on line $line [";
937 - if ($opener !== null) {
938 - echo "opener:$opener;";
939 - }
925 + echo str_repeat( "\t", $depth );
926 + echo "Process token $i on line $line [";
927 + if ( $opener !== null ) {
928 + echo "opener:$opener;";
929 + }
940 930
941 - if ($ignore > 0) {
942 - echo "ignore=$ignore;";
943 - }
931 + if ( $ignore > 0 ) {
932 + echo "ignore=$ignore;";
933 + }
944 934
945 - echo "]: $type => $content".PHP_EOL;
946 - }//end if
935 + echo "]: $type => $content" . PHP_EOL;
936 + }//end if
947 937
948 - // Very special case for IF statements in PHP that can be defined without
949 - // scope tokens. E.g., if (1) 1; 1 ? (1 ? 1 : 1) : 1;
950 - // If an IF statement below this one has an opener but no
951 - // keyword, the opener will be incorrectly assigned to this IF statement.
952 - // The same case also applies to USE statements, which don't have to have
953 - // openers, so a following USE statement can cause an incorrect brace match.
954 - if (($currType === T_IF || $currType === T_ELSE || $currType === T_USE)
955 - && $opener === null
956 - && ($this->tokens[$i]['code'] === T_SEMICOLON
957 - || $this->tokens[$i]['code'] === T_CLOSE_TAG)
958 - ) {
959 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
960 - $type = $this->tokens[$stackPtr]['type'];
961 - echo str_repeat("\t", $depth);
962 - if ($this->tokens[$i]['code'] === T_SEMICOLON) {
963 - $closerType = 'semicolon';
964 - } else {
965 - $closerType = 'close tag';
966 - }
938 + // Very special case for IF statements in PHP that can be defined without
939 + // scope tokens. E.g., if (1) 1; 1 ? (1 ? 1 : 1) : 1;
940 + // If an IF statement below this one has an opener but no
941 + // keyword, the opener will be incorrectly assigned to this IF statement.
942 + // The same case also applies to USE statements, which don't have to have
943 + // openers, so a following USE statement can cause an incorrect brace match.
944 + if ( ( $currType === T_IF || $currType === T_ELSE || $currType === T_USE )
945 + && $opener === null
946 + && ( $this->tokens[ $i ]['code'] === T_SEMICOLON
947 + || $this->tokens[ $i ]['code'] === T_CLOSE_TAG )
948 + ) {
949 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
950 + $type = $this->tokens[ $stackPtr ]['type'];
951 + echo str_repeat( "\t", $depth );
952 + if ( $this->tokens[ $i ]['code'] === T_SEMICOLON ) {
953 + $closerType = 'semicolon';
954 + } else {
955 + $closerType = 'close tag';
956 + }
967 957
968 - echo "=> Found $closerType before scope opener for $stackPtr:$type, bailing".PHP_EOL;
969 - }
958 + echo "=> Found $closerType before scope opener for $stackPtr:$type, bailing" . PHP_EOL;
959 + }
970 960
971 - return $i;
972 - }
961 + return $i;
962 + }
973 963
974 - // Special case for PHP control structures that have no braces.
975 - // If we find a curly brace closer before we find the opener,
976 - // we're not going to find an opener. That closer probably belongs to
977 - // a control structure higher up.
978 - if ($opener === null
979 - && $ignore === 0
980 - && $tokenType === T_CLOSE_CURLY_BRACKET
981 - && isset($this->scopeOpeners[$currType]['end'][$tokenType]) === true
982 - ) {
983 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
984 - $type = $this->tokens[$stackPtr]['type'];
985 - echo str_repeat("\t", $depth);
986 - echo "=> Found curly brace closer before scope opener for $stackPtr:$type, bailing".PHP_EOL;
987 - }
964 + // Special case for PHP control structures that have no braces.
965 + // If we find a curly brace closer before we find the opener,
966 + // we're not going to find an opener. That closer probably belongs to
967 + // a control structure higher up.
968 + if ( $opener === null
969 + && $ignore === 0
970 + && $tokenType === T_CLOSE_CURLY_BRACKET
971 + && isset( $this->scopeOpeners[ $currType ]['end'][ $tokenType ] ) === true
972 + ) {
973 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
974 + $type = $this->tokens[ $stackPtr ]['type'];
975 + echo str_repeat( "\t", $depth );
976 + echo "=> Found curly brace closer before scope opener for $stackPtr:$type, bailing" . PHP_EOL;
977 + }
988 978
989 - return ($i - 1);
990 - }
979 + return ( $i - 1 );
980 + }
991 981
992 - if ($opener !== null
993 - && (isset($this->tokens[$i]['scope_opener']) === false
994 - || $this->scopeOpeners[$this->tokens[$stackPtr]['code']]['shared'] === true)
995 - && isset($this->scopeOpeners[$currType]['end'][$tokenType]) === true
996 - ) {
997 - if ($ignore > 0 && $tokenType === T_CLOSE_CURLY_BRACKET) {
998 - // The last opening bracket must have been for a string
999 - // offset or alike, so let's ignore it.
1000 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1001 - echo str_repeat("\t", $depth);
1002 - echo '* finished ignoring curly brace *'.PHP_EOL;
1003 - }
982 + if ( $opener !== null
983 + && ( isset( $this->tokens[ $i ]['scope_opener'] ) === false
984 + || $this->scopeOpeners[ $this->tokens[ $stackPtr ]['code'] ]['shared'] === true )
985 + && isset( $this->scopeOpeners[ $currType ]['end'][ $tokenType ] ) === true
986 + ) {
987 + if ( $ignore > 0 && $tokenType === T_CLOSE_CURLY_BRACKET ) {
988 + // The last opening bracket must have been for a string
989 + // offset or alike, so let's ignore it.
990 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
991 + echo str_repeat( "\t", $depth );
992 + echo '* finished ignoring curly brace *' . PHP_EOL;
993 + }
1004 994
1005 - $ignore--;
1006 - continue;
1007 - } else if ($this->tokens[$opener]['code'] === T_OPEN_CURLY_BRACKET
1008 - && $tokenType !== T_CLOSE_CURLY_BRACKET
1009 - ) {
1010 - // The opener is a curly bracket so the closer must be a curly bracket as well.
1011 - // We ignore this closer to handle cases such as T_ELSE or T_ELSEIF being considered
1012 - // a closer of T_IF when it should not.
1013 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1014 - $type = $this->tokens[$stackPtr]['type'];
1015 - echo str_repeat("\t", $depth);
1016 - echo "=> Ignoring non-curly scope closer for $stackPtr:$type".PHP_EOL;
1017 - }
1018 - } else {
1019 - $scopeCloser = $i;
1020 - $todo = [
1021 - $stackPtr,
1022 - $opener,
1023 - ];
995 + $ignore --;
996 + continue;
997 + } else if ( $this->tokens[ $opener ]['code'] === T_OPEN_CURLY_BRACKET
998 + && $tokenType !== T_CLOSE_CURLY_BRACKET
999 + ) {
1000 + // The opener is a curly bracket so the closer must be a curly bracket as well.
1001 + // We ignore this closer to handle cases such as T_ELSE or T_ELSEIF being considered
1002 + // a closer of T_IF when it should not.
1003 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1004 + $type = $this->tokens[ $stackPtr ]['type'];
1005 + echo str_repeat( "\t", $depth );
1006 + echo "=> Ignoring non-curly scope closer for $stackPtr:$type" . PHP_EOL;
1007 + }
1008 + } else {
1009 + $scopeCloser = $i;
1010 + $todo = [
1011 + $stackPtr,
1012 + $opener,
1013 + ];
1024 1014
1025 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1026 - $type = $this->tokens[$stackPtr]['type'];
1027 - $closerType = $this->tokens[$scopeCloser]['type'];
1028 - echo str_repeat("\t", $depth);
1029 - echo "=> Found scope closer ($scopeCloser:$closerType) for $stackPtr:$type".PHP_EOL;
1030 - }
1015 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1016 + $type = $this->tokens[ $stackPtr ]['type'];
1017 + $closerType = $this->tokens[ $scopeCloser ]['type'];
1018 + echo str_repeat( "\t", $depth );
1019 + echo "=> Found scope closer ($scopeCloser:$closerType) for $stackPtr:$type" . PHP_EOL;
1020 + }
1031 1021
1032 - $validCloser = true;
1033 - if (($this->tokens[$stackPtr]['code'] === T_IF || $this->tokens[$stackPtr]['code'] === T_ELSEIF)
1034 - && ($tokenType === T_ELSE || $tokenType === T_ELSEIF)
1035 - ) {
1036 - // To be a closer, this token must have an opener.
1037 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1038 - echo str_repeat("\t", $depth);
1039 - echo "* closer needs to be tested *".PHP_EOL;
1040 - }
1022 + $validCloser = true;
1023 + if ( ( $this->tokens[ $stackPtr ]['code'] === T_IF || $this->tokens[ $stackPtr ]['code'] === T_ELSEIF )
1024 + && ( $tokenType === T_ELSE || $tokenType === T_ELSEIF )
1025 + ) {
1026 + // To be a closer, this token must have an opener.
1027 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1028 + echo str_repeat( "\t", $depth );
1029 + echo "* closer needs to be tested *" . PHP_EOL;
1030 + }
1041 1031
1042 - $i = self::recurseScopeMap($i, ($depth + 1), $ignore);
1032 + $i = self::recurseScopeMap( $i, ( $depth + 1 ), $ignore );
1043 1033
1044 - if (isset($this->tokens[$scopeCloser]['scope_opener']) === false) {
1045 - $validCloser = false;
1046 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1047 - echo str_repeat("\t", $depth);
1048 - echo "* closer is not valid (no opener found) *".PHP_EOL;
1049 - }
1050 - } else if ($this->tokens[$this->tokens[$scopeCloser]['scope_opener']]['code'] !== $this->tokens[$opener]['code']) {
1051 - $validCloser = false;
1052 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1053 - echo str_repeat("\t", $depth);
1054 - $type = $this->tokens[$this->tokens[$scopeCloser]['scope_opener']]['type'];
1055 - $openerType = $this->tokens[$opener]['type'];
1056 - echo "* closer is not valid (mismatched opener type; $type != $openerType) *".PHP_EOL;
1057 - }
1058 - } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
1059 - echo str_repeat("\t", $depth);
1060 - echo "* closer was valid *".PHP_EOL;
1061 - }
1062 - } else {
1063 - // The closer was not processed, so we need to
1064 - // complete that token as well.
1065 - $todo[] = $scopeCloser;
1066 - }//end if
1034 + if ( isset( $this->tokens[ $scopeCloser ]['scope_opener'] ) === false ) {
1035 + $validCloser = false;
1036 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1037 + echo str_repeat( "\t", $depth );
1038 + echo "* closer is not valid (no opener found) *" . PHP_EOL;
1039 + }
1040 + } else if ( $this->tokens[ $this->tokens[ $scopeCloser ]['scope_opener'] ]['code'] !== $this->tokens[ $opener ]['code'] ) {
1041 + $validCloser = false;
1042 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1043 + echo str_repeat( "\t", $depth );
1044 + $type = $this->tokens[ $this->tokens[ $scopeCloser ]['scope_opener'] ]['type'];
1045 + $openerType = $this->tokens[ $opener ]['type'];
1046 + echo "* closer is not valid (mismatched opener type; $type != $openerType) *" . PHP_EOL;
1047 + }
1048 + } else if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1049 + echo str_repeat( "\t", $depth );
1050 + echo "* closer was valid *" . PHP_EOL;
1051 + }
1052 + } else {
1053 + // The closer was not processed, so we need to
1054 + // complete that token as well.
1055 + $todo[] = $scopeCloser;
1056 + }//end if
1067 1057
1068 - if ($validCloser === true) {
1069 - foreach ($todo as $token) {
1070 - $this->tokens[$token]['scope_condition'] = $stackPtr;
1071 - $this->tokens[$token]['scope_opener'] = $opener;
1072 - $this->tokens[$token]['scope_closer'] = $scopeCloser;
1073 - }
1058 + if ( $validCloser === true ) {
1059 + foreach ( $todo as $token ) {
1060 + $this->tokens[ $token ]['scope_condition'] = $stackPtr;
1061 + $this->tokens[ $token ]['scope_opener'] = $opener;
1062 + $this->tokens[ $token ]['scope_closer'] = $scopeCloser;
1063 + }
1074 1064
1075 - if ($this->scopeOpeners[$this->tokens[$stackPtr]['code']]['shared'] === true) {
1076 - // As we are going back to where we started originally, restore
1077 - // the ignore value back to its original value.
1078 - $ignore = $originalIgnore;
1079 - return $opener;
1080 - } else if ($scopeCloser === $i
1081 - && isset($this->scopeOpeners[$tokenType]) === true
1082 - ) {
1083 - // Unset scope_condition here or else the token will appear to have
1084 - // already been processed, and it will be skipped. Normally we want that,
1085 - // but in this case, the token is both a closer and an opener, so
1086 - // it needs to act like an opener. This is also why we return the
1087 - // token before this one; so the closer has a chance to be processed
1088 - // a second time, but as an opener.
1089 - unset($this->tokens[$scopeCloser]['scope_condition']);
1090 - return ($i - 1);
1091 - } else {
1092 - return $i;
1093 - }
1094 - } else {
1095 - continue;
1096 - }//end if
1097 - }//end if
1098 - }//end if
1065 + if ( $this->scopeOpeners[ $this->tokens[ $stackPtr ]['code'] ]['shared'] === true ) {
1066 + // As we are going back to where we started originally, restore
1067 + // the ignore value back to its original value.
1068 + $ignore = $originalIgnore;
1099 1069
1100 - // Is this an opening condition ?
1101 - if (isset($this->scopeOpeners[$tokenType]) === true) {
1102 - if ($opener === null) {
1103 - if ($tokenType === T_USE) {
1104 - // PHP use keywords are special because they can be
1105 - // used as blocks but also inline in function definitions.
1106 - // So if we find them nested inside another opener, just skip them.
1107 - continue;
1108 - }
1070 + return $opener;
1071 + } else if ( $scopeCloser === $i
1072 + && isset( $this->scopeOpeners[ $tokenType ] ) === true
1073 + ) {
1074 + // Unset scope_condition here or else the token will appear to have
1075 + // already been processed, and it will be skipped. Normally we want that,
1076 + // but in this case, the token is both a closer and an opener, so
1077 + // it needs to act like an opener. This is also why we return the
1078 + // token before this one; so the closer has a chance to be processed
1079 + // a second time, but as an opener.
1080 + unset( $this->tokens[ $scopeCloser ]['scope_condition'] );
1109 1081
1110 - if ($tokenType === T_FUNCTION
1111 - && $this->tokens[$stackPtr]['code'] !== T_FUNCTION
1112 - ) {
1113 - // Probably a closure, so process it manually.
1114 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1115 - $type = $this->tokens[$stackPtr]['type'];
1116 - echo str_repeat("\t", $depth);
1117 - echo "=> Found function before scope opener for $stackPtr:$type, processing manually".PHP_EOL;
1118 - }
1082 + return ( $i - 1 );
1083 + } else {
1084 + return $i;
1085 + }
1086 + } else {
1087 + continue;
1088 + }//end if
1089 + }//end if
1090 + }//end if
1119 1091
1120 - if (isset($this->tokens[$i]['scope_closer']) === true) {
1121 - // We've already processed this closure.
1122 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1123 - echo str_repeat("\t", $depth);
1124 - echo '* already processed, skipping *'.PHP_EOL;
1125 - }
1092 + // Is this an opening condition ?
1093 + if ( isset( $this->scopeOpeners[ $tokenType ] ) === true ) {
1094 + if ( $opener === null ) {
1095 + if ( $tokenType === T_USE ) {
1096 + // PHP use keywords are special because they can be
1097 + // used as blocks but also inline in function definitions.
1098 + // So if we find them nested inside another opener, just skip them.
1099 + continue;
1100 + }
1126 1101
1127 - $i = $this->tokens[$i]['scope_closer'];
1128 - continue;
1129 - }
1102 + if ( $tokenType === T_FUNCTION
1103 + && $this->tokens[ $stackPtr ]['code'] !== T_FUNCTION
1104 + ) {
1105 + // Probably a closure, so process it manually.
1106 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1107 + $type = $this->tokens[ $stackPtr ]['type'];
1108 + echo str_repeat( "\t", $depth );
1109 + echo "=> Found function before scope opener for $stackPtr:$type, processing manually" . PHP_EOL;
1110 + }
1130 1111
1131 - $i = self::recurseScopeMap($i, ($depth + 1), $ignore);
1132 - continue;
1133 - }//end if
1112 + if ( isset( $this->tokens[ $i ]['scope_closer'] ) === true ) {
1113 + // We've already processed this closure.
1114 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1115 + echo str_repeat( "\t", $depth );
1116 + echo '* already processed, skipping *' . PHP_EOL;
1117 + }
1134 1118
1135 - if ($tokenType === T_CLASS) {
1136 - // Probably an anonymous class inside another anonymous class,
1137 - // so process it manually.
1138 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1139 - $type = $this->tokens[$stackPtr]['type'];
1140 - echo str_repeat("\t", $depth);
1141 - echo "=> Found class before scope opener for $stackPtr:$type, processing manually".PHP_EOL;
1142 - }
1119 + $i = $this->tokens[ $i ]['scope_closer'];
1120 + continue;
1121 + }
1143 1122
1144 - if (isset($this->tokens[$i]['scope_closer']) === true) {
1145 - // We've already processed this anon class.
1146 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1147 - echo str_repeat("\t", $depth);
1148 - echo '* already processed, skipping *'.PHP_EOL;
1149 - }
1123 + $i = self::recurseScopeMap( $i, ( $depth + 1 ), $ignore );
1124 + continue;
1125 + }//end if
1150 1126
1151 - $i = $this->tokens[$i]['scope_closer'];
1152 - continue;
1153 - }
1127 + if ( $tokenType === T_CLASS ) {
1128 + // Probably an anonymous class inside another anonymous class,
1129 + // so process it manually.
1130 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1131 + $type = $this->tokens[ $stackPtr ]['type'];
1132 + echo str_repeat( "\t", $depth );
1133 + echo "=> Found class before scope opener for $stackPtr:$type, processing manually" . PHP_EOL;
1134 + }
1154 1135
1155 - $i = self::recurseScopeMap($i, ($depth + 1), $ignore);
1156 - continue;
1157 - }//end if
1136 + if ( isset( $this->tokens[ $i ]['scope_closer'] ) === true ) {
1137 + // We've already processed this anon class.
1138 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1139 + echo str_repeat( "\t", $depth );
1140 + echo '* already processed, skipping *' . PHP_EOL;
1141 + }
1158 1142
1159 - // Found another opening condition but still haven't
1160 - // found our opener, so we are never going to find one.
1161 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1162 - $type = $this->tokens[$stackPtr]['type'];
1163 - echo str_repeat("\t", $depth);
1164 - echo "=> Found new opening condition before scope opener for $stackPtr:$type, ";
1165 - }
1143 + $i = $this->tokens[ $i ]['scope_closer'];
1144 + continue;
1145 + }
1166 1146
1167 - if (($this->tokens[$stackPtr]['code'] === T_IF
1168 - || $this->tokens[$stackPtr]['code'] === T_ELSEIF
1169 - || $this->tokens[$stackPtr]['code'] === T_ELSE)
1170 - && ($this->tokens[$i]['code'] === T_ELSE
1171 - || $this->tokens[$i]['code'] === T_ELSEIF)
1172 - ) {
1173 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1174 - echo "continuing".PHP_EOL;
1175 - }
1147 + $i = self::recurseScopeMap( $i, ( $depth + 1 ), $ignore );
1148 + continue;
1149 + }//end if
1176 1150
1177 - return ($i - 1);
1178 - } else {
1179 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1180 - echo "backtracking".PHP_EOL;
1181 - }
1151 + // Found another opening condition but still haven't
1152 + // found our opener, so we are never going to find one.
1153 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1154 + $type = $this->tokens[ $stackPtr ]['type'];
1155 + echo str_repeat( "\t", $depth );
1156 + echo "=> Found new opening condition before scope opener for $stackPtr:$type, ";
1157 + }
1182 1158
1183 - return $stackPtr;
1184 - }
1185 - }//end if
1159 + if ( ( $this->tokens[ $stackPtr ]['code'] === T_IF
1160 + || $this->tokens[ $stackPtr ]['code'] === T_ELSEIF
1161 + || $this->tokens[ $stackPtr ]['code'] === T_ELSE )
1162 + && ( $this->tokens[ $i ]['code'] === T_ELSE
1163 + || $this->tokens[ $i ]['code'] === T_ELSEIF )
1164 + ) {
1165 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1166 + echo "continuing" . PHP_EOL;
1167 + }
1186 1168
1187 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1188 - echo str_repeat("\t", $depth);
1189 - echo '* token is an opening condition *'.PHP_EOL;
1190 - }
1169 + return ( $i - 1 );
1170 + } else {
1171 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1172 + echo "backtracking" . PHP_EOL;
1173 + }
1191 1174
1192 - $isShared = ($this->scopeOpeners[$tokenType]['shared'] === true);
1175 + return $stackPtr;
1176 + }
1177 + }//end if
1193 1178
1194 - if (isset($this->tokens[$i]['scope_condition']) === true) {
1195 - // We've been here before.
1196 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1197 - echo str_repeat("\t", $depth);
1198 - echo '* already processed, skipping *'.PHP_EOL;
1199 - }
1179 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1180 + echo str_repeat( "\t", $depth );
1181 + echo '* token is an opening condition *' . PHP_EOL;
1182 + }
1200 1183
1201 - if ($isShared === false
1202 - && isset($this->tokens[$i]['scope_closer']) === true
1203 - ) {
1204 - $i = $this->tokens[$i]['scope_closer'];
1205 - }
1184 + $isShared = ( $this->scopeOpeners[ $tokenType ]['shared'] === true );
1206 1185
1207 - continue;
1208 - } else if ($currType === $tokenType
1209 - && $isShared === false
1210 - && $opener === null
1211 - ) {
1212 - // We haven't yet found our opener, but we have found another
1213 - // scope opener which is the same type as us, and we don't
1214 - // share openers, so we will never find one.
1215 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1216 - echo str_repeat("\t", $depth);
1217 - echo '* it was another token\'s opener, bailing *'.PHP_EOL;
1218 - }
1186 + if ( isset( $this->tokens[ $i ]['scope_condition'] ) === true ) {
1187 + // We've been here before.
1188 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1189 + echo str_repeat( "\t", $depth );
1190 + echo '* already processed, skipping *' . PHP_EOL;
1191 + }
1219 1192
1220 - return $stackPtr;
1221 - } else {
1222 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1223 - echo str_repeat("\t", $depth);
1224 - echo '* searching for opener *'.PHP_EOL;
1225 - }
1193 + if ( $isShared === false
1194 + && isset( $this->tokens[ $i ]['scope_closer'] ) === true
1195 + ) {
1196 + $i = $this->tokens[ $i ]['scope_closer'];
1197 + }
1226 1198
1227 - if (isset($this->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
1228 - $oldIgnore = $ignore;
1229 - $ignore = 0;
1230 - }
1199 + continue;
1200 + } else if ( $currType === $tokenType
1201 + && $isShared === false
1202 + && $opener === null
1203 + ) {
1204 + // We haven't yet found our opener, but we have found another
1205 + // scope opener which is the same type as us, and we don't
1206 + // share openers, so we will never find one.
1207 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1208 + echo str_repeat( "\t", $depth );
1209 + echo '* it was another token\'s opener, bailing *' . PHP_EOL;
1210 + }
1231 1211
1232 - // PHP has a max nesting level for functions. Stop before we hit that limit
1233 - // because too many loops means we've run into trouble anyway.
1234 - if ($depth > 50) {
1235 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1236 - echo str_repeat("\t", $depth);
1237 - echo '* reached maximum nesting level; aborting *'.PHP_EOL;
1238 - }
1212 + return $stackPtr;
1213 + } else {
1214 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1215 + echo str_repeat( "\t", $depth );
1216 + echo '* searching for opener *' . PHP_EOL;
1217 + }
1239 1218
1240 - throw new TokenizerException('Maximum nesting level reached; file could not be processed');
1241 - }
1219 + if ( isset( $this->scopeOpeners[ $tokenType ]['end'][ T_CLOSE_CURLY_BRACKET ] ) === true ) {
1220 + $oldIgnore = $ignore;
1221 + $ignore = 0;
1222 + }
1242 1223
1243 - $oldDepth = $depth;
1244 - if ($isShared === true
1245 - && isset($this->scopeOpeners[$tokenType]['with'][$currType]) === true
1246 - ) {
1247 - // Don't allow the depth to increment because this is
1248 - // possibly not a true nesting if we are sharing our closer.
1249 - // This can happen, for example, when a SWITCH has a large
1250 - // number of CASE statements with the same shared BREAK.
1251 - $depth--;
1252 - }
1224 + // PHP has a max nesting level for functions. Stop before we hit that limit
1225 + // because too many loops means we've run into trouble anyway.
1226 + if ( $depth > 50 ) {
1227 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1228 + echo str_repeat( "\t", $depth );
1229 + echo '* reached maximum nesting level; aborting *' . PHP_EOL;
1230 + }
1253 1231
1254 - $i = self::recurseScopeMap($i, ($depth + 1), $ignore);
1255 - $depth = $oldDepth;
1232 + throw new TokenizerException( 'Maximum nesting level reached; file could not be processed' );
1233 + }
1256 1234
1257 - if (isset($this->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
1258 - $ignore = $oldIgnore;
1259 - }
1260 - }//end if
1261 - }//end if
1235 + $oldDepth = $depth;
1236 + if ( $isShared === true
1237 + && isset( $this->scopeOpeners[ $tokenType ]['with'][ $currType ] ) === true
1238 + ) {
1239 + // Don't allow the depth to increment because this is
1240 + // possibly not a true nesting if we are sharing our closer.
1241 + // This can happen, for example, when a SWITCH has a large
1242 + // number of CASE statements with the same shared BREAK.
1243 + $depth --;
1244 + }
1262 1245
1263 - if (isset($this->scopeOpeners[$currType]['start'][$tokenType]) === true
1264 - && $opener === null
1265 - ) {
1266 - if ($tokenType === T_OPEN_CURLY_BRACKET) {
1267 - if (isset($this->tokens[$stackPtr]['parenthesis_closer']) === true
1268 - && $i < $this->tokens[$stackPtr]['parenthesis_closer']
1269 - ) {
1270 - // We found a curly brace inside the condition of the
1271 - // current scope opener, so it must be a string offset.
1272 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1273 - echo str_repeat("\t", $depth);
1274 - echo '* ignoring curly brace inside condition *'.PHP_EOL;
1275 - }
1246 + $i = self::recurseScopeMap( $i, ( $depth + 1 ), $ignore );
1247 + $depth = $oldDepth;
1276 1248
1277 - $ignore++;
1278 - } else {
1279 - // Make sure this is actually an opener and not a
1280 - // string offset (e.g., $var{0}).
1281 - for ($x = ($i - 1); $x > 0; $x--) {
1282 - if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === true) {
1283 - continue;
1284 - } else {
1285 - // If the first non-whitespace/comment token looks like this
1286 - // brace is a string offset, or this brace is mid-way through
1287 - // a new statement, it isn't a scope opener.
1288 - $disallowed = Util\Tokens::$assignmentTokens;
1289 - $disallowed += [
1290 - T_DOLLAR => true,
1291 - T_VARIABLE => true,
1292 - T_OBJECT_OPERATOR => true,
1293 - T_COMMA => true,
1294 - T_OPEN_PARENTHESIS => true,
1295 - ];
1249 + if ( isset( $this->scopeOpeners[ $tokenType ]['end'][ T_CLOSE_CURLY_BRACKET ] ) === true ) {
1250 + $ignore = $oldIgnore;
1251 + }
1252 + }//end if
1253 + }//end if
1296 1254
1297 - if (isset($disallowed[$this->tokens[$x]['code']]) === true) {
1298 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1299 - echo str_repeat("\t", $depth);
1300 - echo '* ignoring curly brace *'.PHP_EOL;
1301 - }
1255 + if ( isset( $this->scopeOpeners[ $currType ]['start'][ $tokenType ] ) === true
1256 + && $opener === null
1257 + ) {
1258 + if ( $tokenType === T_OPEN_CURLY_BRACKET ) {
1259 + if ( isset( $this->tokens[ $stackPtr ]['parenthesis_closer'] ) === true
1260 + && $i < $this->tokens[ $stackPtr ]['parenthesis_closer']
1261 + ) {
1262 + // We found a curly brace inside the condition of the
1263 + // current scope opener, so it must be a string offset.
1264 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1265 + echo str_repeat( "\t", $depth );
1266 + echo '* ignoring curly brace inside condition *' . PHP_EOL;
1267 + }
1302 1268
1303 - $ignore++;
1304 - }
1269 + $ignore ++;
1270 + } else {
1271 + // Make sure this is actually an opener and not a
1272 + // string offset (e.g., $var{0}).
1273 + for ( $x = ( $i - 1 ); $x > 0; $x -- ) {
1274 + if ( isset( Util\Tokens::$emptyTokens[ $this->tokens[ $x ]['code'] ] ) === true ) {
1275 + continue;
1276 + } else {
1277 + // If the first non-whitespace/comment token looks like this
1278 + // brace is a string offset, or this brace is mid-way through
1279 + // a new statement, it isn't a scope opener.
1280 + $disallowed = Util\Tokens::$assignmentTokens;
1281 + $disallowed += [
1282 + T_DOLLAR => true,
1283 + T_VARIABLE => true,
1284 + T_OBJECT_OPERATOR => true,
1285 + T_COMMA => true,
1286 + T_OPEN_PARENTHESIS => true,
1287 + ];
1305 1288
1306 - break;
1307 - }//end if
1308 - }//end for
1309 - }//end if
1310 - }//end if
1289 + if ( isset( $disallowed[ $this->tokens[ $x ]['code'] ] ) === true ) {
1290 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1291 + echo str_repeat( "\t", $depth );
1292 + echo '* ignoring curly brace *' . PHP_EOL;
1293 + }
1311 1294
1312 - if ($ignore === 0 || $tokenType !== T_OPEN_CURLY_BRACKET) {
1313 - // We found the opening scope token for $currType.
1314 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1315 - $type = $this->tokens[$stackPtr]['type'];
1316 - echo str_repeat("\t", $depth);
1317 - echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
1318 - }
1295 + $ignore ++;
1296 + }
1319 1297
1320 - $opener = $i;
1321 - }
1322 - } else if ($tokenType === T_SEMICOLON
1323 - && $opener === null
1324 - && (isset($this->tokens[$stackPtr]['parenthesis_closer']) === false
1325 - || $i > $this->tokens[$stackPtr]['parenthesis_closer'])
1326 - ) {
1327 - // Found the end of a statement but still haven't
1328 - // found our opener, so we are never going to find one.
1329 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1330 - $type = $this->tokens[$stackPtr]['type'];
1331 - echo str_repeat("\t", $depth);
1332 - echo "=> Found end of statement before scope opener for $stackPtr:$type, continuing".PHP_EOL;
1333 - }
1298 + break;
1299 + }//end if
1300 + }//end for
1301 + }//end if
1302 + }//end if
1334 1303
1335 - return ($i - 1);
1336 - } else if ($tokenType === T_OPEN_PARENTHESIS) {
1337 - if (isset($this->tokens[$i]['parenthesis_owner']) === true) {
1338 - $owner = $this->tokens[$i]['parenthesis_owner'];
1339 - if (isset(Util\Tokens::$scopeOpeners[$this->tokens[$owner]['code']]) === true
1340 - && isset($this->tokens[$i]['parenthesis_closer']) === true
1341 - ) {
1342 - // If we get into here, then we opened a parenthesis for
1343 - // a scope (eg. an if or else if) so we need to update the
1344 - // start of the line so that when we check to see
1345 - // if the closing parenthesis is more than n lines away from
1346 - // the statement, we check from the closing parenthesis.
1347 - $startLine = $this->tokens[$this->tokens[$i]['parenthesis_closer']]['line'];
1348 - }
1349 - }
1350 - } else if ($tokenType === T_OPEN_CURLY_BRACKET && $opener !== null) {
1351 - // We opened something that we don't have a scope opener for.
1352 - // Examples of this are curly brackets for string offsets etc.
1353 - // We want to ignore this so that we don't have an invalid scope
1354 - // map.
1355 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1356 - echo str_repeat("\t", $depth);
1357 - echo '* ignoring curly brace *'.PHP_EOL;
1358 - }
1304 + if ( $ignore === 0 || $tokenType !== T_OPEN_CURLY_BRACKET ) {
1305 + // We found the opening scope token for $currType.
1306 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1307 + $type = $this->tokens[ $stackPtr ]['type'];
1308 + echo str_repeat( "\t", $depth );
1309 + echo "=> Found scope opener for $stackPtr:$type" . PHP_EOL;
1310 + }
1359 1311
1360 - $ignore++;
1361 - } else if ($tokenType === T_CLOSE_CURLY_BRACKET && $ignore > 0) {
1362 - // We found the end token for the opener we were ignoring.
1363 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1364 - echo str_repeat("\t", $depth);
1365 - echo '* finished ignoring curly brace *'.PHP_EOL;
1366 - }
1312 + $opener = $i;
1313 + }
1314 + } else if ( $tokenType === T_SEMICOLON
1315 + && $opener === null
1316 + && ( isset( $this->tokens[ $stackPtr ]['parenthesis_closer'] ) === false
1317 + || $i > $this->tokens[ $stackPtr ]['parenthesis_closer'] )
1318 + ) {
1319 + // Found the end of a statement but still haven't
1320 + // found our opener, so we are never going to find one.
1321 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1322 + $type = $this->tokens[ $stackPtr ]['type'];
1323 + echo str_repeat( "\t", $depth );
1324 + echo "=> Found end of statement before scope opener for $stackPtr:$type, continuing" . PHP_EOL;
1325 + }
1367 1326
1368 - $ignore--;
1369 - } else if ($opener === null
1370 - && isset($this->scopeOpeners[$currType]) === true
1371 - ) {
1372 - // If we still haven't found the opener after 30 lines,
1373 - // we're not going to find it, unless we know it requires
1374 - // an opener (in which case we better keep looking) or the last
1375 - // token was empty (in which case we'll just confirm there is
1376 - // more code in this file and not just a big comment).
1377 - if ($this->tokens[$i]['line'] >= ($startLine + 30)
1378 - && isset(Util\Tokens::$emptyTokens[$this->tokens[($i - 1)]['code']]) === false
1379 - ) {
1380 - if ($this->scopeOpeners[$currType]['strict'] === true) {
1381 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1382 - $type = $this->tokens[$stackPtr]['type'];
1383 - $lines = ($this->tokens[$i]['line'] - $startLine);
1384 - echo str_repeat("\t", $depth);
1385 - echo "=> Still looking for $stackPtr:$type scope opener after $lines lines".PHP_EOL;
1386 - }
1387 - } else {
1388 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1389 - $type = $this->tokens[$stackPtr]['type'];
1390 - echo str_repeat("\t", $depth);
1391 - echo "=> Couldn't find scope opener for $stackPtr:$type, bailing".PHP_EOL;
1392 - }
1327 + return ( $i - 1 );
1328 + } else if ( $tokenType === T_OPEN_PARENTHESIS ) {
1329 + if ( isset( $this->tokens[ $i ]['parenthesis_owner'] ) === true ) {
1330 + $owner = $this->tokens[ $i ]['parenthesis_owner'];
1331 + if ( isset( Util\Tokens::$scopeOpeners[ $this->tokens[ $owner ]['code'] ] ) === true
1332 + && isset( $this->tokens[ $i ]['parenthesis_closer'] ) === true
1333 + ) {
1334 + // If we get into here, then we opened a parenthesis for
1335 + // a scope (eg. an if or else if) so we need to update the
1336 + // start of the line so that when we check to see
1337 + // if the closing parenthesis is more than n lines away from
1338 + // the statement, we check from the closing parenthesis.
1339 + $startLine = $this->tokens[ $this->tokens[ $i ]['parenthesis_closer'] ]['line'];
1340 + }
1341 + }
1342 + } else if ( $tokenType === T_OPEN_CURLY_BRACKET && $opener !== null ) {
1343 + // We opened something that we don't have a scope opener for.
1344 + // Examples of this are curly brackets for string offsets etc.
1345 + // We want to ignore this so that we don't have an invalid scope
1346 + // map.
1347 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1348 + echo str_repeat( "\t", $depth );
1349 + echo '* ignoring curly brace *' . PHP_EOL;
1350 + }
1393 1351
1394 - return $stackPtr;
1395 - }
1396 - }
1397 - } else if ($opener !== null
1398 - && $tokenType !== T_BREAK
1399 - && isset($this->endScopeTokens[$tokenType]) === true
1400 - ) {
1401 - if (isset($this->tokens[$i]['scope_condition']) === false) {
1402 - if ($ignore > 0) {
1403 - // We found the end token for the opener we were ignoring.
1404 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1405 - echo str_repeat("\t", $depth);
1406 - echo '* finished ignoring curly brace *'.PHP_EOL;
1407 - }
1352 + $ignore ++;
1353 + } else if ( $tokenType === T_CLOSE_CURLY_BRACKET && $ignore > 0 ) {
1354 + // We found the end token for the opener we were ignoring.
1355 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1356 + echo str_repeat( "\t", $depth );
1357 + echo '* finished ignoring curly brace *' . PHP_EOL;
1358 + }
1408 1359
1409 - $ignore--;
1410 - } else {
1411 - // We found a token that closes the scope but it doesn't
1412 - // have a condition, so it belongs to another token and
1413 - // our token doesn't have a closer, so pretend this is
1414 - // the closer.
1415 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1416 - $type = $this->tokens[$stackPtr]['type'];
1417 - echo str_repeat("\t", $depth);
1418 - echo "=> Found (unexpected) scope closer for $stackPtr:$type".PHP_EOL;
1419 - }
1360 + $ignore --;
1361 + } else if ( $opener === null
1362 + && isset( $this->scopeOpeners[ $currType ] ) === true
1363 + ) {
1364 + // If we still haven't found the opener after 30 lines,
1365 + // we're not going to find it, unless we know it requires
1366 + // an opener (in which case we better keep looking) or the last
1367 + // token was empty (in which case we'll just confirm there is
1368 + // more code in this file and not just a big comment).
1369 + if ( $this->tokens[ $i ]['line'] >= ( $startLine + 30 )
1370 + && isset( Util\Tokens::$emptyTokens[ $this->tokens[ ( $i - 1 ) ]['code'] ] ) === false
1371 + ) {
1372 + if ( $this->scopeOpeners[ $currType ]['strict'] === true ) {
1373 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1374 + $type = $this->tokens[ $stackPtr ]['type'];
1375 + $lines = ( $this->tokens[ $i ]['line'] - $startLine );
1376 + echo str_repeat( "\t", $depth );
1377 + echo "=> Still looking for $stackPtr:$type scope opener after $lines lines" . PHP_EOL;
1378 + }
1379 + } else {
1380 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1381 + $type = $this->tokens[ $stackPtr ]['type'];
1382 + echo str_repeat( "\t", $depth );
1383 + echo "=> Couldn't find scope opener for $stackPtr:$type, bailing" . PHP_EOL;
1384 + }
1420 1385
1421 - foreach ([$stackPtr, $opener] as $token) {
1422 - $this->tokens[$token]['scope_condition'] = $stackPtr;
1423 - $this->tokens[$token]['scope_opener'] = $opener;
1424 - $this->tokens[$token]['scope_closer'] = $i;
1425 - }
1386 + return $stackPtr;
1387 + }
1388 + }
1389 + } else if ( $opener !== null
1390 + && $tokenType !== T_BREAK
1391 + && isset( $this->endScopeTokens[ $tokenType ] ) === true
1392 + ) {
1393 + if ( isset( $this->tokens[ $i ]['scope_condition'] ) === false ) {
1394 + if ( $ignore > 0 ) {
1395 + // We found the end token for the opener we were ignoring.
1396 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1397 + echo str_repeat( "\t", $depth );
1398 + echo '* finished ignoring curly brace *' . PHP_EOL;
1399 + }
1426 1400
1427 - return ($i - 1);
1428 - }//end if
1429 - }//end if
1430 - }//end if
1431 - }//end for
1401 + $ignore --;
1402 + } else {
1403 + // We found a token that closes the scope but it doesn't
1404 + // have a condition, so it belongs to another token and
1405 + // our token doesn't have a closer, so pretend this is
1406 + // the closer.
1407 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1408 + $type = $this->tokens[ $stackPtr ]['type'];
1409 + echo str_repeat( "\t", $depth );
1410 + echo "=> Found (unexpected) scope closer for $stackPtr:$type" . PHP_EOL;
1411 + }
1432 1412
1433 - return $stackPtr;
1413 + foreach ( [ $stackPtr, $opener ] as $token ) {
1414 + $this->tokens[ $token ]['scope_condition'] = $stackPtr;
1415 + $this->tokens[ $token ]['scope_opener'] = $opener;
1416 + $this->tokens[ $token ]['scope_closer'] = $i;
1417 + }
1434 1418
1435 - }//end recurseScopeMap()
1419 + return ( $i - 1 );
1420 + }//end if
1421 + }//end if
1422 + }//end if
1423 + }//end for
1436 1424
1425 + return $stackPtr;
1437 1426
1438 - /**
1439 - * Constructs the level map.
1440 - *
1441 - * The level map adds a 'level' index to each token which indicates the
1442 - * depth that a token within a set of scope blocks. It also adds a
1443 - * 'conditions' index which is an array of the scope conditions that opened
1444 - * each of the scopes - position 0 being the first scope opener.
1445 - *
1446 - * @return void
1447 - */
1448 - private function createLevelMap()
1449 - {
1450 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1451 - echo "\t*** START LEVEL MAP ***".PHP_EOL;
1452 - }
1427 + }//end recurseScopeMap()
1453 1428
1454 - $this->numTokens = count($this->tokens);
1455 - $level = 0;
1456 - $conditions = [];
1457 - $lastOpener = null;
1458 - $openers = [];
1459 1429
1460 - for ($i = 0; $i < $this->numTokens; $i++) {
1461 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1462 - $type = $this->tokens[$i]['type'];
1463 - $line = $this->tokens[$i]['line'];
1464 - $len = $this->tokens[$i]['length'];
1465 - $col = $this->tokens[$i]['column'];
1430 + /**
1431 + * Constructs the level map.
1432 + *
1433 + * The level map adds a 'level' index to each token which indicates the
1434 + * depth that a token within a set of scope blocks. It also adds a
1435 + * 'conditions' index which is an array of the scope conditions that opened
1436 + * each of the scopes - position 0 being the first scope opener.
1437 + *
1438 + * @return void
1439 + */
1440 + private function createLevelMap() {
1441 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1442 + echo "\t*** START LEVEL MAP ***" . PHP_EOL;
1443 + }
1466 1444
1467 - $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
1445 + $this->numTokens = count( $this->tokens );
1446 + $level = 0;
1447 + $conditions = [];
1448 + $lastOpener = null;
1449 + $openers = [];
1468 1450
1469 - echo str_repeat("\t", ($level + 1));
1470 - echo "Process token $i on line $line [col:$col;len:$len;lvl:$level;";
1471 - if (empty($conditions) !== true) {
1472 - $conditionString = 'conds;';
1473 - foreach ($conditions as $condition) {
1474 - $conditionString .= Util\Tokens::tokenName($condition).',';
1475 - }
1451 + for ( $i = 0; $i < $this->numTokens; $i ++ ) {
1452 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1453 + $type = $this->tokens[ $i ]['type'];
1454 + $line = $this->tokens[ $i ]['line'];
1455 + $len = $this->tokens[ $i ]['length'];
1456 + $col = $this->tokens[ $i ]['column'];
1476 1457
1477 - echo rtrim($conditionString, ',').';';
1478 - }
1458 + $content = Util\Common::prepareForOutput( $this->tokens[ $i ]['content'] );
1479 1459
1480 - echo "]: $type => $content".PHP_EOL;
1481 - }//end if
1460 + echo str_repeat( "\t", ( $level + 1 ) );
1461 + echo "Process token $i on line $line [col:$col;len:$len;lvl:$level;";
1462 + if ( empty( $conditions ) !== true ) {
1463 + $conditionString = 'conds;';
1464 + foreach ( $conditions as $condition ) {
1465 + $conditionString .= Util\Tokens::tokenName( $condition ) . ',';
1466 + }
1482 1467
1483 - $this->tokens[$i]['level'] = $level;
1484 - $this->tokens[$i]['conditions'] = $conditions;
1468 + echo rtrim( $conditionString, ',' ) . ';';
1469 + }
1485 1470
1486 - if (isset($this->tokens[$i]['scope_condition']) === true) {
1487 - // Check to see if this token opened the scope.
1488 - if ($this->tokens[$i]['scope_opener'] === $i) {
1489 - $stackPtr = $this->tokens[$i]['scope_condition'];
1490 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1491 - $type = $this->tokens[$stackPtr]['type'];
1492 - echo str_repeat("\t", ($level + 1));
1493 - echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
1494 - }
1471 + echo "]: $type => $content" . PHP_EOL;
1472 + }//end if
1495 1473
1496 - $stackPtr = $this->tokens[$i]['scope_condition'];
1474 + $this->tokens[ $i ]['level'] = $level;
1475 + $this->tokens[ $i ]['conditions'] = $conditions;
1497 1476
1498 - // If we find a scope opener that has a shared closer,
1499 - // then we need to go back over the condition map that we
1500 - // just created and fix ourselves as we just added some
1501 - // conditions where there was none. This happens for T_CASE
1502 - // statements that are using the same break statement.
1503 - if ($lastOpener !== null && $this->tokens[$lastOpener]['scope_closer'] === $this->tokens[$i]['scope_closer']) {
1504 - // This opener shares its closer with the previous opener,
1505 - // but we still need to check if the two openers share their
1506 - // closer with each other directly (like CASE and DEFAULT)
1507 - // or if they are just sharing because one doesn't have a
1508 - // closer (like CASE with no BREAK using a SWITCHes closer).
1509 - $thisType = $this->tokens[$this->tokens[$i]['scope_condition']]['code'];
1510 - $opener = $this->tokens[$lastOpener]['scope_condition'];
1477 + if ( isset( $this->tokens[ $i ]['scope_condition'] ) === true ) {
1478 + // Check to see if this token opened the scope.
1479 + if ( $this->tokens[ $i ]['scope_opener'] === $i ) {
1480 + $stackPtr = $this->tokens[ $i ]['scope_condition'];
1481 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1482 + $type = $this->tokens[ $stackPtr ]['type'];
1483 + echo str_repeat( "\t", ( $level + 1 ) );
1484 + echo "=> Found scope opener for $stackPtr:$type" . PHP_EOL;
1485 + }
1511 1486
1512 - $isShared = isset($this->scopeOpeners[$thisType]['with'][$this->tokens[$opener]['code']]);
1487 + $stackPtr = $this->tokens[ $i ]['scope_condition'];
1513 1488
1514 - reset($this->scopeOpeners[$thisType]['end']);
1515 - reset($this->scopeOpeners[$this->tokens[$opener]['code']]['end']);
1516 - $sameEnd = (current($this->scopeOpeners[$thisType]['end']) === current($this->scopeOpeners[$this->tokens[$opener]['code']]['end']));
1489 + // If we find a scope opener that has a shared closer,
1490 + // then we need to go back over the condition map that we
1491 + // just created and fix ourselves as we just added some
1492 + // conditions where there was none. This happens for T_CASE
1493 + // statements that are using the same break statement.
1494 + if ( $lastOpener !== null && $this->tokens[ $lastOpener ]['scope_closer'] === $this->tokens[ $i ]['scope_closer'] ) {
1495 + // This opener shares its closer with the previous opener,
1496 + // but we still need to check if the two openers share their
1497 + // closer with each other directly (like CASE and DEFAULT)
1498 + // or if they are just sharing because one doesn't have a
1499 + // closer (like CASE with no BREAK using a SWITCHes closer).
1500 + $thisType = $this->tokens[ $this->tokens[ $i ]['scope_condition'] ]['code'];
1501 + $opener = $this->tokens[ $lastOpener ]['scope_condition'];
1517 1502
1518 - if ($isShared === true && $sameEnd === true) {
1519 - $badToken = $opener;
1520 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1521 - $type = $this->tokens[$badToken]['type'];
1522 - echo str_repeat("\t", ($level + 1));
1523 - echo "* shared closer, cleaning up $badToken:$type *".PHP_EOL;
1524 - }
1503 + $isShared = isset( $this->scopeOpeners[ $thisType ]['with'][ $this->tokens[ $opener ]['code'] ] );
1525 1504
1526 - for ($x = $this->tokens[$i]['scope_condition']; $x <= $i; $x++) {
1527 - $oldConditions = $this->tokens[$x]['conditions'];
1528 - $oldLevel = $this->tokens[$x]['level'];
1529 - $this->tokens[$x]['level']--;
1530 - unset($this->tokens[$x]['conditions'][$badToken]);
1531 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1532 - $type = $this->tokens[$x]['type'];
1533 - $oldConds = '';
1534 - foreach ($oldConditions as $condition) {
1535 - $oldConds .= Util\Tokens::tokenName($condition).',';
1536 - }
1505 + reset( $this->scopeOpeners[ $thisType ]['end'] );
1506 + reset( $this->scopeOpeners[ $this->tokens[ $opener ]['code'] ]['end'] );
1507 + $sameEnd = ( current( $this->scopeOpeners[ $thisType ]['end'] ) === current( $this->scopeOpeners[ $this->tokens[ $opener ]['code'] ]['end'] ) );
1537 1508
1538 - $oldConds = rtrim($oldConds, ',');
1509 + if ( $isShared === true && $sameEnd === true ) {
1510 + $badToken = $opener;
1511 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1512 + $type = $this->tokens[ $badToken ]['type'];
1513 + echo str_repeat( "\t", ( $level + 1 ) );
1514 + echo "* shared closer, cleaning up $badToken:$type *" . PHP_EOL;
1515 + }
1539 1516
1540 - $newConds = '';
1541 - foreach ($this->tokens[$x]['conditions'] as $condition) {
1542 - $newConds .= Util\Tokens::tokenName($condition).',';
1543 - }
1517 + for ( $x = $this->tokens[ $i ]['scope_condition']; $x <= $i; $x ++ ) {
1518 + $oldConditions = $this->tokens[ $x ]['conditions'];
1519 + $oldLevel = $this->tokens[ $x ]['level'];
1520 + $this->tokens[ $x ]['level'] --;
1521 + unset( $this->tokens[ $x ]['conditions'][ $badToken ] );
1522 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1523 + $type = $this->tokens[ $x ]['type'];
1524 + $oldConds = '';
1525 + foreach ( $oldConditions as $condition ) {
1526 + $oldConds .= Util\Tokens::tokenName( $condition ) . ',';
1527 + }
1544 1528
1545 - $newConds = rtrim($newConds, ',');
1529 + $oldConds = rtrim( $oldConds, ',' );
1546 1530
1547 - $newLevel = $this->tokens[$x]['level'];
1548 - echo str_repeat("\t", ($level + 1));
1549 - echo "* cleaned $x:$type *".PHP_EOL;
1550 - echo str_repeat("\t", ($level + 2));
1551 - echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
1552 - echo str_repeat("\t", ($level + 2));
1553 - echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
1554 - }//end if
1555 - }//end for
1531 + $newConds = '';
1532 + foreach ( $this->tokens[ $x ]['conditions'] as $condition ) {
1533 + $newConds .= Util\Tokens::tokenName( $condition ) . ',';
1534 + }
1556 1535
1557 - unset($conditions[$badToken]);
1558 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1559 - $type = $this->tokens[$badToken]['type'];
1560 - echo str_repeat("\t", ($level + 1));
1561 - echo "* token $badToken:$type removed from conditions array *".PHP_EOL;
1562 - }
1536 + $newConds = rtrim( $newConds, ',' );
1563 1537
1564 - unset($openers[$lastOpener]);
1538 + $newLevel = $this->tokens[ $x ]['level'];
1539 + echo str_repeat( "\t", ( $level + 1 ) );
1540 + echo "* cleaned $x:$type *" . PHP_EOL;
1541 + echo str_repeat( "\t", ( $level + 2 ) );
1542 + echo "=> level changed from $oldLevel to $newLevel" . PHP_EOL;
1543 + echo str_repeat( "\t", ( $level + 2 ) );
1544 + echo "=> conditions changed from $oldConds to $newConds" . PHP_EOL;
1545 + }//end if
1546 + }//end for
1565 1547
1566 - $level--;
1567 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1568 - echo str_repeat("\t", ($level + 2));
1569 - echo '* level decreased *'.PHP_EOL;
1570 - }
1571 - }//end if
1572 - }//end if
1548 + unset( $conditions[ $badToken ] );
1549 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1550 + $type = $this->tokens[ $badToken ]['type'];
1551 + echo str_repeat( "\t", ( $level + 1 ) );
1552 + echo "* token $badToken:$type removed from conditions array *" . PHP_EOL;
1553 + }
1573 1554
1574 - $level++;
1575 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1576 - echo str_repeat("\t", ($level + 1));
1577 - echo '* level increased *'.PHP_EOL;
1578 - }
1555 + unset( $openers[ $lastOpener ] );
1579 1556
1580 - $conditions[$stackPtr] = $this->tokens[$stackPtr]['code'];
1581 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1582 - $type = $this->tokens[$stackPtr]['type'];
1583 - echo str_repeat("\t", ($level + 1));
1584 - echo "* token $stackPtr:$type added to conditions array *".PHP_EOL;
1585 - }
1557 + $level --;
1558 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1559 + echo str_repeat( "\t", ( $level + 2 ) );
1560 + echo '* level decreased *' . PHP_EOL;
1561 + }
1562 + }//end if
1563 + }//end if
1586 1564
1587 - $lastOpener = $this->tokens[$i]['scope_opener'];
1588 - if ($lastOpener !== null) {
1589 - $openers[$lastOpener] = $lastOpener;
1590 - }
1591 - } else if ($lastOpener !== null && $this->tokens[$lastOpener]['scope_closer'] === $i) {
1592 - foreach (array_reverse($openers) as $opener) {
1593 - if ($this->tokens[$opener]['scope_closer'] === $i) {
1594 - $oldOpener = array_pop($openers);
1595 - if (empty($openers) === false) {
1596 - $lastOpener = array_pop($openers);
1597 - $openers[$lastOpener] = $lastOpener;
1598 - } else {
1599 - $lastOpener = null;
1600 - }
1565 + $level ++;
1566 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1567 + echo str_repeat( "\t", ( $level + 1 ) );
1568 + echo '* level increased *' . PHP_EOL;
1569 + }
1601 1570
1602 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1603 - $type = $this->tokens[$oldOpener]['type'];
1604 - echo str_repeat("\t", ($level + 1));
1605 - echo "=> Found scope closer for $oldOpener:$type".PHP_EOL;
1606 - }
1571 + $conditions[ $stackPtr ] = $this->tokens[ $stackPtr ]['code'];
1572 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1573 + $type = $this->tokens[ $stackPtr ]['type'];
1574 + echo str_repeat( "\t", ( $level + 1 ) );
1575 + echo "* token $stackPtr:$type added to conditions array *" . PHP_EOL;
1576 + }
1607 1577
1608 - $oldCondition = array_pop($conditions);
1609 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1610 - echo str_repeat("\t", ($level + 1));
1611 - echo '* token '.Util\Tokens::tokenName($oldCondition).' removed from conditions array *'.PHP_EOL;
1612 - }
1578 + $lastOpener = $this->tokens[ $i ]['scope_opener'];
1579 + if ( $lastOpener !== null ) {
1580 + $openers[ $lastOpener ] = $lastOpener;
1581 + }
1582 + } else if ( $lastOpener !== null && $this->tokens[ $lastOpener ]['scope_closer'] === $i ) {
1583 + foreach ( array_reverse( $openers ) as $opener ) {
1584 + if ( $this->tokens[ $opener ]['scope_closer'] === $i ) {
1585 + $oldOpener = array_pop( $openers );
1586 + if ( empty( $openers ) === false ) {
1587 + $lastOpener = array_pop( $openers );
1588 + $openers[ $lastOpener ] = $lastOpener;
1589 + } else {
1590 + $lastOpener = null;
1591 + }
1613 1592
1614 - // Make sure this closer actually belongs to us.
1615 - // Either the condition also has to think this is the
1616 - // closer, or it has to allow sharing with us.
1617 - $condition = $this->tokens[$this->tokens[$i]['scope_condition']]['code'];
1618 - if ($condition !== $oldCondition) {
1619 - if (isset($this->scopeOpeners[$oldCondition]['with'][$condition]) === false) {
1620 - $badToken = $this->tokens[$oldOpener]['scope_condition'];
1593 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1594 + $type = $this->tokens[ $oldOpener ]['type'];
1595 + echo str_repeat( "\t", ( $level + 1 ) );
1596 + echo "=> Found scope closer for $oldOpener:$type" . PHP_EOL;
1597 + }
1621 1598
1622 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1623 - $type = Util\Tokens::tokenName($oldCondition);
1624 - echo str_repeat("\t", ($level + 1));
1625 - echo "* scope closer was bad, cleaning up $badToken:$type *".PHP_EOL;
1626 - }
1599 + $oldCondition = array_pop( $conditions );
1600 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1601 + echo str_repeat( "\t", ( $level + 1 ) );
1602 + echo '* token ' . Util\Tokens::tokenName( $oldCondition ) . ' removed from conditions array *' . PHP_EOL;
1603 + }
1627 1604
1628 - for ($x = ($oldOpener + 1); $x <= $i; $x++) {
1629 - $oldConditions = $this->tokens[$x]['conditions'];
1630 - $oldLevel = $this->tokens[$x]['level'];
1631 - $this->tokens[$x]['level']--;
1632 - unset($this->tokens[$x]['conditions'][$badToken]);
1633 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1634 - $type = $this->tokens[$x]['type'];
1635 - $oldConds = '';
1636 - foreach ($oldConditions as $condition) {
1637 - $oldConds .= Util\Tokens::tokenName($condition).',';
1638 - }
1605 + // Make sure this closer actually belongs to us.
1606 + // Either the condition also has to think this is the
1607 + // closer, or it has to allow sharing with us.
1608 + $condition = $this->tokens[ $this->tokens[ $i ]['scope_condition'] ]['code'];
1609 + if ( $condition !== $oldCondition ) {
1610 + if ( isset( $this->scopeOpeners[ $oldCondition ]['with'][ $condition ] ) === false ) {
1611 + $badToken = $this->tokens[ $oldOpener ]['scope_condition'];
1639 1612
1640 - $oldConds = rtrim($oldConds, ',');
1613 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1614 + $type = Util\Tokens::tokenName( $oldCondition );
1615 + echo str_repeat( "\t", ( $level + 1 ) );
1616 + echo "* scope closer was bad, cleaning up $badToken:$type *" . PHP_EOL;
1617 + }
1641 1618
1642 - $newConds = '';
1643 - foreach ($this->tokens[$x]['conditions'] as $condition) {
1644 - $newConds .= Util\Tokens::tokenName($condition).',';
1645 - }
1619 + for ( $x = ( $oldOpener + 1 ); $x <= $i; $x ++ ) {
1620 + $oldConditions = $this->tokens[ $x ]['conditions'];
1621 + $oldLevel = $this->tokens[ $x ]['level'];
1622 + $this->tokens[ $x ]['level'] --;
1623 + unset( $this->tokens[ $x ]['conditions'][ $badToken ] );
1624 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1625 + $type = $this->tokens[ $x ]['type'];
1626 + $oldConds = '';
1627 + foreach ( $oldConditions as $condition ) {
1628 + $oldConds .= Util\Tokens::tokenName( $condition ) . ',';
1629 + }
1646 1630
1647 - $newConds = rtrim($newConds, ',');
1631 + $oldConds = rtrim( $oldConds, ',' );
1648 1632
1649 - $newLevel = $this->tokens[$x]['level'];
1650 - echo str_repeat("\t", ($level + 1));
1651 - echo "* cleaned $x:$type *".PHP_EOL;
1652 - echo str_repeat("\t", ($level + 2));
1653 - echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
1654 - echo str_repeat("\t", ($level + 2));
1655 - echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
1656 - }//end if
1657 - }//end for
1658 - }//end if
1659 - }//end if
1633 + $newConds = '';
1634 + foreach ( $this->tokens[ $x ]['conditions'] as $condition ) {
1635 + $newConds .= Util\Tokens::tokenName( $condition ) . ',';
1636 + }
1660 1637
1661 - $level--;
1662 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1663 - echo str_repeat("\t", ($level + 2));
1664 - echo '* level decreased *'.PHP_EOL;
1665 - }
1638 + $newConds = rtrim( $newConds, ',' );
1666 1639
1667 - $this->tokens[$i]['level'] = $level;
1668 - $this->tokens[$i]['conditions'] = $conditions;
1669 - }//end if
1670 - }//end foreach
1671 - }//end if
1672 - }//end if
1673 - }//end for
1640 + $newLevel = $this->tokens[ $x ]['level'];
1641 + echo str_repeat( "\t", ( $level + 1 ) );
1642 + echo "* cleaned $x:$type *" . PHP_EOL;
1643 + echo str_repeat( "\t", ( $level + 2 ) );
1644 + echo "=> level changed from $oldLevel to $newLevel" . PHP_EOL;
1645 + echo str_repeat( "\t", ( $level + 2 ) );
1646 + echo "=> conditions changed from $oldConds to $newConds" . PHP_EOL;
1647 + }//end if
1648 + }//end for
1649 + }//end if
1650 + }//end if
1674 1651
1675 - if (PHP_CODESNIFFER_VERBOSITY > 1) {
1676 - echo "\t*** END LEVEL MAP ***".PHP_EOL;
1677 - }
1652 + $level --;
1653 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1654 + echo str_repeat( "\t", ( $level + 2 ) );
1655 + echo '* level decreased *' . PHP_EOL;
1656 + }
1678 1657
1679 - }//end createLevelMap()
1658 + $this->tokens[ $i ]['level'] = $level;
1659 + $this->tokens[ $i ]['conditions'] = $conditions;
1660 + }//end if
1661 + }//end foreach
1662 + }//end if
1663 + }//end if
1664 + }//end for
1665 +
1666 + if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
1667 + echo "\t*** END LEVEL MAP ***" . PHP_EOL;
1668 + }
1669 +
1670 + }//end createLevelMap()
1680 1671
1681 1672
1682 1673 }//end class