annotate vendor/drupal/coder/coder_sniffer/Drupal/Sniffs/WhiteSpace/ScopeIndentSniff.php @ 19:fa3358dc1485 tip

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