| Total Complexity | 286 |
| Total Lines | 1290 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like ScopeIndentSniff often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ScopeIndentSniff, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 17 | class ScopeIndentSniff implements Sniff |
||
| 18 | { |
||
| 19 | |||
| 20 | /** |
||
| 21 | * A list of tokenizers this sniff supports. |
||
| 22 | * |
||
| 23 | * @var array |
||
| 24 | */ |
||
| 25 | public $supportedTokenizers = [ |
||
| 26 | 'PHP', |
||
| 27 | 'JS', |
||
| 28 | ]; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * The number of spaces code should be indented. |
||
| 32 | * |
||
| 33 | * @var integer |
||
| 34 | */ |
||
| 35 | public $indent = 4; |
||
| 36 | |||
| 37 | /** |
||
| 38 | * Does the indent need to be exactly right? |
||
| 39 | * |
||
| 40 | * If TRUE, indent needs to be exactly $indent spaces. If FALSE, |
||
| 41 | * indent needs to be at least $indent spaces (but can be more). |
||
| 42 | * |
||
| 43 | * @var boolean |
||
| 44 | */ |
||
| 45 | public $exact = false; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * Should tabs be used for indenting? |
||
| 49 | * |
||
| 50 | * If TRUE, fixes will be made using tabs instead of spaces. |
||
| 51 | * The size of each tab is important, so it should be specified |
||
| 52 | * using the --tab-width CLI argument. |
||
| 53 | * |
||
| 54 | * @var boolean |
||
| 55 | */ |
||
| 56 | public $tabIndent = false; |
||
| 57 | |||
| 58 | /** |
||
| 59 | * The --tab-width CLI value that is being used. |
||
| 60 | * |
||
| 61 | * @var integer |
||
| 62 | */ |
||
| 63 | private $tabWidth = null; |
||
| 64 | |||
| 65 | /** |
||
| 66 | * List of tokens not needing to be checked for indentation. |
||
| 67 | * |
||
| 68 | * Useful to allow Sniffs based on this to easily ignore/skip some |
||
| 69 | * tokens from verification. For example, inline HTML sections |
||
| 70 | * or PHP open/close tags can escape from here and have their own |
||
| 71 | * rules elsewhere. |
||
| 72 | * |
||
| 73 | * @var int[] |
||
| 74 | */ |
||
| 75 | public $ignoreIndentationTokens = []; |
||
| 76 | |||
| 77 | /** |
||
| 78 | * List of tokens not needing to be checked for indentation. |
||
| 79 | * |
||
| 80 | * This is a cached copy of the public version of this var, which |
||
| 81 | * can be set in a ruleset file, and some core ignored tokens. |
||
| 82 | * |
||
| 83 | * @var int[] |
||
| 84 | */ |
||
| 85 | private $ignoreIndentation = []; |
||
| 86 | |||
| 87 | /** |
||
| 88 | * Any scope openers that should not cause an indent. |
||
| 89 | * |
||
| 90 | * @var int[] |
||
| 91 | */ |
||
| 92 | protected $nonIndentingScopes = []; |
||
| 93 | |||
| 94 | /** |
||
| 95 | * Show debug output for this sniff. |
||
| 96 | * |
||
| 97 | * @var boolean |
||
| 98 | */ |
||
| 99 | private $debug = false; |
||
| 100 | |||
| 101 | |||
| 102 | /** |
||
| 103 | * Returns an array of tokens this test wants to listen for. |
||
| 104 | * |
||
| 105 | * @return array |
||
| 106 | */ |
||
| 107 | public function register() |
||
| 108 | { |
||
| 109 | if (defined('PHP_CODESNIFFER_IN_TESTS') === true) { |
||
| 110 | $this->debug = false; |
||
| 111 | } |
||
| 112 | |||
| 113 | return [T_OPEN_TAG]; |
||
| 114 | |||
| 115 | }//end register() |
||
| 116 | |||
| 117 | |||
| 118 | /** |
||
| 119 | * Processes this test, when one of its tokens is encountered. |
||
| 120 | * |
||
| 121 | * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document. |
||
| 122 | * @param int $stackPtr The position of the current token |
||
| 123 | * in the stack passed in $tokens. |
||
| 124 | * |
||
| 125 | * @return void |
||
| 126 | */ |
||
| 127 | public function process(File $phpcsFile, $stackPtr) |
||
| 128 | { |
||
| 129 | $debug = Config::getConfigData('scope_indent_debug'); |
||
| 130 | if ($debug !== null) { |
||
| 131 | $this->debug = (bool) $debug; |
||
| 132 | } |
||
| 133 | |||
| 134 | if ($this->tabWidth === null) { |
||
| 135 | if (isset($phpcsFile->config->tabWidth) === false || $phpcsFile->config->tabWidth === 0) { |
||
| 136 | // We have no idea how wide tabs are, so assume 4 spaces for fixing. |
||
| 137 | // It shouldn't really matter because indent checks elsewhere in the |
||
| 138 | // standard should fix things up. |
||
| 139 | $this->tabWidth = 4; |
||
| 140 | } else { |
||
| 141 | $this->tabWidth = $phpcsFile->config->tabWidth; |
||
| 142 | } |
||
| 143 | } |
||
| 144 | |||
| 145 | $lastOpenTag = $stackPtr; |
||
| 146 | $lastCloseTag = null; |
||
| 147 | $openScopes = []; |
||
| 148 | $adjustments = []; |
||
| 149 | $setIndents = []; |
||
| 150 | |||
| 151 | $tokens = $phpcsFile->getTokens(); |
||
| 152 | $first = $phpcsFile->findFirstOnLine(T_INLINE_HTML, $stackPtr); |
||
| 153 | $trimmed = ltrim($tokens[$first]['content']); |
||
| 154 | if ($trimmed === '') { |
||
| 155 | $currentIndent = ($tokens[$stackPtr]['column'] - 1); |
||
| 156 | } else { |
||
| 157 | $currentIndent = (strlen($tokens[$first]['content']) - strlen($trimmed)); |
||
| 158 | } |
||
| 159 | |||
| 160 | if ($this->debug === true) { |
||
| 161 | $line = $tokens[$stackPtr]['line']; |
||
| 162 | echo "Start with token $stackPtr on line $line with indent $currentIndent".PHP_EOL; |
||
| 163 | } |
||
| 164 | |||
| 165 | if (empty($this->ignoreIndentation) === true) { |
||
| 166 | $this->ignoreIndentation = [T_INLINE_HTML => true]; |
||
| 167 | foreach ($this->ignoreIndentationTokens as $token) { |
||
| 168 | if (is_int($token) === false) { |
||
| 169 | if (defined($token) === false) { |
||
| 170 | continue; |
||
| 171 | } |
||
| 172 | |||
| 173 | $token = constant($token); |
||
| 174 | } |
||
| 175 | |||
| 176 | $this->ignoreIndentation[$token] = true; |
||
| 177 | } |
||
| 178 | }//end if |
||
| 179 | |||
| 180 | $this->exact = (bool) $this->exact; |
||
| 181 | $this->tabIndent = (bool) $this->tabIndent; |
||
| 182 | |||
| 183 | for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) { |
||
| 184 | if ($i === false) { |
||
| 185 | // Something has gone very wrong; maybe a parse error. |
||
| 186 | break; |
||
| 187 | } |
||
| 188 | |||
| 189 | $checkToken = null; |
||
| 190 | $checkIndent = null; |
||
| 191 | |||
| 192 | $exact = (bool) $this->exact; |
||
| 193 | if ($exact === true && isset($tokens[$i]['nested_parenthesis']) === true) { |
||
| 194 | // Don't check indents exactly between parenthesis as they |
||
| 195 | // tend to have custom rules, such as with multi-line function calls |
||
| 196 | // and control structure conditions. |
||
| 197 | $exact = false; |
||
| 198 | } |
||
| 199 | |||
| 200 | // Detect line changes and figure out where the indent is. |
||
| 201 | if ($tokens[$i]['column'] === 1) { |
||
| 202 | $trimmed = ltrim($tokens[$i]['content']); |
||
| 203 | if ($trimmed === '') { |
||
| 204 | if (isset($tokens[($i + 1)]) === true |
||
| 205 | && $tokens[$i]['line'] === $tokens[($i + 1)]['line'] |
||
| 206 | ) { |
||
| 207 | $checkToken = ($i + 1); |
||
| 208 | $tokenIndent = ($tokens[($i + 1)]['column'] - 1); |
||
| 209 | } |
||
| 210 | } else { |
||
| 211 | $checkToken = $i; |
||
| 212 | $tokenIndent = (strlen($tokens[$i]['content']) - strlen($trimmed)); |
||
| 213 | } |
||
| 214 | } |
||
| 215 | |||
| 216 | // Closing parenthesis should just be indented to at least |
||
| 217 | // the same level as where they were opened (but can be more). |
||
| 218 | if (($checkToken !== null |
||
| 219 | && $tokens[$checkToken]['code'] === T_CLOSE_PARENTHESIS |
||
| 220 | && isset($tokens[$checkToken]['parenthesis_opener']) === true) |
||
| 221 | || ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS |
||
| 222 | && isset($tokens[$i]['parenthesis_opener']) === true) |
||
| 223 | ) { |
||
| 224 | if ($checkToken !== null) { |
||
| 225 | $parenCloser = $checkToken; |
||
| 226 | } else { |
||
| 227 | $parenCloser = $i; |
||
| 228 | } |
||
| 229 | |||
| 230 | if ($this->debug === true) { |
||
| 231 | $line = $tokens[$i]['line']; |
||
| 232 | echo "Closing parenthesis found on line $line".PHP_EOL; |
||
| 233 | } |
||
| 234 | |||
| 235 | $parenOpener = $tokens[$parenCloser]['parenthesis_opener']; |
||
| 236 | if ($tokens[$parenCloser]['line'] !== $tokens[$parenOpener]['line']) { |
||
| 237 | $parens = 0; |
||
| 238 | if (isset($tokens[$parenCloser]['nested_parenthesis']) === true |
||
| 239 | && empty($tokens[$parenCloser]['nested_parenthesis']) === false |
||
| 240 | ) { |
||
| 241 | end($tokens[$parenCloser]['nested_parenthesis']); |
||
| 242 | $parens = key($tokens[$parenCloser]['nested_parenthesis']); |
||
| 243 | if ($this->debug === true) { |
||
| 244 | $line = $tokens[$parens]['line']; |
||
| 245 | echo "\t* token has nested parenthesis $parens on line $line *".PHP_EOL; |
||
| 246 | } |
||
| 247 | } |
||
| 248 | |||
| 249 | $condition = 0; |
||
| 250 | if (isset($tokens[$parenCloser]['conditions']) === true |
||
| 251 | && empty($tokens[$parenCloser]['conditions']) === false |
||
| 252 | ) { |
||
| 253 | end($tokens[$parenCloser]['conditions']); |
||
| 254 | $condition = key($tokens[$parenCloser]['conditions']); |
||
| 255 | if ($this->debug === true) { |
||
| 256 | $line = $tokens[$condition]['line']; |
||
| 257 | $type = $tokens[$condition]['type']; |
||
| 258 | echo "\t* token is inside condition $condition ($type) on line $line *".PHP_EOL; |
||
| 259 | } |
||
| 260 | } |
||
| 261 | |||
| 262 | if ($parens > $condition) { |
||
| 263 | if ($this->debug === true) { |
||
| 264 | echo "\t* using parenthesis *".PHP_EOL; |
||
| 265 | } |
||
| 266 | |||
| 267 | $parenOpener = $parens; |
||
| 268 | $condition = 0; |
||
| 269 | } else if ($condition > 0) { |
||
| 270 | if ($this->debug === true) { |
||
| 271 | echo "\t* using condition *".PHP_EOL; |
||
| 272 | } |
||
| 273 | |||
| 274 | $parenOpener = $condition; |
||
| 275 | $parens = 0; |
||
| 276 | } |
||
| 277 | |||
| 278 | $exact = false; |
||
| 279 | |||
| 280 | $lastOpenTagConditions = array_keys($tokens[$lastOpenTag]['conditions']); |
||
| 281 | $lastOpenTagCondition = array_pop($lastOpenTagConditions); |
||
| 282 | |||
| 283 | if ($condition > 0 && $lastOpenTagCondition === $condition) { |
||
| 284 | if ($this->debug === true) { |
||
| 285 | echo "\t* open tag is inside condition; using open tag *".PHP_EOL; |
||
| 286 | } |
||
| 287 | |||
| 288 | $checkIndent = ($tokens[$lastOpenTag]['column'] - 1); |
||
| 289 | if (isset($adjustments[$condition]) === true) { |
||
| 290 | $checkIndent += $adjustments[$condition]; |
||
| 291 | } |
||
| 292 | |||
| 293 | $currentIndent = $checkIndent; |
||
| 294 | |||
| 295 | if ($this->debug === true) { |
||
| 296 | $type = $tokens[$lastOpenTag]['type']; |
||
| 297 | echo "\t=> checking indent of $checkIndent; main indent set to $currentIndent by token $lastOpenTag ($type)".PHP_EOL; |
||
| 298 | } |
||
| 299 | } else if ($condition > 0 |
||
| 300 | && isset($tokens[$condition]['scope_opener']) === true |
||
| 301 | && isset($setIndents[$tokens[$condition]['scope_opener']]) === true |
||
| 302 | ) { |
||
| 303 | $checkIndent = $setIndents[$tokens[$condition]['scope_opener']]; |
||
| 304 | if (isset($adjustments[$condition]) === true) { |
||
| 305 | $checkIndent += $adjustments[$condition]; |
||
| 306 | } |
||
| 307 | |||
| 308 | $currentIndent = $checkIndent; |
||
| 309 | |||
| 310 | if ($this->debug === true) { |
||
| 311 | $type = $tokens[$condition]['type']; |
||
| 312 | echo "\t=> checking indent of $checkIndent; main indent set to $currentIndent by token $condition ($type)".PHP_EOL; |
||
| 313 | } |
||
| 314 | } else { |
||
| 315 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $parenOpener, true); |
||
| 316 | |||
| 317 | $checkIndent = ($tokens[$first]['column'] - 1); |
||
| 318 | if (isset($adjustments[$first]) === true) { |
||
| 319 | $checkIndent += $adjustments[$first]; |
||
| 320 | } |
||
| 321 | |||
| 322 | if ($this->debug === true) { |
||
| 323 | $line = $tokens[$first]['line']; |
||
| 324 | $type = $tokens[$first]['type']; |
||
| 325 | echo "\t* first token on line $line is $first ($type) *".PHP_EOL; |
||
| 326 | } |
||
| 327 | |||
| 328 | if ($first === $tokens[$parenCloser]['parenthesis_opener'] |
||
| 329 | && $tokens[($first - 1)]['line'] === $tokens[$first]['line'] |
||
| 330 | ) { |
||
| 331 | // This is unlikely to be the start of the statement, so look |
||
| 332 | // back further to find it. |
||
| 333 | $first--; |
||
| 334 | if ($this->debug === true) { |
||
| 335 | $line = $tokens[$first]['line']; |
||
| 336 | $type = $tokens[$first]['type']; |
||
| 337 | echo "\t* first token is the parenthesis opener *".PHP_EOL; |
||
| 338 | echo "\t* amended first token is $first ($type) on line $line *".PHP_EOL; |
||
| 339 | } |
||
| 340 | } |
||
| 341 | |||
| 342 | $prev = $phpcsFile->findStartOfStatement($first, T_COMMA); |
||
| 343 | if ($prev !== $first) { |
||
| 344 | // This is not the start of the statement. |
||
| 345 | if ($this->debug === true) { |
||
| 346 | $line = $tokens[$prev]['line']; |
||
| 347 | $type = $tokens[$prev]['type']; |
||
| 348 | echo "\t* previous is $type on line $line *".PHP_EOL; |
||
| 349 | } |
||
| 350 | |||
| 351 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true); |
||
| 352 | $prev = $phpcsFile->findStartOfStatement($first, T_COMMA); |
||
| 353 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true); |
||
| 354 | if ($this->debug === true) { |
||
| 355 | $line = $tokens[$first]['line']; |
||
| 356 | $type = $tokens[$first]['type']; |
||
| 357 | echo "\t* amended first token is $first ($type) on line $line *".PHP_EOL; |
||
| 358 | } |
||
| 359 | } |
||
| 360 | |||
| 361 | if (isset($tokens[$first]['scope_closer']) === true |
||
| 362 | && $tokens[$first]['scope_closer'] === $first |
||
| 363 | ) { |
||
| 364 | if ($this->debug === true) { |
||
| 365 | echo "\t* first token is a scope closer *".PHP_EOL; |
||
| 366 | } |
||
| 367 | |||
| 368 | if (isset($tokens[$first]['scope_condition']) === true) { |
||
| 369 | $scopeCloser = $first; |
||
| 370 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $tokens[$scopeCloser]['scope_condition'], true); |
||
| 371 | |||
| 372 | $currentIndent = ($tokens[$first]['column'] - 1); |
||
| 373 | if (isset($adjustments[$first]) === true) { |
||
| 374 | $currentIndent += $adjustments[$first]; |
||
| 375 | } |
||
| 376 | |||
| 377 | // Make sure it is divisible by our expected indent. |
||
| 378 | if ($tokens[$tokens[$scopeCloser]['scope_condition']]['code'] !== T_CLOSURE) { |
||
| 379 | $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent); |
||
| 380 | } |
||
| 381 | |||
| 382 | $setIndents[$first] = $currentIndent; |
||
| 383 | |||
| 384 | if ($this->debug === true) { |
||
| 385 | $type = $tokens[$first]['type']; |
||
| 386 | echo "\t=> indent set to $currentIndent by token $first ($type)".PHP_EOL; |
||
| 387 | } |
||
| 388 | }//end if |
||
| 389 | } else { |
||
| 390 | // Don't force current indent to divisible because there could be custom |
||
| 391 | // rules in place between parenthesis, such as with arrays. |
||
| 392 | $currentIndent = ($tokens[$first]['column'] - 1); |
||
| 393 | if (isset($adjustments[$first]) === true) { |
||
| 394 | $currentIndent += $adjustments[$first]; |
||
| 395 | } |
||
| 396 | |||
| 397 | $setIndents[$first] = $currentIndent; |
||
| 398 | |||
| 399 | if ($this->debug === true) { |
||
| 400 | $type = $tokens[$first]['type']; |
||
| 401 | echo "\t=> checking indent of $checkIndent; main indent set to $currentIndent by token $first ($type)".PHP_EOL; |
||
| 402 | } |
||
| 403 | }//end if |
||
| 404 | }//end if |
||
| 405 | } else if ($this->debug === true) { |
||
| 406 | echo "\t * ignoring single-line definition *".PHP_EOL; |
||
| 407 | }//end if |
||
| 408 | }//end if |
||
| 409 | |||
| 410 | // Closing short array bracket should just be indented to at least |
||
| 411 | // the same level as where it was opened (but can be more). |
||
| 412 | if ($tokens[$i]['code'] === T_CLOSE_SHORT_ARRAY |
||
| 413 | || ($checkToken !== null |
||
| 414 | && $tokens[$checkToken]['code'] === T_CLOSE_SHORT_ARRAY) |
||
| 415 | ) { |
||
| 416 | if ($checkToken !== null) { |
||
| 417 | $arrayCloser = $checkToken; |
||
| 418 | } else { |
||
| 419 | $arrayCloser = $i; |
||
| 420 | } |
||
| 421 | |||
| 422 | if ($this->debug === true) { |
||
| 423 | $line = $tokens[$arrayCloser]['line']; |
||
| 424 | echo "Closing short array bracket found on line $line".PHP_EOL; |
||
| 425 | } |
||
| 426 | |||
| 427 | $arrayOpener = $tokens[$arrayCloser]['bracket_opener']; |
||
| 428 | if ($tokens[$arrayCloser]['line'] !== $tokens[$arrayOpener]['line']) { |
||
| 429 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $arrayOpener, true); |
||
| 430 | $checkIndent = ($tokens[$first]['column'] - 1); |
||
| 431 | if (isset($adjustments[$first]) === true) { |
||
| 432 | $checkIndent += $adjustments[$first]; |
||
| 433 | } |
||
| 434 | |||
| 435 | $exact = false; |
||
| 436 | |||
| 437 | if ($this->debug === true) { |
||
| 438 | $line = $tokens[$first]['line']; |
||
| 439 | $type = $tokens[$first]['type']; |
||
| 440 | echo "\t* first token on line $line is $first ($type) *".PHP_EOL; |
||
| 441 | } |
||
| 442 | |||
| 443 | if ($first === $tokens[$arrayCloser]['bracket_opener']) { |
||
| 444 | // This is unlikely to be the start of the statement, so look |
||
| 445 | // back further to find it. |
||
| 446 | $first--; |
||
| 447 | } |
||
| 448 | |||
| 449 | $prev = $phpcsFile->findStartOfStatement($first, [T_COMMA, T_DOUBLE_ARROW]); |
||
| 450 | if ($prev !== $first) { |
||
| 451 | // This is not the start of the statement. |
||
| 452 | if ($this->debug === true) { |
||
| 453 | $line = $tokens[$prev]['line']; |
||
| 454 | $type = $tokens[$prev]['type']; |
||
| 455 | echo "\t* previous is $type on line $line *".PHP_EOL; |
||
| 456 | } |
||
| 457 | |||
| 458 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true); |
||
| 459 | $prev = $phpcsFile->findStartOfStatement($first, [T_COMMA, T_DOUBLE_ARROW]); |
||
| 460 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true); |
||
| 461 | if ($this->debug === true) { |
||
| 462 | $line = $tokens[$first]['line']; |
||
| 463 | $type = $tokens[$first]['type']; |
||
| 464 | echo "\t* amended first token is $first ($type) on line $line *".PHP_EOL; |
||
| 465 | } |
||
| 466 | } else if ($tokens[$first]['code'] === T_WHITESPACE) { |
||
| 467 | $first = $phpcsFile->findNext(T_WHITESPACE, ($first + 1), null, true); |
||
| 468 | } |
||
| 469 | |||
| 470 | if (isset($tokens[$first]['scope_closer']) === true |
||
| 471 | && $tokens[$first]['scope_closer'] === $first |
||
| 472 | ) { |
||
| 473 | // The first token is a scope closer and would have already |
||
| 474 | // been processed and set the indent level correctly, so |
||
| 475 | // don't adjust it again. |
||
| 476 | if ($this->debug === true) { |
||
| 477 | echo "\t* first token is a scope closer; ignoring closing short array bracket *".PHP_EOL; |
||
| 478 | } |
||
| 479 | |||
| 480 | if (isset($setIndents[$first]) === true) { |
||
| 481 | $currentIndent = $setIndents[$first]; |
||
| 482 | if ($this->debug === true) { |
||
| 483 | echo "\t=> indent reset to $currentIndent".PHP_EOL; |
||
| 484 | } |
||
| 485 | } |
||
| 486 | } else { |
||
| 487 | // Don't force current indent to be divisible because there could be custom |
||
| 488 | // rules in place for arrays. |
||
| 489 | $currentIndent = ($tokens[$first]['column'] - 1); |
||
| 490 | if (isset($adjustments[$first]) === true) { |
||
| 491 | $currentIndent += $adjustments[$first]; |
||
| 492 | } |
||
| 493 | |||
| 494 | $setIndents[$first] = $currentIndent; |
||
| 495 | |||
| 496 | if ($this->debug === true) { |
||
| 497 | $type = $tokens[$first]['type']; |
||
| 498 | echo "\t=> checking indent of $checkIndent; main indent set to $currentIndent by token $first ($type)".PHP_EOL; |
||
| 499 | } |
||
| 500 | }//end if |
||
| 501 | } else if ($this->debug === true) { |
||
| 502 | echo "\t * ignoring single-line definition *".PHP_EOL; |
||
| 503 | }//end if |
||
| 504 | }//end if |
||
| 505 | |||
| 506 | // Adjust lines within scopes while auto-fixing. |
||
| 507 | if ($checkToken !== null |
||
| 508 | && $exact === false |
||
| 509 | && (empty($tokens[$checkToken]['conditions']) === false |
||
| 510 | || (isset($tokens[$checkToken]['scope_opener']) === true |
||
| 511 | && $tokens[$checkToken]['scope_opener'] === $checkToken)) |
||
| 512 | ) { |
||
| 513 | if (empty($tokens[$checkToken]['conditions']) === false) { |
||
| 514 | end($tokens[$checkToken]['conditions']); |
||
| 515 | $condition = key($tokens[$checkToken]['conditions']); |
||
| 516 | } else { |
||
| 517 | $condition = $tokens[$checkToken]['scope_condition']; |
||
| 518 | } |
||
| 519 | |||
| 520 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $condition, true); |
||
| 521 | |||
| 522 | if (isset($adjustments[$first]) === true |
||
| 523 | && (($adjustments[$first] < 0 && $tokenIndent > $currentIndent) |
||
| 524 | || ($adjustments[$first] > 0 && $tokenIndent < $currentIndent)) |
||
| 525 | ) { |
||
| 526 | $padding = ($tokenIndent + $adjustments[$first]); |
||
| 527 | if ($padding > 0) { |
||
| 528 | if ($this->tabIndent === true) { |
||
| 529 | $numTabs = floor($padding / $this->tabWidth); |
||
| 530 | $numSpaces = ($padding - ($numTabs * $this->tabWidth)); |
||
| 531 | $padding = str_repeat("\t", $numTabs).str_repeat(' ', $numSpaces); |
||
| 532 | } else { |
||
| 533 | $padding = str_repeat(' ', $padding); |
||
| 534 | } |
||
| 535 | } else { |
||
| 536 | $padding = ''; |
||
| 537 | } |
||
| 538 | |||
| 539 | if ($phpcsFile->fixer->enabled === true) { |
||
| 540 | if ($checkToken === $i) { |
||
| 541 | $phpcsFile->fixer->replaceToken($checkToken, $padding.$trimmed); |
||
| 542 | } else { |
||
| 543 | // Easier to just replace the entire indent. |
||
| 544 | $phpcsFile->fixer->replaceToken(($checkToken - 1), $padding); |
||
| 545 | } |
||
| 546 | } |
||
| 547 | |||
| 548 | if ($this->debug === true) { |
||
| 549 | $length = strlen($padding); |
||
| 550 | $line = $tokens[$checkToken]['line']; |
||
| 551 | $type = $tokens[$checkToken]['type']; |
||
| 552 | echo "Indent adjusted to $length for $type on line $line".PHP_EOL; |
||
| 553 | } |
||
| 554 | |||
| 555 | $adjustments[$checkToken] = $adjustments[$first]; |
||
| 556 | |||
| 557 | if ($this->debug === true) { |
||
| 558 | $line = $tokens[$checkToken]['line']; |
||
| 559 | $type = $tokens[$checkToken]['type']; |
||
| 560 | echo "\t=> Add adjustment of ".$adjustments[$checkToken]." for token $checkToken ($type) on line $line".PHP_EOL; |
||
| 561 | } |
||
| 562 | }//end if |
||
| 563 | }//end if |
||
| 564 | |||
| 565 | // Scope closers reset the required indent to the same level as the opening condition. |
||
| 566 | if (($checkToken !== null |
||
| 567 | && isset($openScopes[$checkToken]) === true |
||
| 568 | || (isset($tokens[$checkToken]['scope_condition']) === true |
||
| 569 | && isset($tokens[$checkToken]['scope_closer']) === true |
||
| 570 | && $tokens[$checkToken]['scope_closer'] === $checkToken |
||
| 571 | && $tokens[$checkToken]['line'] !== $tokens[$tokens[$checkToken]['scope_opener']]['line'])) |
||
| 572 | || ($checkToken === null |
||
| 573 | && isset($openScopes[$i]) === true |
||
| 574 | || (isset($tokens[$i]['scope_condition']) === true |
||
| 575 | && isset($tokens[$i]['scope_closer']) === true |
||
| 576 | && $tokens[$i]['scope_closer'] === $i |
||
| 577 | && $tokens[$i]['line'] !== $tokens[$tokens[$i]['scope_opener']]['line'])) |
||
| 578 | ) { |
||
| 579 | if ($this->debug === true) { |
||
| 580 | if ($checkToken === null) { |
||
| 581 | $type = $tokens[$tokens[$i]['scope_condition']]['type']; |
||
| 582 | $line = $tokens[$i]['line']; |
||
| 583 | } else { |
||
| 584 | $type = $tokens[$tokens[$checkToken]['scope_condition']]['type']; |
||
| 585 | $line = $tokens[$checkToken]['line']; |
||
| 586 | } |
||
| 587 | |||
| 588 | echo "Close scope ($type) on line $line".PHP_EOL; |
||
| 589 | } |
||
| 590 | |||
| 591 | $scopeCloser = $checkToken; |
||
| 592 | if ($scopeCloser === null) { |
||
| 593 | $scopeCloser = $i; |
||
| 594 | } else { |
||
| 595 | array_pop($openScopes); |
||
| 596 | } |
||
| 597 | |||
| 598 | if (isset($tokens[$scopeCloser]['scope_condition']) === true) { |
||
| 599 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $tokens[$scopeCloser]['scope_condition'], true); |
||
| 600 | if ($this->debug === true) { |
||
| 601 | $line = $tokens[$first]['line']; |
||
| 602 | $type = $tokens[$first]['type']; |
||
| 603 | echo "\t* first token is $first ($type) on line $line *".PHP_EOL; |
||
| 604 | } |
||
| 605 | |||
| 606 | while ($tokens[$first]['code'] === T_CONSTANT_ENCAPSED_STRING |
||
| 607 | && $tokens[($first - 1)]['code'] === T_CONSTANT_ENCAPSED_STRING |
||
| 608 | ) { |
||
| 609 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, ($first - 1), true); |
||
| 610 | if ($this->debug === true) { |
||
| 611 | $line = $tokens[$first]['line']; |
||
| 612 | $type = $tokens[$first]['type']; |
||
| 613 | echo "\t* found multi-line string; amended first token is $first ($type) on line $line *".PHP_EOL; |
||
| 614 | } |
||
| 615 | } |
||
| 616 | |||
| 617 | $currentIndent = ($tokens[$first]['column'] - 1); |
||
| 618 | if (isset($adjustments[$first]) === true) { |
||
| 619 | $currentIndent += $adjustments[$first]; |
||
| 620 | } |
||
| 621 | |||
| 622 | // Make sure it is divisible by our expected indent. |
||
| 623 | if ($tokens[$tokens[$scopeCloser]['scope_condition']]['code'] !== T_CLOSURE) { |
||
| 624 | $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent); |
||
| 625 | } |
||
| 626 | |||
| 627 | $setIndents[$scopeCloser] = $currentIndent; |
||
| 628 | |||
| 629 | if ($this->debug === true) { |
||
| 630 | $type = $tokens[$scopeCloser]['type']; |
||
| 631 | echo "\t=> indent set to $currentIndent by token $scopeCloser ($type)".PHP_EOL; |
||
| 632 | } |
||
| 633 | |||
| 634 | // We only check the indent of scope closers if they are |
||
| 635 | // curly braces because other constructs tend to have different rules. |
||
| 636 | if ($tokens[$scopeCloser]['code'] === T_CLOSE_CURLY_BRACKET) { |
||
| 637 | $exact = true; |
||
| 638 | } else { |
||
| 639 | $checkToken = null; |
||
| 640 | } |
||
| 641 | }//end if |
||
| 642 | }//end if |
||
| 643 | |||
| 644 | // Handle scope for JS object notation. |
||
| 645 | if ($phpcsFile->tokenizerType === 'JS' |
||
| 646 | && (($checkToken !== null |
||
| 647 | && $tokens[$checkToken]['code'] === T_CLOSE_OBJECT |
||
| 648 | && $tokens[$checkToken]['line'] !== $tokens[$tokens[$checkToken]['bracket_opener']]['line']) |
||
| 649 | || ($checkToken === null |
||
| 650 | && $tokens[$i]['code'] === T_CLOSE_OBJECT |
||
| 651 | && $tokens[$i]['line'] !== $tokens[$tokens[$i]['bracket_opener']]['line'])) |
||
| 652 | ) { |
||
| 653 | if ($this->debug === true) { |
||
| 654 | $line = $tokens[$i]['line']; |
||
| 655 | echo "Close JS object on line $line".PHP_EOL; |
||
| 656 | } |
||
| 657 | |||
| 658 | $scopeCloser = $checkToken; |
||
| 659 | if ($scopeCloser === null) { |
||
| 660 | $scopeCloser = $i; |
||
| 661 | } else { |
||
| 662 | array_pop($openScopes); |
||
| 663 | } |
||
| 664 | |||
| 665 | $parens = 0; |
||
| 666 | if (isset($tokens[$scopeCloser]['nested_parenthesis']) === true |
||
| 667 | && empty($tokens[$scopeCloser]['nested_parenthesis']) === false |
||
| 668 | ) { |
||
| 669 | end($tokens[$scopeCloser]['nested_parenthesis']); |
||
| 670 | $parens = key($tokens[$scopeCloser]['nested_parenthesis']); |
||
| 671 | if ($this->debug === true) { |
||
| 672 | $line = $tokens[$parens]['line']; |
||
| 673 | echo "\t* token has nested parenthesis $parens on line $line *".PHP_EOL; |
||
| 674 | } |
||
| 675 | } |
||
| 676 | |||
| 677 | $condition = 0; |
||
| 678 | if (isset($tokens[$scopeCloser]['conditions']) === true |
||
| 679 | && empty($tokens[$scopeCloser]['conditions']) === false |
||
| 680 | ) { |
||
| 681 | end($tokens[$scopeCloser]['conditions']); |
||
| 682 | $condition = key($tokens[$scopeCloser]['conditions']); |
||
| 683 | if ($this->debug === true) { |
||
| 684 | $line = $tokens[$condition]['line']; |
||
| 685 | $type = $tokens[$condition]['type']; |
||
| 686 | echo "\t* token is inside condition $condition ($type) on line $line *".PHP_EOL; |
||
| 687 | } |
||
| 688 | } |
||
| 689 | |||
| 690 | if ($parens > $condition) { |
||
| 691 | if ($this->debug === true) { |
||
| 692 | echo "\t* using parenthesis *".PHP_EOL; |
||
| 693 | } |
||
| 694 | |||
| 695 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $parens, true); |
||
| 696 | $condition = 0; |
||
| 697 | } else if ($condition > 0) { |
||
| 698 | if ($this->debug === true) { |
||
| 699 | echo "\t* using condition *".PHP_EOL; |
||
| 700 | } |
||
| 701 | |||
| 702 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $condition, true); |
||
| 703 | $parens = 0; |
||
| 704 | } else { |
||
| 705 | if ($this->debug === true) { |
||
| 706 | $line = $tokens[$tokens[$scopeCloser]['bracket_opener']]['line']; |
||
| 707 | echo "\t* token is not in parenthesis or condition; using opener on line $line *".PHP_EOL; |
||
| 708 | } |
||
| 709 | |||
| 710 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $tokens[$scopeCloser]['bracket_opener'], true); |
||
| 711 | }//end if |
||
| 712 | |||
| 713 | $currentIndent = ($tokens[$first]['column'] - 1); |
||
| 714 | if (isset($adjustments[$first]) === true) { |
||
| 715 | $currentIndent += $adjustments[$first]; |
||
| 716 | } |
||
| 717 | |||
| 718 | if ($parens > 0 || $condition > 0) { |
||
| 719 | $checkIndent = ($tokens[$first]['column'] - 1); |
||
| 720 | if (isset($adjustments[$first]) === true) { |
||
| 721 | $checkIndent += $adjustments[$first]; |
||
| 722 | } |
||
| 723 | |||
| 724 | if ($condition > 0) { |
||
| 725 | $checkIndent += $this->indent; |
||
| 726 | $currentIndent += $this->indent; |
||
| 727 | $exact = true; |
||
| 728 | } |
||
| 729 | } else { |
||
| 730 | $checkIndent = $currentIndent; |
||
| 731 | } |
||
| 732 | |||
| 733 | // Make sure it is divisible by our expected indent. |
||
| 734 | $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent); |
||
| 735 | $checkIndent = (int) (ceil($checkIndent / $this->indent) * $this->indent); |
||
| 736 | $setIndents[$first] = $currentIndent; |
||
| 737 | |||
| 738 | if ($this->debug === true) { |
||
| 739 | $type = $tokens[$first]['type']; |
||
| 740 | echo "\t=> checking indent of $checkIndent; main indent set to $currentIndent by token $first ($type)".PHP_EOL; |
||
| 741 | } |
||
| 742 | }//end if |
||
| 743 | |||
| 744 | if ($checkToken !== null |
||
| 745 | && isset(Tokens::$scopeOpeners[$tokens[$checkToken]['code']]) === true |
||
| 746 | && in_array($tokens[$checkToken]['code'], $this->nonIndentingScopes) === false |
||
| 747 | && isset($tokens[$checkToken]['scope_opener']) === true |
||
| 748 | ) { |
||
| 749 | $exact = true; |
||
| 750 | |||
| 751 | $lastOpener = null; |
||
| 752 | if (empty($openScopes) === false) { |
||
| 753 | end($openScopes); |
||
| 754 | $lastOpener = current($openScopes); |
||
| 755 | } |
||
| 756 | |||
| 757 | // A scope opener that shares a closer with another token (like multiple |
||
| 758 | // CASEs using the same BREAK) needs to reduce the indent level so its |
||
| 759 | // indent is checked correctly. It will then increase the indent again |
||
| 760 | // (as all openers do) after being checked. |
||
| 761 | if ($lastOpener !== null |
||
| 762 | && isset($tokens[$lastOpener]['scope_closer']) === true |
||
| 763 | && $tokens[$lastOpener]['level'] === $tokens[$checkToken]['level'] |
||
| 764 | && $tokens[$lastOpener]['scope_closer'] === $tokens[$checkToken]['scope_closer'] |
||
| 765 | ) { |
||
| 766 | $currentIndent -= $this->indent; |
||
| 767 | $setIndents[$lastOpener] = $currentIndent; |
||
| 768 | if ($this->debug === true) { |
||
| 769 | $line = $tokens[$i]['line']; |
||
| 770 | $type = $tokens[$lastOpener]['type']; |
||
| 771 | echo "Shared closer found on line $line".PHP_EOL; |
||
| 772 | echo "\t=> indent set to $currentIndent by token $lastOpener ($type)".PHP_EOL; |
||
| 773 | } |
||
| 774 | } |
||
| 775 | |||
| 776 | if ($tokens[$checkToken]['code'] === T_CLOSURE |
||
| 777 | && $tokenIndent > $currentIndent |
||
| 778 | ) { |
||
| 779 | // The opener is indented more than needed, which is fine. |
||
| 780 | // But just check that it is divisible by our expected indent. |
||
| 781 | $checkIndent = (int) (ceil($tokenIndent / $this->indent) * $this->indent); |
||
| 782 | $exact = false; |
||
| 783 | |||
| 784 | if ($this->debug === true) { |
||
| 785 | $line = $tokens[$i]['line']; |
||
| 786 | echo "Closure found on line $line".PHP_EOL; |
||
| 787 | echo "\t=> checking indent of $checkIndent; main indent remains at $currentIndent".PHP_EOL; |
||
| 788 | } |
||
| 789 | } |
||
| 790 | }//end if |
||
| 791 | |||
| 792 | // Method prefix indentation has to be exact or else it will break |
||
| 793 | // the rest of the function declaration, and potentially future ones. |
||
| 794 | if ($checkToken !== null |
||
| 795 | && isset(Tokens::$methodPrefixes[$tokens[$checkToken]['code']]) === true |
||
| 796 | && $tokens[($checkToken + 1)]['code'] !== T_DOUBLE_COLON |
||
| 797 | ) { |
||
| 798 | $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($checkToken + 1), null, true); |
||
| 799 | if ($next === false || $tokens[$next]['code'] !== T_CLOSURE) { |
||
| 800 | if ($this->debug === true) { |
||
| 801 | $line = $tokens[$checkToken]['line']; |
||
| 802 | $type = $tokens[$checkToken]['type']; |
||
| 803 | echo "\t* method prefix ($type) found on line $line; indent set to exact *".PHP_EOL; |
||
| 804 | } |
||
| 805 | |||
| 806 | $exact = true; |
||
| 807 | } |
||
| 808 | } |
||
| 809 | |||
| 810 | // JS property indentation has to be exact or else if will break |
||
| 811 | // things like function and object indentation. |
||
| 812 | if ($checkToken !== null && $tokens[$checkToken]['code'] === T_PROPERTY) { |
||
| 813 | $exact = true; |
||
| 814 | } |
||
| 815 | |||
| 816 | // PHP tags needs to be indented to exact column positions |
||
| 817 | // so they don't cause problems with indent checks for the code |
||
| 818 | // within them, but they don't need to line up with the current indent. |
||
| 819 | if ($checkToken !== null |
||
| 820 | && ($tokens[$checkToken]['code'] === T_OPEN_TAG |
||
| 821 | || $tokens[$checkToken]['code'] === T_OPEN_TAG_WITH_ECHO |
||
| 822 | || $tokens[$checkToken]['code'] === T_CLOSE_TAG) |
||
| 823 | ) { |
||
| 824 | $exact = true; |
||
| 825 | $checkIndent = ($tokens[$checkToken]['column'] - 1); |
||
| 826 | $checkIndent = (int) (ceil($checkIndent / $this->indent) * $this->indent); |
||
| 827 | } |
||
| 828 | |||
| 829 | // Special case for ELSE statements that are not on the same |
||
| 830 | // line as the previous IF statements closing brace. They still need |
||
| 831 | // to have the same indent or it will break code after the block. |
||
| 832 | if ($checkToken !== null && $tokens[$checkToken]['code'] === T_ELSE) { |
||
| 833 | $exact = true; |
||
| 834 | } |
||
| 835 | |||
| 836 | if ($checkIndent === null) { |
||
| 837 | $checkIndent = $currentIndent; |
||
| 838 | } |
||
| 839 | |||
| 840 | /* |
||
| 841 | The indent of the line is checked by the following IF block. |
||
| 842 | |||
| 843 | Up until now, we've just been figuring out what the indent |
||
| 844 | of this line should be. |
||
| 845 | |||
| 846 | After this IF block, we adjust the indent again for |
||
| 847 | the checking of future line. |
||
| 848 | */ |
||
| 849 | |||
| 850 | if ($checkToken !== null |
||
| 851 | && isset($this->ignoreIndentation[$tokens[$checkToken]['code']]) === false |
||
| 852 | && (($tokenIndent !== $checkIndent && $exact === true) |
||
| 853 | || ($tokenIndent < $checkIndent && $exact === false)) |
||
| 854 | ) { |
||
| 855 | $type = 'IncorrectExact'; |
||
| 856 | $error = 'Line indented incorrectly; expected '; |
||
| 857 | if ($exact === false) { |
||
| 858 | $error .= 'at least '; |
||
| 859 | $type = 'Incorrect'; |
||
| 860 | } |
||
| 861 | |||
| 862 | if ($this->tabIndent === true) { |
||
| 863 | $error .= '%s tabs, found %s'; |
||
| 864 | $data = [ |
||
| 865 | floor($checkIndent / $this->tabWidth), |
||
| 866 | floor($tokenIndent / $this->tabWidth), |
||
| 867 | ]; |
||
| 868 | } else { |
||
| 869 | $error .= '%s spaces, found %s'; |
||
| 870 | $data = [ |
||
| 871 | $checkIndent, |
||
| 872 | $tokenIndent, |
||
| 873 | ]; |
||
| 874 | } |
||
| 875 | |||
| 876 | if ($this->debug === true) { |
||
| 877 | $line = $tokens[$checkToken]['line']; |
||
| 878 | $message = vsprintf($error, $data); |
||
| 879 | echo "[Line $line] $message".PHP_EOL; |
||
| 880 | } |
||
| 881 | |||
| 882 | $fix = $phpcsFile->addFixableError($error, $checkToken, $type, $data); |
||
| 883 | if ($fix === true || $this->debug === true) { |
||
| 884 | $padding = ''; |
||
| 885 | if ($this->tabIndent === true) { |
||
| 886 | $numTabs = floor($checkIndent / $this->tabWidth); |
||
| 887 | if ($numTabs > 0) { |
||
| 888 | $numSpaces = ($checkIndent - ($numTabs * $this->tabWidth)); |
||
| 889 | $padding = str_repeat("\t", $numTabs).str_repeat(' ', $numSpaces); |
||
| 890 | } |
||
| 891 | } else if ($checkIndent > 0) { |
||
| 892 | $padding = str_repeat(' ', $checkIndent); |
||
| 893 | } |
||
| 894 | |||
| 895 | if ($checkToken === $i) { |
||
| 896 | $accepted = $phpcsFile->fixer->replaceToken($checkToken, $padding.$trimmed); |
||
| 897 | } else { |
||
| 898 | // Easier to just replace the entire indent. |
||
| 899 | $accepted = $phpcsFile->fixer->replaceToken(($checkToken - 1), $padding); |
||
| 900 | } |
||
| 901 | |||
| 902 | if ($accepted === true) { |
||
| 903 | $adjustments[$checkToken] = ($checkIndent - $tokenIndent); |
||
| 904 | if ($this->debug === true) { |
||
| 905 | $line = $tokens[$checkToken]['line']; |
||
| 906 | $type = $tokens[$checkToken]['type']; |
||
| 907 | echo "\t=> Add adjustment of ".$adjustments[$checkToken]." for token $checkToken ($type) on line $line".PHP_EOL; |
||
| 908 | } |
||
| 909 | } |
||
| 910 | } else { |
||
| 911 | // Assume the change would be applied and continue |
||
| 912 | // checking indents under this assumption. This gives more |
||
| 913 | // technically accurate error messages. |
||
| 914 | $adjustments[$checkToken] = ($checkIndent - $tokenIndent); |
||
| 915 | }//end if |
||
| 916 | }//end if |
||
| 917 | |||
| 918 | if ($checkToken !== null) { |
||
| 919 | $i = $checkToken; |
||
| 920 | } |
||
| 921 | |||
| 922 | // Completely skip here/now docs as the indent is a part of the |
||
| 923 | // content itself. |
||
| 924 | if ($tokens[$i]['code'] === T_START_HEREDOC |
||
| 925 | || $tokens[$i]['code'] === T_START_NOWDOC |
||
| 926 | ) { |
||
| 927 | $i = $phpcsFile->findNext([T_END_HEREDOC, T_END_NOWDOC], ($i + 1)); |
||
| 928 | continue; |
||
| 929 | } |
||
| 930 | |||
| 931 | // Completely skip multi-line strings as the indent is a part of the |
||
| 932 | // content itself. |
||
| 933 | if ($tokens[$i]['code'] === T_CONSTANT_ENCAPSED_STRING |
||
| 934 | || $tokens[$i]['code'] === T_DOUBLE_QUOTED_STRING |
||
| 935 | ) { |
||
| 936 | $i = $phpcsFile->findNext($tokens[$i]['code'], ($i + 1), null, true); |
||
| 937 | $i--; |
||
| 938 | continue; |
||
| 939 | } |
||
| 940 | |||
| 941 | // Completely skip doc comments as they tend to have complex |
||
| 942 | // indentation rules. |
||
| 943 | if ($tokens[$i]['code'] === T_DOC_COMMENT_OPEN_TAG) { |
||
| 944 | $i = $tokens[$i]['comment_closer']; |
||
| 945 | continue; |
||
| 946 | } |
||
| 947 | |||
| 948 | // Open tags reset the indent level. |
||
| 949 | if ($tokens[$i]['code'] === T_OPEN_TAG |
||
| 950 | || $tokens[$i]['code'] === T_OPEN_TAG_WITH_ECHO |
||
| 951 | ) { |
||
| 952 | if ($this->debug === true) { |
||
| 953 | $line = $tokens[$i]['line']; |
||
| 954 | echo "Open PHP tag found on line $line".PHP_EOL; |
||
| 955 | } |
||
| 956 | |||
| 957 | if ($checkToken === null) { |
||
| 958 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $i, true); |
||
| 959 | $currentIndent = (strlen($tokens[$first]['content']) - strlen(ltrim($tokens[$first]['content']))); |
||
| 960 | } else { |
||
| 961 | $currentIndent = ($tokens[$i]['column'] - 1); |
||
| 962 | } |
||
| 963 | |||
| 964 | $lastOpenTag = $i; |
||
| 965 | |||
| 966 | if (isset($adjustments[$i]) === true) { |
||
| 967 | $currentIndent += $adjustments[$i]; |
||
| 968 | } |
||
| 969 | |||
| 970 | // Make sure it is divisible by our expected indent. |
||
| 971 | $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent); |
||
| 972 | $setIndents[$i] = $currentIndent; |
||
| 973 | |||
| 974 | if ($this->debug === true) { |
||
| 975 | $type = $tokens[$i]['type']; |
||
| 976 | echo "\t=> indent set to $currentIndent by token $i ($type)".PHP_EOL; |
||
| 977 | } |
||
| 978 | |||
| 979 | continue; |
||
| 980 | }//end if |
||
| 981 | |||
| 982 | // Close tags reset the indent level, unless they are closing a tag |
||
| 983 | // opened on the same line. |
||
| 984 | if ($tokens[$i]['code'] === T_CLOSE_TAG) { |
||
| 985 | if ($this->debug === true) { |
||
| 986 | $line = $tokens[$i]['line']; |
||
| 987 | echo "Close PHP tag found on line $line".PHP_EOL; |
||
| 988 | } |
||
| 989 | |||
| 990 | if ($tokens[$lastOpenTag]['line'] !== $tokens[$i]['line']) { |
||
| 991 | $currentIndent = ($tokens[$i]['column'] - 1); |
||
| 992 | $lastCloseTag = $i; |
||
| 993 | } else { |
||
| 994 | if ($lastCloseTag === null) { |
||
| 995 | $currentIndent = 0; |
||
| 996 | } else { |
||
| 997 | $currentIndent = ($tokens[$lastCloseTag]['column'] - 1); |
||
| 998 | } |
||
| 999 | } |
||
| 1000 | |||
| 1001 | if (isset($adjustments[$i]) === true) { |
||
| 1002 | $currentIndent += $adjustments[$i]; |
||
| 1003 | } |
||
| 1004 | |||
| 1005 | // Make sure it is divisible by our expected indent. |
||
| 1006 | $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent); |
||
| 1007 | $setIndents[$i] = $currentIndent; |
||
| 1008 | |||
| 1009 | if ($this->debug === true) { |
||
| 1010 | $type = $tokens[$i]['type']; |
||
| 1011 | echo "\t=> indent set to $currentIndent by token $i ($type)".PHP_EOL; |
||
| 1012 | } |
||
| 1013 | |||
| 1014 | continue; |
||
| 1015 | }//end if |
||
| 1016 | |||
| 1017 | // Anon classes and functions set the indent based on their own indent level. |
||
| 1018 | if ($tokens[$i]['code'] === T_CLOSURE || $tokens[$i]['code'] === T_ANON_CLASS) { |
||
| 1019 | $closer = $tokens[$i]['scope_closer']; |
||
| 1020 | if ($tokens[$i]['line'] === $tokens[$closer]['line']) { |
||
| 1021 | if ($this->debug === true) { |
||
| 1022 | $type = str_replace('_', ' ', strtolower(substr($tokens[$i]['type'], 2))); |
||
| 1023 | $line = $tokens[$i]['line']; |
||
| 1024 | echo "* ignoring single-line $type on line $line".PHP_EOL; |
||
| 1025 | } |
||
| 1026 | |||
| 1027 | $i = $closer; |
||
| 1028 | continue; |
||
| 1029 | } |
||
| 1030 | |||
| 1031 | if ($this->debug === true) { |
||
| 1032 | $type = str_replace('_', ' ', strtolower(substr($tokens[$i]['type'], 2))); |
||
| 1033 | $line = $tokens[$i]['line']; |
||
| 1034 | echo "Open $type on line $line".PHP_EOL; |
||
| 1035 | } |
||
| 1036 | |||
| 1037 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $i, true); |
||
| 1038 | if ($this->debug === true) { |
||
| 1039 | $line = $tokens[$first]['line']; |
||
| 1040 | $type = $tokens[$first]['type']; |
||
| 1041 | echo "\t* first token is $first ($type) on line $line *".PHP_EOL; |
||
| 1042 | } |
||
| 1043 | |||
| 1044 | while ($tokens[$first]['code'] === T_CONSTANT_ENCAPSED_STRING |
||
| 1045 | && $tokens[($first - 1)]['code'] === T_CONSTANT_ENCAPSED_STRING |
||
| 1046 | ) { |
||
| 1047 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, ($first - 1), true); |
||
| 1048 | if ($this->debug === true) { |
||
| 1049 | $line = $tokens[$first]['line']; |
||
| 1050 | $type = $tokens[$first]['type']; |
||
| 1051 | echo "\t* found multi-line string; amended first token is $first ($type) on line $line *".PHP_EOL; |
||
| 1052 | } |
||
| 1053 | } |
||
| 1054 | |||
| 1055 | $currentIndent = (($tokens[$first]['column'] - 1) + $this->indent); |
||
| 1056 | $openScopes[$tokens[$i]['scope_closer']] = $tokens[$i]['scope_condition']; |
||
| 1057 | |||
| 1058 | if (isset($adjustments[$first]) === true) { |
||
| 1059 | $currentIndent += $adjustments[$first]; |
||
| 1060 | } |
||
| 1061 | |||
| 1062 | // Make sure it is divisible by our expected indent. |
||
| 1063 | $currentIndent = (int) (floor($currentIndent / $this->indent) * $this->indent); |
||
| 1064 | $i = $tokens[$i]['scope_opener']; |
||
| 1065 | $setIndents[$i] = $currentIndent; |
||
| 1066 | |||
| 1067 | if ($this->debug === true) { |
||
| 1068 | $type = $tokens[$i]['type']; |
||
| 1069 | echo "\t=> indent set to $currentIndent by token $i ($type)".PHP_EOL; |
||
| 1070 | } |
||
| 1071 | |||
| 1072 | continue; |
||
| 1073 | }//end if |
||
| 1074 | |||
| 1075 | // Scope openers increase the indent level. |
||
| 1076 | if (isset($tokens[$i]['scope_condition']) === true |
||
| 1077 | && isset($tokens[$i]['scope_opener']) === true |
||
| 1078 | && $tokens[$i]['scope_opener'] === $i |
||
| 1079 | ) { |
||
| 1080 | $closer = $tokens[$i]['scope_closer']; |
||
| 1081 | if ($tokens[$i]['line'] === $tokens[$closer]['line']) { |
||
| 1082 | if ($this->debug === true) { |
||
| 1083 | $line = $tokens[$i]['line']; |
||
| 1084 | $type = $tokens[$i]['type']; |
||
| 1085 | echo "* ignoring single-line $type on line $line".PHP_EOL; |
||
| 1086 | } |
||
| 1087 | |||
| 1088 | $i = $closer; |
||
| 1089 | continue; |
||
| 1090 | } |
||
| 1091 | |||
| 1092 | $condition = $tokens[$tokens[$i]['scope_condition']]['code']; |
||
| 1093 | if (isset(Tokens::$scopeOpeners[$condition]) === true |
||
| 1094 | && in_array($condition, $this->nonIndentingScopes) === false |
||
| 1095 | ) { |
||
| 1096 | if ($this->debug === true) { |
||
| 1097 | $line = $tokens[$i]['line']; |
||
| 1098 | $type = $tokens[$tokens[$i]['scope_condition']]['type']; |
||
| 1099 | echo "Open scope ($type) on line $line".PHP_EOL; |
||
| 1100 | } |
||
| 1101 | |||
| 1102 | $currentIndent += $this->indent; |
||
| 1103 | $setIndents[$i] = $currentIndent; |
||
| 1104 | $openScopes[$tokens[$i]['scope_closer']] = $tokens[$i]['scope_condition']; |
||
| 1105 | |||
| 1106 | if ($this->debug === true) { |
||
| 1107 | $type = $tokens[$i]['type']; |
||
| 1108 | echo "\t=> indent set to $currentIndent by token $i ($type)".PHP_EOL; |
||
| 1109 | } |
||
| 1110 | |||
| 1111 | continue; |
||
| 1112 | } |
||
| 1113 | }//end if |
||
| 1114 | |||
| 1115 | // JS objects set the indent level. |
||
| 1116 | if ($phpcsFile->tokenizerType === 'JS' |
||
| 1117 | && $tokens[$i]['code'] === T_OBJECT |
||
| 1118 | ) { |
||
| 1119 | $closer = $tokens[$i]['bracket_closer']; |
||
| 1120 | if ($tokens[$i]['line'] === $tokens[$closer]['line']) { |
||
| 1121 | if ($this->debug === true) { |
||
| 1122 | $line = $tokens[$i]['line']; |
||
| 1123 | echo "* ignoring single-line JS object on line $line".PHP_EOL; |
||
| 1124 | } |
||
| 1125 | |||
| 1126 | $i = $closer; |
||
| 1127 | continue; |
||
| 1128 | } |
||
| 1129 | |||
| 1130 | if ($this->debug === true) { |
||
| 1131 | $line = $tokens[$i]['line']; |
||
| 1132 | echo "Open JS object on line $line".PHP_EOL; |
||
| 1133 | } |
||
| 1134 | |||
| 1135 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $i, true); |
||
| 1136 | $currentIndent = (($tokens[$first]['column'] - 1) + $this->indent); |
||
| 1137 | if (isset($adjustments[$first]) === true) { |
||
| 1138 | $currentIndent += $adjustments[$first]; |
||
| 1139 | } |
||
| 1140 | |||
| 1141 | // Make sure it is divisible by our expected indent. |
||
| 1142 | $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent); |
||
| 1143 | $setIndents[$first] = $currentIndent; |
||
| 1144 | |||
| 1145 | if ($this->debug === true) { |
||
| 1146 | $type = $tokens[$first]['type']; |
||
| 1147 | echo "\t=> indent set to $currentIndent by token $first ($type)".PHP_EOL; |
||
| 1148 | } |
||
| 1149 | |||
| 1150 | continue; |
||
| 1151 | }//end if |
||
| 1152 | |||
| 1153 | // Closing an anon class or function. |
||
| 1154 | if (isset($tokens[$i]['scope_condition']) === true |
||
| 1155 | && $tokens[$i]['scope_closer'] === $i |
||
| 1156 | && ($tokens[$tokens[$i]['scope_condition']]['code'] === T_CLOSURE |
||
| 1157 | || $tokens[$tokens[$i]['scope_condition']]['code'] === T_ANON_CLASS) |
||
| 1158 | ) { |
||
| 1159 | if ($this->debug === true) { |
||
| 1160 | $type = str_replace('_', ' ', strtolower(substr($tokens[$tokens[$i]['scope_condition']]['type'], 2))); |
||
| 1161 | $line = $tokens[$i]['line']; |
||
| 1162 | echo "Close $type on line $line".PHP_EOL; |
||
| 1163 | } |
||
| 1164 | |||
| 1165 | $prev = false; |
||
| 1166 | |||
| 1167 | $object = 0; |
||
| 1168 | if ($phpcsFile->tokenizerType === 'JS') { |
||
| 1169 | $conditions = $tokens[$i]['conditions']; |
||
| 1170 | krsort($conditions, SORT_NUMERIC); |
||
| 1171 | foreach ($conditions as $token => $condition) { |
||
| 1172 | if ($condition === T_OBJECT) { |
||
| 1173 | $object = $token; |
||
| 1174 | break; |
||
| 1175 | } |
||
| 1176 | } |
||
| 1177 | |||
| 1178 | if ($this->debug === true && $object !== 0) { |
||
| 1179 | $line = $tokens[$object]['line']; |
||
| 1180 | echo "\t* token is inside JS object $object on line $line *".PHP_EOL; |
||
| 1181 | } |
||
| 1182 | } |
||
| 1183 | |||
| 1184 | $parens = 0; |
||
| 1185 | if (isset($tokens[$i]['nested_parenthesis']) === true |
||
| 1186 | && empty($tokens[$i]['nested_parenthesis']) === false |
||
| 1187 | ) { |
||
| 1188 | end($tokens[$i]['nested_parenthesis']); |
||
| 1189 | $parens = key($tokens[$i]['nested_parenthesis']); |
||
| 1190 | if ($this->debug === true) { |
||
| 1191 | $line = $tokens[$parens]['line']; |
||
| 1192 | echo "\t* token has nested parenthesis $parens on line $line *".PHP_EOL; |
||
| 1193 | } |
||
| 1194 | } |
||
| 1195 | |||
| 1196 | $condition = 0; |
||
| 1197 | if (isset($tokens[$i]['conditions']) === true |
||
| 1198 | && empty($tokens[$i]['conditions']) === false |
||
| 1199 | ) { |
||
| 1200 | end($tokens[$i]['conditions']); |
||
| 1201 | $condition = key($tokens[$i]['conditions']); |
||
| 1202 | if ($this->debug === true) { |
||
| 1203 | $line = $tokens[$condition]['line']; |
||
| 1204 | $type = $tokens[$condition]['type']; |
||
| 1205 | echo "\t* token is inside condition $condition ($type) on line $line *".PHP_EOL; |
||
| 1206 | } |
||
| 1207 | } |
||
| 1208 | |||
| 1209 | if ($parens > $object && $parens > $condition) { |
||
| 1210 | if ($this->debug === true) { |
||
| 1211 | echo "\t* using parenthesis *".PHP_EOL; |
||
| 1212 | } |
||
| 1213 | |||
| 1214 | $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($parens - 1), null, true); |
||
| 1215 | $object = 0; |
||
| 1216 | $condition = 0; |
||
| 1217 | } else if ($object > 0 && $object >= $condition) { |
||
| 1218 | if ($this->debug === true) { |
||
| 1219 | echo "\t* using object *".PHP_EOL; |
||
| 1220 | } |
||
| 1221 | |||
| 1222 | $prev = $object; |
||
| 1223 | $parens = 0; |
||
| 1224 | $condition = 0; |
||
| 1225 | } else if ($condition > 0) { |
||
| 1226 | if ($this->debug === true) { |
||
| 1227 | echo "\t* using condition *".PHP_EOL; |
||
| 1228 | } |
||
| 1229 | |||
| 1230 | $prev = $condition; |
||
| 1231 | $object = 0; |
||
| 1232 | $parens = 0; |
||
| 1233 | }//end if |
||
| 1234 | |||
| 1235 | if ($prev === false) { |
||
| 1236 | $prev = $phpcsFile->findPrevious([T_EQUAL, T_RETURN], ($tokens[$i]['scope_condition'] - 1), null, false, null, true); |
||
| 1237 | if ($prev === false) { |
||
| 1238 | $prev = $i; |
||
| 1239 | if ($this->debug === true) { |
||
| 1240 | echo "\t* could not find a previous T_EQUAL or T_RETURN token; will use current token *".PHP_EOL; |
||
| 1241 | } |
||
| 1242 | } |
||
| 1243 | } |
||
| 1244 | |||
| 1245 | if ($this->debug === true) { |
||
| 1246 | $line = $tokens[$prev]['line']; |
||
| 1247 | $type = $tokens[$prev]['type']; |
||
| 1248 | echo "\t* previous token is $type on line $line *".PHP_EOL; |
||
| 1249 | } |
||
| 1250 | |||
| 1251 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true); |
||
| 1252 | if ($this->debug === true) { |
||
| 1253 | $line = $tokens[$first]['line']; |
||
| 1254 | $type = $tokens[$first]['type']; |
||
| 1255 | echo "\t* first token on line $line is $first ($type) *".PHP_EOL; |
||
| 1256 | } |
||
| 1257 | |||
| 1258 | $prev = $phpcsFile->findStartOfStatement($first); |
||
| 1259 | if ($prev !== $first) { |
||
| 1260 | // This is not the start of the statement. |
||
| 1261 | if ($this->debug === true) { |
||
| 1262 | $line = $tokens[$prev]['line']; |
||
| 1263 | $type = $tokens[$prev]['type']; |
||
| 1264 | echo "\t* amended previous is $type on line $line *".PHP_EOL; |
||
| 1265 | } |
||
| 1266 | |||
| 1267 | $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true); |
||
| 1268 | if ($this->debug === true) { |
||
| 1269 | $line = $tokens[$first]['line']; |
||
| 1270 | $type = $tokens[$first]['type']; |
||
| 1271 | echo "\t* amended first token is $first ($type) on line $line *".PHP_EOL; |
||
| 1272 | } |
||
| 1273 | } |
||
| 1274 | |||
| 1275 | $currentIndent = ($tokens[$first]['column'] - 1); |
||
| 1276 | if ($object > 0 || $condition > 0) { |
||
| 1277 | $currentIndent += $this->indent; |
||
| 1278 | } |
||
| 1279 | |||
| 1280 | if (isset($tokens[$first]['scope_closer']) === true |
||
| 1281 | && $tokens[$first]['scope_closer'] === $first |
||
| 1282 | ) { |
||
| 1283 | if ($this->debug === true) { |
||
| 1284 | echo "\t* first token is a scope closer *".PHP_EOL; |
||
| 1285 | } |
||
| 1286 | |||
| 1287 | if ($condition === 0 || $tokens[$condition]['scope_opener'] < $first) { |
||
| 1288 | $currentIndent = $setIndents[$first]; |
||
| 1289 | } else if ($this->debug === true) { |
||
| 1290 | echo "\t* ignoring scope closer *".PHP_EOL; |
||
| 1291 | } |
||
| 1292 | } |
||
| 1293 | |||
| 1294 | // Make sure it is divisible by our expected indent. |
||
| 1295 | $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent); |
||
| 1296 | $setIndents[$first] = $currentIndent; |
||
| 1297 | |||
| 1298 | if ($this->debug === true) { |
||
| 1299 | $type = $tokens[$first]['type']; |
||
| 1300 | echo "\t=> indent set to $currentIndent by token $first ($type)".PHP_EOL; |
||
| 1301 | } |
||
| 1302 | }//end if |
||
| 1303 | }//end for |
||
| 1304 | |||
| 1305 | // Don't process the rest of the file. |
||
| 1306 | return $phpcsFile->numTokens; |
||
| 1307 | |||
| 1312 |