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\Files\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, array (T_COMMA, T_DOUBLE_ARROW ));
// 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, array (T_COMMA, T_DOUBLE_ARROW ));
$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;
} else if ($tokens[$first]['code'] === T_WHITESPACE ) {
$first = $phpcsFile->findNext (T_WHITESPACE , ($first + 1 ), null , true );
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 ($phpcsFile->fixer ->enabled === true ) {
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 );
if ($this->debug === true ) {
$line = $tokens[$first]['line'];
$type = $tokens[$first]['type'];
echo " \t* first token is $first ($type) on line $line *".PHP_EOL;
while ($tokens[$first]['code'] === T_CONSTANT_ENCAPSED_STRING
&& $tokens[($first - 1 )]['code'] === T_CONSTANT_ENCAPSED_STRING
$first = $phpcsFile->findFirstOnLine (T_WHITESPACE , ($first - 1 ), true );
if ($this->debug === true ) {
$line = $tokens[$first]['line'];
$type = $tokens[$first]['type'];
echo " \t* found multi-line string; amended first token is $first ($type) on line $line *".PHP_EOL;
$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);
// Special case for ELSE statements that are not on the same
// line as the previous IF statements closing brace. They still need
// to have the same indent or it will break code after the block.
if ($checkToken !== null && $tokens[$checkToken]['code'] === T_ELSE ) {
if ($checkIndent === null ) {
$checkIndent = $currentIndent;
The indent of the line is checked by the following IF block.
Up until now, we've just been figuring out what the indent
After this IF block, we adjust the indent again for
the checking of future line.
&& 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 );
if ($this->debug === true ) {
$line = $tokens[$first]['line'];
$type = $tokens[$first]['type'];
echo " \t* first token is $first ($type) on line $line *".PHP_EOL;
while ($tokens[$first]['code'] === T_CONSTANT_ENCAPSED_STRING
&& $tokens[($first - 1 )]['code'] === T_CONSTANT_ENCAPSED_STRING
$first = $phpcsFile->findFirstOnLine (T_WHITESPACE , ($first - 1 ), true );
if ($this->debug === true ) {
$line = $tokens[$first]['line'];
$type = $tokens[$first]['type'];
echo " \t* found multi-line string; amended first token is $first ($type) on line $line *".PHP_EOL;
$currentIndent = (($tokens[$first]['column'] - 1 ) + $this->indent);
$openScopes[$tokens[$i]['scope_closer']] = $tokens[$i]['scope_condition'];
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 15:27:46 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|