Source for file ScopeIndentSniff.php
Documentation is available at ScopeIndentSniff.php
* Checks that control structures are defined and indented correctly.
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\WhiteSpace;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Config;
class ScopeIndentSniff implements Sniff
* A list of tokenizers this sniff supports.
public $supportedTokenizers = array (
* The number of spaces code should be indented.
* Does the indent need to be exactly right?
* If TRUE, indent needs to be exactly $indent spaces. If FALSE,
* indent needs to be at least $indent spaces (but can be more).
* Should tabs be used for indenting?
* If TRUE, fixes will be made using tabs instead of spaces.
* The size of each tab is important, so it should be specified
* using the --tab-width CLI argument.
public $tabIndent = false;
* The --tab-width CLI value that is being used.
private $tabWidth = null;
* List of tokens not needing to be checked for indentation.
* Useful to allow Sniffs based on this to easily ignore/skip some
* tokens from verification. For example, inline HTML sections
* or PHP open/close tags can escape from here and have their own
public $ignoreIndentationTokens = array ();
* List of tokens not needing to be checked for indentation.
* This is a cached copy of the public version of this var, which
* can be set in a ruleset file, and some core ignored tokens.
private $ignoreIndentation = array ();
* Any scope openers that should not cause an indent.
protected $nonIndentingScopes = array ();
* Show debug output for this sniff.
* Returns an array of tokens this test wants to listen for.
public function register ()
if (defined('PHP_CODESNIFFER_IN_TESTS') === true ) {
return array (T_OPEN_TAG );
* Processes this test, when one of its tokens is encountered.
* @param PHP_CodeSniffer_File $phpcsFile All the tokens found in the document.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
public function process (File $phpcsFile, $stackPtr)
$debug = Config ::getConfigData ('scope_indent_debug');
$this->debug = (bool) $debug;
if ($this->tabWidth === null ) {
if (isset ($phpcsFile->config ->tabWidth ) === false || $phpcsFile->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 = $phpcsFile->config ->tabWidth;
$lastOpenTag = $stackPtr;
$tokens = $phpcsFile->getTokens ();
$first = $phpcsFile->findFirstOnLine (T_INLINE_HTML , $stackPtr);
$trimmed = ltrim($tokens[$first]['content']);
$currentIndent = ($tokens[$stackPtr]['column'] - 1 );
$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->ignoreIndentation) === true ) {
$this->ignoreIndentation = array (T_INLINE_HTML => true );
foreach ($this->ignoreIndentationTokens as $token) {
if (is_int($token) === false ) {
$this->ignoreIndentation[$token] = true;
$this->exact = (bool) $this->exact;
$this->tabIndent = (bool) $this->tabIndent;
for ($i = ($stackPtr + 1 ); $i < $phpcsFile->numTokens; $i++ ) {
// Something has gone very wrong; maybe a parse error.
$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.
// Detect line changes and figure out where the indent is.
if ($tokens[$i]['column'] === 1 ) {
$trimmed = ltrim($tokens[$i]['content']);
if (isset ($tokens[($i + 1 )]) === true
&& $tokens[$i]['line'] === $tokens[($i + 1 )]['line']
$tokenIndent = ($tokens[($i + 1 )]['column'] - 1 );
$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
&& isset ($tokens[$checkToken]['parenthesis_opener']) === true )
&& isset ($tokens[$i]['parenthesis_opener']) === true )
if ($checkToken !== null ) {
$parenCloser = $checkToken;
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']) {
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;
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;
} else if ($condition > 0 ) {
if ($this->debug === true ) {
echo "\t* using condition *".PHP_EOL;
$parenOpener = $condition;
$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;
$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.
$prev = $phpcsFile->findStartOfStatement ($first, T_COMMA);
// 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 ) {
$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;
// 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;
} else if ($this->debug === true ) {
echo "\t * ignoring single-line definition *".PHP_EOL;
// Closing short array bracket should just be indented to at least
// the same level as where it was opened (but can be more).
if ($checkToken !== null ) {
$arrayCloser = $checkToken;
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];
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.
$prev = $phpcsFile->findStartOfStatement ($first, T_COMMA);
// 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;
// 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;
} else if ($this->debug === true ) {
echo "\t * ignoring single-line definition *".PHP_EOL;
// Adjust lines within scopes while auto-fixing.
&& (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']);
$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 ($this->tabIndent === true ) {
$numTabs = floor($padding / $this->tabWidth);
$numSpaces = ($padding - ($numTabs * $this->tabWidth));
if ($checkToken === $i) {
$phpcsFile->fixer ->replaceToken ($checkToken, $padding. $trimmed);
// Easier to just replace the entire indent.
$phpcsFile->fixer ->replaceToken (($checkToken - 1 ), $padding);
if ($this->debug === true ) {
$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;
// 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']))
&& 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'];
$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 ) {
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.
// Handle scope for JS object notation.
if ($phpcsFile->tokenizerType === 'JS'
&& (($checkToken !== null
&& $tokens[$checkToken]['line'] !== $tokens[$tokens[$checkToken]['bracket_opener']]['line'])
&& $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 ) {
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;
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 );
} else if ($condition > 0 ) {
if ($this->debug === true ) {
echo "\t* using condition *".PHP_EOL;
$first = $phpcsFile->findFirstOnLine (T_WHITESPACE , $condition, true );
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 );
$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];
$checkIndent += $this->indent;
$currentIndent += $this->indent;
$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;
&& isset (Tokens ::$scopeOpeners[$tokens[$checkToken]['code']]) === true
&& in_array($tokens[$checkToken]['code'], $this->nonIndentingScopes) === false
&& isset ($tokens[$checkToken]['scope_opener']) === true
if (empty ($openScopes) === false ) {
$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.
&& 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);
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;
// Method prefix indentation has to be exact or else if will break
// the rest of the function declaration, and potentially future ones.
&& isset (Tokens ::$methodPrefixes[$tokens[$checkToken]['code']]) === true
&& $tokens[($checkToken + 1 )]['code'] !== T_DOUBLE_COLON
// 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) {
// 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.
&& ($tokens[$checkToken]['code'] === T_OPEN_TAG
|| $tokens[$checkToken]['code'] === T_OPEN_TAG_WITH_ECHO
|| $tokens[$checkToken]['code'] === T_CLOSE_TAG )
$checkIndent = ($tokens[$checkToken]['column'] - 1 );
$checkIndent = (int) (ceil($checkIndent / $this->indent) * $this->indent);
// Check the line indent.
if ($checkIndent === null ) {
$checkIndent = $currentIndent;
&& isset ($this->ignoreIndentation[$tokens[$checkToken]['code']]) === false
&& (($tokenIndent !== $checkIndent && $exact === true )
|| ($tokenIndent < $checkIndent && $exact === false ))
$type = 'IncorrectExact';
$error = 'Line indented incorrectly; expected ';
if ($this->tabIndent === true ) {
$error .= '%s tabs, found %s';
floor($checkIndent / $this->tabWidth),
floor($tokenIndent / $this->tabWidth),
$error .= '%s spaces, found %s';
if ($this->debug === true ) {
$line = $tokens[$checkToken]['line'];
echo " [Line $line] $message".PHP_EOL;
$fix = $phpcsFile->addFixableError ($error, $checkToken, $type, $data);
if ($fix === true || $this->debug === true ) {
if ($this->tabIndent === true ) {
$numTabs = floor($checkIndent / $this->tabWidth);
$numSpaces = ($checkIndent - ($numTabs * $this->tabWidth));
} else if ($checkIndent > 0 ) {
if ($checkToken === $i) {
$accepted = $phpcsFile->fixer ->replaceToken ($checkToken, $padding. $trimmed);
// 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;
// Assume the change would be applied and continue
// checking indents under this assumption. This gives more
// technically accurate error messages.
$adjustments[$checkToken] = ($checkIndent - $tokenIndent);
if ($checkToken !== null ) {
// Completely skip here/now docs as the indent is a part of the
if ($tokens[$i]['code'] === T_START_HEREDOC
$i = $phpcsFile->findNext (array (T_END_HEREDOC , T_END_NOWDOC), ($i + 1 ));
// Completely skip multi-line strings as the indent is a part of the
if ($tokens[$i]['code'] === T_CONSTANT_ENCAPSED_STRING
$i = $phpcsFile->findNext ($tokens[$i]['code'], ($i + 1 ), null , true );
// Completely skip doc comments as they tend to have complex
$i = $tokens[$i]['comment_closer'];
// 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'])));
$currentIndent = ($tokens[$i]['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;
// 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 );
if ($lastCloseTag === null ) {
$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;
// Anon classes and functions set the indent based on their own indent level.
$closer = $tokens[$i]['scope_closer'];
if ($tokens[$i]['line'] === $tokens[$closer]['line']) {
if ($this->debug === true ) {
$line = $tokens[$i]['line'];
echo " * ignoring single-line $type on line $line".PHP_EOL;
if ($this->debug === true ) {
$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;
// 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;
$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;
// JS objects set the indent level.
if ($phpcsFile->tokenizerType === 'JS'
$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;
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;
// 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 ) {
$line = $tokens[$i]['line'];
echo " Close $type on line $line".PHP_EOL;
if ($phpcsFile->tokenizerType === 'JS') {
$conditions = $tokens[$i]['conditions'];
krsort($conditions, SORT_NUMERIC );
foreach ($conditions as $token => $condition) {
if ($this->debug === true && $object !== 0 ) {
$line = $tokens[$object]['line'];
echo " \t* token is inside JS object $object on line $line *".PHP_EOL;
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;
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 );
} else if ($object > 0 && $object >= $condition) {
if ($this->debug === true ) {
echo "\t* using object *".PHP_EOL;
} else if ($condition > 0 ) {
if ($this->debug === true ) {
echo "\t* using condition *".PHP_EOL;
$prev = $phpcsFile->findPrevious (array (T_EQUAL, T_RETURN ), ($tokens[$i]['scope_condition'] - 1 ), null , false , null , true );
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);
// 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;
// Don't process the rest of the file.
return $phpcsFile->numTokens;
Documentation generated on Mon, 11 Mar 2019 14:49:57 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|