You are here

public function ScopeIndentSniff::process in Coder 8.2

Same name and namespace in other branches
  1. 8.3 coder_sniffer/Drupal/Sniffs/WhiteSpace/ScopeIndentSniff.php \Drupal\Sniffs\WhiteSpace\ScopeIndentSniff::process()
  2. 8.3.x coder_sniffer/Drupal/Sniffs/WhiteSpace/ScopeIndentSniff.php \Drupal\Sniffs\WhiteSpace\ScopeIndentSniff::process()

Processes this test, when one of its tokens is encountered.

Parameters

\PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.:

int $stackPtr The position of the current token: in the stack passed in $tokens.

Return value

void

File

coder_sniffer/Drupal/Sniffs/WhiteSpace/ScopeIndentSniff.php, line 138

Class

ScopeIndentSniff
Largely copied from \PHP_CodeSniffer\Standards\Generic\Sniffs\WhiteSpace\ScopeIndentSniff, modified to make the exact mode working with comments and multi line statements.

Namespace

Drupal\Sniffs\WhiteSpace

Code

public function process(File $phpcsFile, $stackPtr) {
  $debug = Config::getConfigData('scope_indent_debug');
  if ($debug !== null) {
    $this->_debug = (bool) $debug;
  }
  if ($this->_tabWidth === null) {
    $config = $phpcsFile->config;
    if (isset($config->tabWidth) === false || $config->tabWidth === 0) {

      // We have no idea how wide tabs are, so assume 4 spaces for fixing.
      // It shouldn't really matter because indent checks elsewhere in the
      // standard should fix things up.
      $this->_tabWidth = 4;
    }
    else {
      $this->_tabWidth = $config->tabWidth;
    }
  }
  $currentIndent = 0;
  $lastOpenTag = $stackPtr;
  $lastCloseTag = null;
  $openScopes = array();
  $adjustments = array();
  $setIndents = array();
  $tokens = $phpcsFile
    ->getTokens();
  $first = $phpcsFile
    ->findFirstOnLine(T_INLINE_HTML, $stackPtr);
  $trimmed = ltrim($tokens[$first]['content']);
  if ($trimmed === '') {
    $currentIndent = $tokens[$stackPtr]['column'] - 1;
  }
  else {
    $currentIndent = strlen($tokens[$first]['content']) - strlen($trimmed);
  }
  if ($this->_debug === true) {
    $line = $tokens[$stackPtr]['line'];
    echo "Start with token {$stackPtr} on line {$line} with indent {$currentIndent}" . PHP_EOL;
  }
  if (empty($this->_ignoreIndentationTokens) === true) {
    $this->_ignoreIndentationTokens = array(
      T_INLINE_HTML => true,
    );
    foreach ($this->ignoreIndentationTokens as $token) {
      if (is_int($token) === false) {
        if (defined($token) === false) {
          continue;
        }
        $token = constant($token);
      }
      $this->_ignoreIndentationTokens[$token] = true;
    }
  }

  //end if
  $this->exact = (bool) $this->exact;
  $this->tabIndent = (bool) $this->tabIndent;
  for ($i = $stackPtr + 1; $i < $phpcsFile->numTokens; $i++) {
    if ($i === false) {

      // Something has gone very wrong; maybe a parse error.
      break;
    }
    $checkToken = null;
    $checkIndent = null;
    $exact = (bool) $this->exact;
    if ($exact === true && isset($tokens[$i]['nested_parenthesis']) === true) {

      // Don't check indents exactly between parenthesis as they
      // tend to have custom rules, such as with multi-line function calls
      // and control structure conditions.
      $exact = false;
    }

    // Detect line changes and figure out where the indent is.
    if ($tokens[$i]['column'] === 1) {
      $trimmed = ltrim($tokens[$i]['content']);
      if ($trimmed === '') {
        if (isset($tokens[$i + 1]) === true && $tokens[$i]['line'] === $tokens[$i + 1]['line']) {
          $checkToken = $i + 1;
          $tokenIndent = $tokens[$i + 1]['column'] - 1;
        }
      }
      else {
        $checkToken = $i;
        $tokenIndent = strlen($tokens[$i]['content']) - strlen($trimmed);
      }
    }

    // Closing parenthesis should just be indented to at least
    // the same level as where they were opened (but can be more).
    if ($checkToken !== null && $tokens[$checkToken]['code'] === T_CLOSE_PARENTHESIS && isset($tokens[$checkToken]['parenthesis_opener']) === true || $tokens[$i]['code'] === T_CLOSE_PARENTHESIS && isset($tokens[$i]['parenthesis_opener']) === true) {
      if ($checkToken !== null) {
        $parenCloser = $checkToken;
      }
      else {
        $parenCloser = $i;
      }
      if ($this->_debug === true) {
        $line = $tokens[$i]['line'];
        echo "Closing parenthesis found on line {$line}" . PHP_EOL;
      }
      $parenOpener = $tokens[$parenCloser]['parenthesis_opener'];
      if ($tokens[$parenCloser]['line'] !== $tokens[$parenOpener]['line']) {
        $parens = 0;
        if (isset($tokens[$parenCloser]['nested_parenthesis']) === true && empty($tokens[$parenCloser]['nested_parenthesis']) === false) {
          end($tokens[$parenCloser]['nested_parenthesis']);
          $parens = key($tokens[$parenCloser]['nested_parenthesis']);
          if ($this->_debug === true) {
            $line = $tokens[$parens]['line'];
            echo "\t* token has nested parenthesis {$parens} on line {$line} *" . PHP_EOL;
          }
        }
        $condition = 0;
        if (isset($tokens[$parenCloser]['conditions']) === true && empty($tokens[$parenCloser]['conditions']) === false) {
          end($tokens[$parenCloser]['conditions']);
          $condition = key($tokens[$parenCloser]['conditions']);
          if ($this->_debug === true) {
            $line = $tokens[$condition]['line'];
            $type = $tokens[$condition]['type'];
            echo "\t* token is inside condition {$condition} ({$type}) on line {$line} *" . PHP_EOL;
          }
        }
        if ($parens > $condition) {
          if ($this->_debug === true) {
            echo "\t* using parenthesis *" . PHP_EOL;
          }
          $parenOpener = $parens;
          $condition = 0;
        }
        else {
          if ($condition > 0) {
            if ($this->_debug === true) {
              echo "\t* using condition *" . PHP_EOL;
            }
            $parenOpener = $condition;
            $parens = 0;
          }
        }
        $exact = false;
        $lastOpenTagConditions = array_keys($tokens[$lastOpenTag]['conditions']);
        $lastOpenTagCondition = array_pop($lastOpenTagConditions);
        if ($condition > 0 && $lastOpenTagCondition === $condition) {
          if ($this->_debug === true) {
            echo "\t* open tag is inside condition; using open tag *" . PHP_EOL;
          }
          $checkIndent = $tokens[$lastOpenTag]['column'] - 1;
          if (isset($adjustments[$condition]) === true) {
            $checkIndent += $adjustments[$condition];
          }
          $currentIndent = $checkIndent;
          if ($this->_debug === true) {
            $type = $tokens[$lastOpenTag]['type'];
            echo "\t=> checking indent of {$checkIndent}; main indent set to {$currentIndent} by token {$lastOpenTag} ({$type})" . PHP_EOL;
          }
        }
        else {
          if ($condition > 0 && isset($tokens[$condition]['scope_opener']) === true && isset($setIndents[$tokens[$condition]['scope_opener']]) === true) {
            $checkIndent = $setIndents[$tokens[$condition]['scope_opener']];
            if (isset($adjustments[$condition]) === true) {
              $checkIndent += $adjustments[$condition];
            }
            $currentIndent = $checkIndent;
            if ($this->_debug === true) {
              $type = $tokens[$condition]['type'];
              echo "\t=> checking indent of {$checkIndent}; main indent set to {$currentIndent} by token {$condition} ({$type})" . PHP_EOL;
            }
          }
          else {
            $first = $phpcsFile
              ->findFirstOnLine(T_WHITESPACE, $parenOpener, true);
            $checkIndent = $tokens[$first]['column'] - 1;
            if (isset($adjustments[$first]) === true) {
              $checkIndent += $adjustments[$first];
            }
            if ($this->_debug === true) {
              $line = $tokens[$first]['line'];
              $type = $tokens[$first]['type'];
              echo "\t* first token on line {$line} is {$first} ({$type}) *" . PHP_EOL;
            }
            if ($first === $tokens[$parenCloser]['parenthesis_opener']) {

              // This is unlikely to be the start of the statement, so look
              // back further to find it.
              $first--;
            }
            $prev = $phpcsFile
              ->findStartOfStatement($first, T_COMMA);
            if ($prev !== $first) {

              // This is not the start of the statement.
              if ($this->_debug === true) {
                $line = $tokens[$prev]['line'];
                $type = $tokens[$prev]['type'];
                echo "\t* previous is {$type} on line {$line} *" . PHP_EOL;
              }
              $first = $phpcsFile
                ->findFirstOnLine(T_WHITESPACE, $prev, true);
              $prev = $phpcsFile
                ->findStartOfStatement($first, T_COMMA);
              $first = $phpcsFile
                ->findFirstOnLine(T_WHITESPACE, $prev, true);
              if ($this->_debug === true) {
                $line = $tokens[$first]['line'];
                $type = $tokens[$first]['type'];
                echo "\t* amended first token is {$first} ({$type}) on line {$line} *" . PHP_EOL;
              }
            }
            if (isset($tokens[$first]['scope_closer']) === true && $tokens[$first]['scope_closer'] === $first) {
              if ($this->_debug === true) {
                echo "\t* first token is a scope closer *" . PHP_EOL;
              }
              if (isset($tokens[$first]['scope_condition']) === true) {
                $scopeCloser = $first;
                $first = $phpcsFile
                  ->findFirstOnLine(T_WHITESPACE, $tokens[$scopeCloser]['scope_condition'], true);
                $currentIndent = $tokens[$first]['column'] - 1;
                if (isset($adjustments[$first]) === true) {
                  $currentIndent += $adjustments[$first];
                }

                // Make sure it is divisible by our expected indent.
                if ($tokens[$tokens[$scopeCloser]['scope_condition']]['code'] !== T_CLOSURE) {
                  $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
                }
                $setIndents[$first] = $currentIndent;
                if ($this->_debug === true) {
                  $type = $tokens[$first]['type'];
                  echo "\t=> indent set to {$currentIndent} by token {$first} ({$type})" . PHP_EOL;
                }
              }

              //end if
            }
            else {

              // Don't force current indent to divisible because there could be custom
              // rules in place between parenthesis, such as with arrays.
              $currentIndent = $tokens[$first]['column'] - 1;
              if (isset($adjustments[$first]) === true) {
                $currentIndent += $adjustments[$first];
              }
              $setIndents[$first] = $currentIndent;
              if ($this->_debug === true) {
                $type = $tokens[$first]['type'];
                echo "\t=> checking indent of {$checkIndent}; main indent set to {$currentIndent} by token {$first} ({$type})" . PHP_EOL;
              }
            }

            //end if
          }
        }

        //end if
      }
      else {
        if ($this->_debug === true) {
          echo "\t * ignoring single-line definition *" . PHP_EOL;
        }
      }

      //end if
    }

    //end if

    // Closing short array bracket should just be indented to at least
    // the same level as where it was opened (but can be more).
    if ($tokens[$i]['code'] === T_CLOSE_SHORT_ARRAY || $checkToken !== null && $tokens[$checkToken]['code'] === T_CLOSE_SHORT_ARRAY) {
      if ($checkToken !== null) {
        $arrayCloser = $checkToken;
      }
      else {
        $arrayCloser = $i;
      }
      if ($this->_debug === true) {
        $line = $tokens[$arrayCloser]['line'];
        echo "Closing short array bracket found on line {$line}" . PHP_EOL;
      }
      $arrayOpener = $tokens[$arrayCloser]['bracket_opener'];
      if ($tokens[$arrayCloser]['line'] !== $tokens[$arrayOpener]['line']) {
        $first = $phpcsFile
          ->findFirstOnLine(T_WHITESPACE, $arrayOpener, true);
        $checkIndent = $tokens[$first]['column'] - 1;
        if (isset($adjustments[$first]) === true) {
          $checkIndent += $adjustments[$first];
        }
        $exact = false;
        if ($this->_debug === true) {
          $line = $tokens[$first]['line'];
          $type = $tokens[$first]['type'];
          echo "\t* first token on line {$line} is {$first} ({$type}) *" . PHP_EOL;
        }
        if ($first === $tokens[$arrayCloser]['bracket_opener']) {

          // This is unlikely to be the start of the statement, so look
          // back further to find it.
          $first--;
        }
        $prev = $phpcsFile
          ->findStartOfStatement($first, T_COMMA);
        if ($prev !== $first) {

          // This is not the start of the statement.
          if ($this->_debug === true) {
            $line = $tokens[$prev]['line'];
            $type = $tokens[$prev]['type'];
            echo "\t* previous is {$type} on line {$line} *" . PHP_EOL;
          }
          $first = $phpcsFile
            ->findFirstOnLine(T_WHITESPACE, $prev, true);
          $prev = $phpcsFile
            ->findStartOfStatement($first, T_COMMA);
          $first = $phpcsFile
            ->findFirstOnLine(T_WHITESPACE, $prev, true);
          if ($this->_debug === true) {
            $line = $tokens[$first]['line'];
            $type = $tokens[$first]['type'];
            echo "\t* amended first token is {$first} ({$type}) on line {$line} *" . PHP_EOL;
          }
        }
        if (isset($tokens[$first]['scope_closer']) === true && $tokens[$first]['scope_closer'] === $first) {

          // The first token is a scope closer and would have already
          // been processed and set the indent level correctly, so
          // don't adjust it again.
          if ($this->_debug === true) {
            echo "\t* first token is a scope closer; ignoring closing short array bracket *" . PHP_EOL;
          }
          if (isset($setIndents[$first]) === true) {
            $currentIndent = $setIndents[$first];
            if ($this->_debug === true) {
              echo "\t=> indent reset to {$currentIndent}" . PHP_EOL;
            }
          }
        }
        else {

          // Don't force current indent to be divisible because there could be custom
          // rules in place for arrays.
          $currentIndent = $tokens[$first]['column'] - 1;
          if (isset($adjustments[$first]) === true) {
            $currentIndent += $adjustments[$first];
          }
          $setIndents[$first] = $currentIndent;
          if ($this->_debug === true) {
            $type = $tokens[$first]['type'];
            echo "\t=> checking indent of {$checkIndent}; main indent set to {$currentIndent} by token {$first} ({$type})" . PHP_EOL;
          }
        }

        //end if
      }
      else {
        if ($this->_debug === true) {
          echo "\t * ignoring single-line definition *" . PHP_EOL;
        }
      }

      //end if
    }

    //end if

    // Adjust lines within scopes while auto-fixing.
    if ($checkToken !== null && $exact === false && (empty($tokens[$checkToken]['conditions']) === false || isset($tokens[$checkToken]['scope_opener']) === true && $tokens[$checkToken]['scope_opener'] === $checkToken)) {
      if (empty($tokens[$checkToken]['conditions']) === false) {
        end($tokens[$checkToken]['conditions']);
        $condition = key($tokens[$checkToken]['conditions']);
      }
      else {
        $condition = $tokens[$checkToken]['scope_condition'];
      }
      $first = $phpcsFile
        ->findFirstOnLine(T_WHITESPACE, $condition, true);
      if (isset($adjustments[$first]) === true && ($adjustments[$first] < 0 && $tokenIndent > $currentIndent || $adjustments[$first] > 0 && $tokenIndent < $currentIndent)) {
        $padding = $tokenIndent + $adjustments[$first];
        if ($padding > 0) {
          if ($this->tabIndent === true) {
            $numTabs = floor($padding / $this->_tabWidth);
            $numSpaces = $padding - $numTabs * $this->_tabWidth;
            $padding = str_repeat("\t", $numTabs) . str_repeat(' ', $numSpaces);
          }
          else {
            $padding = str_repeat(' ', $padding);
          }
        }
        else {
          $padding = '';
        }
        if ($checkToken === $i) {
          $phpcsFile->fixer
            ->replaceToken($checkToken, $padding . $trimmed);
        }
        else {

          // Easier to just replace the entire indent.
          $phpcsFile->fixer
            ->replaceToken($checkToken - 1, $padding);
        }
        if ($this->_debug === true) {
          $length = strlen($padding);
          $line = $tokens[$checkToken]['line'];
          $type = $tokens[$checkToken]['type'];
          echo "Indent adjusted to {$length} for {$type} on line {$line}" . PHP_EOL;
        }
        $adjustments[$checkToken] = $adjustments[$first];
        if ($this->_debug === true) {
          $line = $tokens[$checkToken]['line'];
          $type = $tokens[$checkToken]['type'];
          echo "\t=> Add adjustment of " . $adjustments[$checkToken] . " for token {$checkToken} ({$type}) on line {$line}" . PHP_EOL;
        }
      }

      //end if
    }

    //end if

    // Scope closers reset the required indent to the same level as the opening condition.
    if ($checkToken !== null && isset($openScopes[$checkToken]) === true || isset($tokens[$checkToken]['scope_condition']) === true && isset($tokens[$checkToken]['scope_closer']) === true && $tokens[$checkToken]['scope_closer'] === $checkToken && $tokens[$checkToken]['line'] !== $tokens[$tokens[$checkToken]['scope_opener']]['line'] || ($checkToken === null && isset($openScopes[$i]) === true || isset($tokens[$i]['scope_condition']) === true && isset($tokens[$i]['scope_closer']) === true && $tokens[$i]['scope_closer'] === $i && $tokens[$i]['line'] !== $tokens[$tokens[$i]['scope_opener']]['line'])) {
      if ($this->_debug === true) {
        if ($checkToken === null) {
          $type = $tokens[$tokens[$i]['scope_condition']]['type'];
          $line = $tokens[$i]['line'];
        }
        else {
          $type = $tokens[$tokens[$checkToken]['scope_condition']]['type'];
          $line = $tokens[$checkToken]['line'];
        }
        echo "Close scope ({$type}) on line {$line}" . PHP_EOL;
      }
      $scopeCloser = $checkToken;
      if ($scopeCloser === null) {
        $scopeCloser = $i;
      }
      else {
        array_pop($openScopes);
      }
      if (isset($tokens[$scopeCloser]['scope_condition']) === true) {
        $first = $phpcsFile
          ->findFirstOnLine(T_WHITESPACE, $tokens[$scopeCloser]['scope_condition'], true);
        $currentIndent = $tokens[$first]['column'] - 1;
        if (isset($adjustments[$first]) === true) {
          $currentIndent += $adjustments[$first];
        }

        // Make sure it is divisible by our expected indent.
        if ($tokens[$tokens[$scopeCloser]['scope_condition']]['code'] !== T_CLOSURE) {
          $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
        }
        $setIndents[$scopeCloser] = $currentIndent;
        if ($this->_debug === true) {
          $type = $tokens[$scopeCloser]['type'];
          echo "\t=> indent set to {$currentIndent} by token {$scopeCloser} ({$type})" . PHP_EOL;
        }

        // We only check the indent of scope closers if they are
        // curly braces because other constructs tend to have different rules.
        if ($tokens[$scopeCloser]['code'] === T_CLOSE_CURLY_BRACKET) {
          $exact = true;
        }
        else {
          $checkToken = null;
        }
      }

      //end if
    }

    //end if

    // Handle scope for JS object notation.
    if ($phpcsFile->tokenizerType === 'JS' && ($checkToken !== null && $tokens[$checkToken]['code'] === T_CLOSE_OBJECT && $tokens[$checkToken]['line'] !== $tokens[$tokens[$checkToken]['bracket_opener']]['line'] || $checkToken === null && $tokens[$i]['code'] === T_CLOSE_OBJECT && $tokens[$i]['line'] !== $tokens[$tokens[$i]['bracket_opener']]['line'])) {
      if ($this->_debug === true) {
        $line = $tokens[$i]['line'];
        echo "Close JS object on line {$line}" . PHP_EOL;
      }
      $scopeCloser = $checkToken;
      if ($scopeCloser === null) {
        $scopeCloser = $i;
      }
      else {
        array_pop($openScopes);
      }
      $parens = 0;
      if (isset($tokens[$scopeCloser]['nested_parenthesis']) === true && empty($tokens[$scopeCloser]['nested_parenthesis']) === false) {
        end($tokens[$scopeCloser]['nested_parenthesis']);
        $parens = key($tokens[$scopeCloser]['nested_parenthesis']);
        if ($this->_debug === true) {
          $line = $tokens[$parens]['line'];
          echo "\t* token has nested parenthesis {$parens} on line {$line} *" . PHP_EOL;
        }
      }
      $condition = 0;
      if (isset($tokens[$scopeCloser]['conditions']) === true && empty($tokens[$scopeCloser]['conditions']) === false) {
        end($tokens[$scopeCloser]['conditions']);
        $condition = key($tokens[$scopeCloser]['conditions']);
        if ($this->_debug === true) {
          $line = $tokens[$condition]['line'];
          $type = $tokens[$condition]['type'];
          echo "\t* token is inside condition {$condition} ({$type}) on line {$line} *" . PHP_EOL;
        }
      }
      if ($parens > $condition) {
        if ($this->_debug === true) {
          echo "\t* using parenthesis *" . PHP_EOL;
        }
        $first = $phpcsFile
          ->findFirstOnLine(T_WHITESPACE, $parens, true);
        $condition = 0;
      }
      else {
        if ($condition > 0) {
          if ($this->_debug === true) {
            echo "\t* using condition *" . PHP_EOL;
          }
          $first = $phpcsFile
            ->findFirstOnLine(T_WHITESPACE, $condition, true);
          $parens = 0;
        }
        else {
          if ($this->_debug === true) {
            $line = $tokens[$tokens[$scopeCloser]['bracket_opener']]['line'];
            echo "\t* token is not in parenthesis or condition; using opener on line {$line} *" . PHP_EOL;
          }
          $first = $phpcsFile
            ->findFirstOnLine(T_WHITESPACE, $tokens[$scopeCloser]['bracket_opener'], true);
        }
      }

      //end if
      $currentIndent = $tokens[$first]['column'] - 1;
      if (isset($adjustments[$first]) === true) {
        $currentIndent += $adjustments[$first];
      }
      if ($parens > 0 || $condition > 0) {
        $checkIndent = $tokens[$first]['column'] - 1;
        if (isset($adjustments[$first]) === true) {
          $checkIndent += $adjustments[$first];
        }
        if ($condition > 0) {
          $checkIndent += $this->indent;
          $currentIndent += $this->indent;
          $exact = true;
        }
      }
      else {
        $checkIndent = $currentIndent;
      }

      // Make sure it is divisible by our expected indent.
      $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
      $checkIndent = (int) (ceil($checkIndent / $this->indent) * $this->indent);
      $setIndents[$first] = $currentIndent;
      if ($this->_debug === true) {
        $type = $tokens[$first]['type'];
        echo "\t=> checking indent of {$checkIndent}; main indent set to {$currentIndent} by token {$first} ({$type})" . PHP_EOL;
      }
    }

    //end if
    if ($checkToken !== null && isset(Tokens::$scopeOpeners[$tokens[$checkToken]['code']]) === true && in_array($tokens[$checkToken]['code'], $this->nonIndentingScopes) === false && isset($tokens[$checkToken]['scope_opener']) === true) {
      $exact = true;
      $lastOpener = null;
      if (empty($openScopes) === false) {
        end($openScopes);
        $lastOpener = current($openScopes);
      }

      // A scope opener that shares a closer with another token (like multiple
      // CASEs using the same BREAK) needs to reduce the indent level so its
      // indent is checked correctly. It will then increase the indent again
      // (as all openers do) after being checked.
      if ($lastOpener !== null && isset($tokens[$lastOpener]['scope_closer']) === true && $tokens[$lastOpener]['level'] === $tokens[$checkToken]['level'] && $tokens[$lastOpener]['scope_closer'] === $tokens[$checkToken]['scope_closer']) {
        $currentIndent -= $this->indent;
        $setIndents[$lastOpener] = $currentIndent;
        if ($this->_debug === true) {
          $line = $tokens[$i]['line'];
          $type = $tokens[$lastOpener]['type'];
          echo "Shared closer found on line {$line}" . PHP_EOL;
          echo "\t=> indent set to {$currentIndent} by token {$lastOpener} ({$type})" . PHP_EOL;
        }
      }
      if ($tokens[$checkToken]['code'] === T_CLOSURE && $tokenIndent > $currentIndent) {

        // The opener is indented more than needed, which is fine.
        // But just check that it is divisible by our expected indent.
        $checkIndent = (int) (ceil($tokenIndent / $this->indent) * $this->indent);
        $exact = false;
        if ($this->_debug === true) {
          $line = $tokens[$i]['line'];
          echo "Closure found on line {$line}" . PHP_EOL;
          echo "\t=> checking indent of {$checkIndent}; main indent remains at {$currentIndent}" . PHP_EOL;
        }
      }
    }

    //end if

    // Method prefix indentation has to be exact or else if will break
    // the rest of the function declaration, and potentially future ones.
    if ($checkToken !== null && isset(Tokens::$methodPrefixes[$tokens[$checkToken]['code']]) === true && $tokens[$checkToken + 1]['code'] !== T_DOUBLE_COLON) {
      $exact = true;
    }

    // JS property indentation has to be exact or else if will break
    // things like function and object indentation.
    if ($checkToken !== null && $tokens[$checkToken]['code'] === T_PROPERTY) {
      $exact = true;
    }

    // PHP tags needs to be indented to exact column positions
    // so they don't cause problems with indent checks for the code
    // within them, but they don't need to line up with the current indent.
    if ($checkToken !== null && ($tokens[$checkToken]['code'] === T_OPEN_TAG || $tokens[$checkToken]['code'] === T_OPEN_TAG_WITH_ECHO || $tokens[$checkToken]['code'] === T_CLOSE_TAG)) {
      $exact = true;
      $checkIndent = $tokens[$checkToken]['column'] - 1;
      $checkIndent = (int) (ceil($checkIndent / $this->indent) * $this->indent);
    }

    // Check the line indent.
    if ($checkIndent === null) {
      $checkIndent = $currentIndent;
    }

    // If the line starts with "->" we assume this is an indented chained
    // method invocation, so we add one level of indentation.
    if ($checkToken !== null && $tokens[$checkToken]['code'] === T_OBJECT_OPERATOR) {
      $checkIndent += $this->indent;
    }

    // Comments starting with a star have an extra whitespace.
    if ($checkToken !== null && $tokens[$checkToken]['code'] === T_COMMENT) {
      $content = trim($tokens[$checkToken]['content']);
      if ($content[0] === '*') {
        $checkIndent += 1;
      }
    }
    $adjusted = false;
    if ($checkToken !== null && isset($this->_ignoreIndentationTokens[$tokens[$checkToken]['code']]) === false && ($tokenIndent !== $checkIndent && $exact === true || $tokenIndent < $checkIndent && $exact === false)) {
      if ($tokenIndent > $checkIndent) {

        // Ignore multi line statements.
        $before = $phpcsFile
          ->findPrevious(Tokens::$emptyTokens, $checkToken - 1, null, true);
        if ($before !== false && in_array($tokens[$before]['code'], array(
          T_SEMICOLON,
          T_CLOSE_CURLY_BRACKET,
          T_OPEN_CURLY_BRACKET,
          T_COLON,
        )) === false) {
          continue;
        }
      }

      // Skip array closing indentation errors, this is handled by the
      // ArraySniff.
      if ($tokens[$checkToken]['code'] === T_CLOSE_PARENTHESIS && isset($tokens[$checkToken]['parenthesis_owner']) === true && $tokens[$tokens[$checkToken]['parenthesis_owner']]['code'] === T_ARRAY || $tokens[$checkToken]['code'] === T_CLOSE_SHORT_ARRAY) {
        continue;
      }
      $type = 'IncorrectExact';
      $error = 'Line indented incorrectly; expected ';
      if ($exact === false) {
        $error .= 'at least ';
        $type = 'Incorrect';
      }
      if ($this->tabIndent === true) {
        $error .= '%s tabs, found %s';
        $data = array(
          floor($checkIndent / $this->_tabWidth),
          floor($tokenIndent / $this->_tabWidth),
        );
      }
      else {
        $error .= '%s spaces, found %s';
        $data = array(
          $checkIndent,
          $tokenIndent,
        );
      }
      if ($this->_debug === true) {
        $line = $tokens[$checkToken]['line'];
        $message = vsprintf($error, $data);
        echo "[Line {$line}] {$message}" . PHP_EOL;
      }
      $fix = $phpcsFile
        ->addFixableError($error, $checkToken, $type, $data);
      if ($fix === true || $this->_debug === true) {
        $padding = '';
        if ($this->tabIndent === true) {
          $numTabs = floor($checkIndent / $this->_tabWidth);
          if ($numTabs > 0) {
            $numSpaces = $checkIndent - $numTabs * $this->_tabWidth;
            $padding = str_repeat("\t", $numTabs) . str_repeat(' ', $numSpaces);
          }
        }
        else {
          if ($checkIndent > 0) {
            $padding = str_repeat(' ', $checkIndent);
          }
        }
        if ($checkToken === $i) {
          $accepted = $phpcsFile->fixer
            ->replaceToken($checkToken, $padding . $trimmed);
        }
        else {

          // Easier to just replace the entire indent.
          $accepted = $phpcsFile->fixer
            ->replaceToken($checkToken - 1, $padding);
        }
        if ($accepted === true) {
          $adjustments[$checkToken] = $checkIndent - $tokenIndent;
          if ($this->_debug === true) {
            $line = $tokens[$checkToken]['line'];
            $type = $tokens[$checkToken]['type'];
            echo "\t=> Add adjustment of " . $adjustments[$checkToken] . " for token {$checkToken} ({$type}) on line {$line}" . PHP_EOL;
          }
        }
      }
      else {

        // Assume the change would be applied and continue
        // checking indents under this assumption. This gives more
        // technically accurate error messages.
        $adjustments[$checkToken] = $checkIndent - $tokenIndent;
      }

      //end if
    }

    //end if
    if ($checkToken !== null) {
      $i = $checkToken;
    }

    // Completely skip here/now docs as the indent is a part of the
    // content itself.
    if ($tokens[$i]['code'] === T_START_HEREDOC || $tokens[$i]['code'] === T_START_NOWDOC) {
      $i = $phpcsFile
        ->findNext(array(
        T_END_HEREDOC,
        T_END_NOWDOC,
      ), $i + 1);
      continue;
    }

    // Completely skip multi-line strings as the indent is a part of the
    // content itself.
    if ($tokens[$i]['code'] === T_CONSTANT_ENCAPSED_STRING || $tokens[$i]['code'] === T_DOUBLE_QUOTED_STRING) {
      $i = $phpcsFile
        ->findNext($tokens[$i]['code'], $i + 1, null, true);
      $i--;
      continue;
    }

    // Completely skip doc comments as they tend to have complex
    // indentation rules.
    if ($tokens[$i]['code'] === T_DOC_COMMENT_OPEN_TAG) {
      $i = $tokens[$i]['comment_closer'];
      continue;
    }

    // Open tags reset the indent level.
    if ($tokens[$i]['code'] === T_OPEN_TAG || $tokens[$i]['code'] === T_OPEN_TAG_WITH_ECHO) {
      if ($this->_debug === true) {
        $line = $tokens[$i]['line'];
        echo "Open PHP tag found on line {$line}" . PHP_EOL;
      }
      if ($checkToken === null) {
        $first = $phpcsFile
          ->findFirstOnLine(T_WHITESPACE, $i, true);
        $currentIndent = strlen($tokens[$first]['content']) - strlen(ltrim($tokens[$first]['content']));
      }
      else {
        $currentIndent = $tokens[$i]['column'] - 1;
      }
      $lastOpenTag = $i;
      if (isset($adjustments[$i]) === true) {
        $currentIndent += $adjustments[$i];
      }

      // Make sure it is divisible by our expected indent.
      $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
      $setIndents[$i] = $currentIndent;
      if ($this->_debug === true) {
        $type = $tokens[$i]['type'];
        echo "\t=> indent set to {$currentIndent} by token {$i} ({$type})" . PHP_EOL;
      }
      continue;
    }

    //end if

    // Close tags reset the indent level, unless they are closing a tag
    // opened on the same line.
    if ($tokens[$i]['code'] === T_CLOSE_TAG) {
      if ($this->_debug === true) {
        $line = $tokens[$i]['line'];
        echo "Close PHP tag found on line {$line}" . PHP_EOL;
      }
      if ($tokens[$lastOpenTag]['line'] !== $tokens[$i]['line']) {
        $currentIndent = $tokens[$i]['column'] - 1;
        $lastCloseTag = $i;
      }
      else {
        if ($lastCloseTag === null) {
          $currentIndent = 0;
        }
        else {
          $currentIndent = $tokens[$lastCloseTag]['column'] - 1;
        }
      }
      if (isset($adjustments[$i]) === true) {
        $currentIndent += $adjustments[$i];
      }

      // Make sure it is divisible by our expected indent.
      $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
      $setIndents[$i] = $currentIndent;
      if ($this->_debug === true) {
        $type = $tokens[$i]['type'];
        echo "\t=> indent set to {$currentIndent} by token {$i} ({$type})" . PHP_EOL;
      }
      continue;
    }

    //end if

    // Anon classes and functions set the indent based on their own indent level.
    if ($tokens[$i]['code'] === T_CLOSURE || $tokens[$i]['code'] === T_ANON_CLASS) {
      $closer = $tokens[$i]['scope_closer'];
      if ($tokens[$i]['line'] === $tokens[$closer]['line']) {
        if ($this->_debug === true) {
          $type = str_replace('_', ' ', strtolower(substr($tokens[$i]['type'], 2)));
          $line = $tokens[$i]['line'];
          echo "* ignoring single-line {$type} on line {$line}" . PHP_EOL;
        }
        $i = $closer;
        continue;
      }
      if ($this->_debug === true) {
        $type = str_replace('_', ' ', strtolower(substr($tokens[$i]['type'], 2)));
        $line = $tokens[$i]['line'];
        echo "Open {$type} on line {$line}" . PHP_EOL;
      }
      $first = $phpcsFile
        ->findFirstOnLine(T_WHITESPACE, $i, true);
      $currentIndent = $tokens[$first]['column'] - 1 + $this->indent;
      if (isset($adjustments[$first]) === true) {
        $currentIndent += $adjustments[$first];
      }

      // Make sure it is divisible by our expected indent.
      $currentIndent = (int) (floor($currentIndent / $this->indent) * $this->indent);
      $i = $tokens[$i]['scope_opener'];
      $setIndents[$i] = $currentIndent;
      if ($this->_debug === true) {
        $type = $tokens[$i]['type'];
        echo "\t=> indent set to {$currentIndent} by token {$i} ({$type})" . PHP_EOL;
      }
      continue;
    }

    //end if

    // Scope openers increase the indent level.
    if (isset($tokens[$i]['scope_condition']) === true && isset($tokens[$i]['scope_opener']) === true && $tokens[$i]['scope_opener'] === $i) {
      $closer = $tokens[$i]['scope_closer'];
      if ($tokens[$i]['line'] === $tokens[$closer]['line']) {
        if ($this->_debug === true) {
          $line = $tokens[$i]['line'];
          $type = $tokens[$i]['type'];
          echo "* ignoring single-line {$type} on line {$line}" . PHP_EOL;
        }
        $i = $closer;
        continue;
      }
      $condition = $tokens[$tokens[$i]['scope_condition']]['code'];
      if (isset(Tokens::$scopeOpeners[$condition]) === true && in_array($condition, $this->nonIndentingScopes) === false) {
        if ($this->_debug === true) {
          $line = $tokens[$i]['line'];
          $type = $tokens[$tokens[$i]['scope_condition']]['type'];
          echo "Open scope ({$type}) on line {$line}" . PHP_EOL;
        }
        $currentIndent += $this->indent;
        $setIndents[$i] = $currentIndent;
        $openScopes[$tokens[$i]['scope_closer']] = $tokens[$i]['scope_condition'];
        if ($this->_debug === true) {
          $type = $tokens[$i]['type'];
          echo "\t=> indent set to {$currentIndent} by token {$i} ({$type})" . PHP_EOL;
        }
        continue;
      }
    }

    //end if

    // JS objects set the indent level.
    if ($phpcsFile->tokenizerType === 'JS' && $tokens[$i]['code'] === T_OBJECT) {
      $closer = $tokens[$i]['bracket_closer'];
      if ($tokens[$i]['line'] === $tokens[$closer]['line']) {
        if ($this->_debug === true) {
          $line = $tokens[$i]['line'];
          echo "* ignoring single-line JS object on line {$line}" . PHP_EOL;
        }
        $i = $closer;
        continue;
      }
      if ($this->_debug === true) {
        $line = $tokens[$i]['line'];
        echo "Open JS object on line {$line}" . PHP_EOL;
      }
      $first = $phpcsFile
        ->findFirstOnLine(T_WHITESPACE, $i, true);
      $currentIndent = $tokens[$first]['column'] - 1 + $this->indent;
      if (isset($adjustments[$first]) === true) {
        $currentIndent += $adjustments[$first];
      }

      // Make sure it is divisible by our expected indent.
      $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
      $setIndents[$first] = $currentIndent;
      if ($this->_debug === true) {
        $type = $tokens[$first]['type'];
        echo "\t=> indent set to {$currentIndent} by token {$first} ({$type})" . PHP_EOL;
      }
      continue;
    }

    //end if

    // Closing an anon class or function.
    if (isset($tokens[$i]['scope_condition']) === true && $tokens[$i]['scope_closer'] === $i && ($tokens[$tokens[$i]['scope_condition']]['code'] === T_CLOSURE || $tokens[$tokens[$i]['scope_condition']]['code'] === T_ANON_CLASS)) {
      if ($this->_debug === true) {
        $type = str_replace('_', ' ', strtolower(substr($tokens[$tokens[$i]['scope_condition']]['type'], 2)));
        $line = $tokens[$i]['line'];
        echo "Close {$type} on line {$line}" . PHP_EOL;
      }
      $prev = false;
      $object = 0;
      if ($phpcsFile->tokenizerType === 'JS') {
        $conditions = $tokens[$i]['conditions'];
        krsort($conditions, SORT_NUMERIC);
        foreach ($conditions as $token => $condition) {
          if ($condition === T_OBJECT) {
            $object = $token;
            break;
          }
        }
        if ($this->_debug === true && $object !== 0) {
          $line = $tokens[$object]['line'];
          echo "\t* token is inside JS object {$object} on line {$line} *" . PHP_EOL;
        }
      }
      $parens = 0;
      if (isset($tokens[$i]['nested_parenthesis']) === true && empty($tokens[$i]['nested_parenthesis']) === false) {
        end($tokens[$i]['nested_parenthesis']);
        $parens = key($tokens[$i]['nested_parenthesis']);
        if ($this->_debug === true) {
          $line = $tokens[$parens]['line'];
          echo "\t* token has nested parenthesis {$parens} on line {$line} *" . PHP_EOL;
        }
      }
      $condition = 0;
      if (isset($tokens[$i]['conditions']) === true && empty($tokens[$i]['conditions']) === false) {
        end($tokens[$i]['conditions']);
        $condition = key($tokens[$i]['conditions']);
        if ($this->_debug === true) {
          $line = $tokens[$condition]['line'];
          $type = $tokens[$condition]['type'];
          echo "\t* token is inside condition {$condition} ({$type}) on line {$line} *" . PHP_EOL;
        }
      }
      if ($parens > $object && $parens > $condition) {
        if ($this->_debug === true) {
          echo "\t* using parenthesis *" . PHP_EOL;
        }
        $prev = $phpcsFile
          ->findPrevious(Tokens::$emptyTokens, $parens - 1, null, true);
        $object = 0;
        $condition = 0;
      }
      else {
        if ($object > 0 && $object >= $condition) {
          if ($this->_debug === true) {
            echo "\t* using object *" . PHP_EOL;
          }
          $prev = $object;
          $parens = 0;
          $condition = 0;
        }
        else {
          if ($condition > 0) {
            if ($this->_debug === true) {
              echo "\t* using condition *" . PHP_EOL;
            }
            $prev = $condition;
            $object = 0;
            $parens = 0;
          }
        }
      }

      //end if
      if ($prev === false) {
        $prev = $phpcsFile
          ->findPrevious(array(
          T_EQUAL,
          T_RETURN,
        ), $tokens[$i]['scope_condition'] - 1, null, false, null, true);
        if ($prev === false) {
          $prev = $i;
          if ($this->_debug === true) {
            echo "\t* could not find a previous T_EQUAL or T_RETURN token; will use current token *" . PHP_EOL;
          }
        }
      }
      if ($this->_debug === true) {
        $line = $tokens[$prev]['line'];
        $type = $tokens[$prev]['type'];
        echo "\t* previous token is {$type} on line {$line} *" . PHP_EOL;
      }
      $first = $phpcsFile
        ->findFirstOnLine(T_WHITESPACE, $prev, true);
      if ($this->_debug === true) {
        $line = $tokens[$first]['line'];
        $type = $tokens[$first]['type'];
        echo "\t* first token on line {$line} is {$first} ({$type}) *" . PHP_EOL;
      }
      $prev = $phpcsFile
        ->findStartOfStatement($first);
      if ($prev !== $first) {

        // This is not the start of the statement.
        if ($this->_debug === true) {
          $line = $tokens[$prev]['line'];
          $type = $tokens[$prev]['type'];
          echo "\t* amended previous is {$type} on line {$line} *" . PHP_EOL;
        }
        $first = $phpcsFile
          ->findFirstOnLine(T_WHITESPACE, $prev, true);
        if ($this->_debug === true) {
          $line = $tokens[$first]['line'];
          $type = $tokens[$first]['type'];
          echo "\t* amended first token is {$first} ({$type}) on line {$line} *" . PHP_EOL;
        }
      }
      $currentIndent = $tokens[$first]['column'] - 1;
      if ($object > 0 || $condition > 0) {
        $currentIndent += $this->indent;
      }
      if (isset($tokens[$first]['scope_closer']) === true && $tokens[$first]['scope_closer'] === $first) {
        if ($this->_debug === true) {
          echo "\t* first token is a scope closer *" . PHP_EOL;
        }
        if ($condition === 0 || $tokens[$condition]['scope_opener'] < $first) {
          $currentIndent = $setIndents[$first];
        }
        else {
          if ($this->_debug === true) {
            echo "\t* ignoring scope closer *" . PHP_EOL;
          }
        }
      }

      // Make sure it is divisible by our expected indent.
      $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
      $setIndents[$first] = $currentIndent;
      if ($this->_debug === true) {
        $type = $tokens[$first]['type'];
        echo "\t=> indent set to {$currentIndent} by token {$first} ({$type})" . PHP_EOL;
      }
    }

    //end if
  }

  //end for

  // Don't process the rest of the file.
  return $phpcsFile->numTokens;
}