/** * 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. * * @return void */ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // If this is an inline condition (ie. there is no scope opener), then // return, as this is not a new scope. if (isset($tokens[$stackPtr]['scope_closer']) === false) { return; } // We need to actually find the first piece of content on this line, // as if this is a method with tokens before it (public, static etc) // or an if with an else before it, then we need to start the scope // checking from there, rather than the current token. $lineStart = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr, true); $startColumn = $tokens[$lineStart]['column']; $scopeStart = $tokens[$stackPtr]['scope_opener']; $scopeEnd = $tokens[$stackPtr]['scope_closer']; // Check that the closing brace is on it's own line. $lastContent = $phpcsFile->findPrevious(array(T_INLINE_HTML, T_WHITESPACE, T_OPEN_TAG), $scopeEnd - 1, $scopeStart, true); if ($tokens[$lastContent]['line'] === $tokens[$scopeEnd]['line']) { $error = 'Closing brace must be on a line by itself'; $fix = $phpcsFile->addFixableError($error, $scopeEnd, 'ContentBefore'); if ($fix === true) { $phpcsFile->fixer->addNewlineBefore($scopeEnd); } return; } // Check now that the closing brace is lined up correctly. $lineStart = $phpcsFile->findFirstOnLine(T_WHITESPACE, $scopeEnd, true); $braceIndent = $tokens[$lineStart]['column']; if ($tokens[$stackPtr]['code'] !== T_DEFAULT && $tokens[$stackPtr]['code'] !== T_CASE && $braceIndent !== $startColumn) { $error = 'Closing brace indented incorrectly; expected %s spaces, found %s'; $data = array($startColumn - 1, $braceIndent - 1); $fix = $phpcsFile->addFixableError($error, $scopeEnd, 'Indent', $data); if ($fix === true) { $diff = $startColumn - $braceIndent; if ($diff > 0) { $phpcsFile->fixer->addContentBefore($scopeEnd, str_repeat(' ', $diff)); } else { $phpcsFile->fixer->substrToken($scopeEnd - 1, 0, $diff); } } } //end if }
/** * 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. * * @return void */ public function process(File $phpcsFile, $stackPtr) { $debug = Config::getConfigData('scope_indent_debug'); if ($debug !== null) { $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 = 4; } else { $this->tabWidth = $phpcsFile->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->ignoreIndentation) === true) { $this->ignoreIndentation = array(T_INLINE_HTML => true); foreach ($this->ignoreIndentationTokens as $token) { if (is_int($token) === false) { if (defined($token) === false) { continue; } $token = constant($token); } $this->ignoreIndentation[$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 && isset($tokens[$i]['parenthesis_owner']) === true && $tokens[$tokens[$i]['parenthesis_owner']]['code'] === T_ARRAY) { 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; if ($condition > 0 && $lastOpenTag > $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; } $adjusted = false; if ($checkToken !== null && isset($this->ignoreIndentation[$tokens[$checkToken]['code']]) === false && ($tokenIndent !== $checkIndent && $exact === true || $tokenIndent < $checkIndent && $exact === false)) { $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; }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. * * @return void */ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]['scope_opener']) === false) { return; } if ($tokens[$stackPtr]['code'] === T_FUNCTION && (bool) $this->checkFunctions === false || $tokens[$stackPtr]['code'] === T_CLOSURE && (bool) $this->checkClosures === false) { return; } $openingBrace = $tokens[$stackPtr]['scope_opener']; $closeBracket = $tokens[$stackPtr]['parenthesis_closer']; if ($tokens[$stackPtr]['code'] === T_CLOSURE) { $use = $phpcsFile->findNext(T_USE, $closeBracket + 1, $tokens[$stackPtr]['scope_opener']); if ($use !== false) { $openBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $use + 1); $closeBracket = $tokens[$openBracket]['parenthesis_closer']; } } $functionLine = $tokens[$closeBracket]['line']; $braceLine = $tokens[$openingBrace]['line']; $lineDifference = $braceLine - $functionLine; if ($lineDifference === 0) { $error = 'Opening brace should be on a new line'; $fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceOnSameLine'); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); $indent = $phpcsFile->findFirstOnLine(array(), $openingBrace); if ($tokens[$indent]['code'] === T_WHITESPACE) { $phpcsFile->fixer->addContentBefore($openingBrace, $tokens[$indent]['content']); } $phpcsFile->fixer->addNewlineBefore($openingBrace); $phpcsFile->fixer->endChangeset(); } $phpcsFile->recordMetric($stackPtr, 'Function opening brace placement', 'same line'); } else { if ($lineDifference > 1) { $error = 'Opening brace should be on the line after the declaration; found %s blank line(s)'; $data = array($lineDifference - 1); $fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceSpacing', $data); if ($fix === true) { for ($i = $tokens[$stackPtr]['parenthesis_closer'] + 1; $i < $openingBrace; $i++) { if ($tokens[$i]['line'] === $braceLine) { $phpcsFile->fixer->addNewLineBefore($i); break; } $phpcsFile->fixer->replaceToken($i, ''); } } } } //end if $next = $phpcsFile->findNext(T_WHITESPACE, $openingBrace + 1, null, true); if ($tokens[$next]['line'] === $tokens[$openingBrace]['line']) { if ($next === $tokens[$stackPtr]['scope_closer']) { // Ignore empty functions. return; } $error = 'Opening brace must be the last content on the line'; $fix = $phpcsFile->addFixableError($error, $openingBrace, 'ContentAfterBrace'); if ($fix === true) { $phpcsFile->fixer->addNewline($openingBrace); } } // Only continue checking if the opening brace looks good. if ($lineDifference !== 1) { return; } // We need to actually find the first piece of content on this line, // as if this is a method with tokens before it (public, static etc) // or an if with an else before it, then we need to start the scope // checking from there, rather than the current token. $lineStart = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr, true); // The opening brace is on the correct line, now it needs to be // checked to be correctly indented. $startColumn = $tokens[$lineStart]['column']; $braceIndent = $tokens[$openingBrace]['column']; if ($braceIndent !== $startColumn) { $expected = $startColumn - 1; $found = $braceIndent - 1; $error = 'Opening brace indented incorrectly; expected %s spaces, found %s'; $data = array($expected, $found); $fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceIndent', $data); if ($fix === true) { $indent = str_repeat(' ', $expected); if ($found === 0) { $phpcsFile->fixer->addContentBefore($openingBrace, $indent); } else { $phpcsFile->fixer->replaceToken($openingBrace - 1, $indent); } } } //end if $phpcsFile->recordMetric($stackPtr, 'Function opening brace placement', 'new line'); }
/** * Processes the closing section of a class declaration. * * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. * * @return void */ public function processClose(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]['scope_closer']) === false) { return; } $closeBrace = $tokens[$stackPtr]['scope_closer']; // Check that the closing brace has one blank line after it. for ($nextContent = $closeBrace + 1; $nextContent < $phpcsFile->numTokens; $nextContent++) { // Ignore comments on the same lines as the brace. if ($tokens[$nextContent]['line'] === $tokens[$closeBrace]['line'] && ($tokens[$nextContent]['code'] === T_WHITESPACE || $tokens[$nextContent]['code'] === T_COMMENT)) { continue; } if ($tokens[$nextContent]['code'] !== T_WHITESPACE) { break; } } if ($nextContent === $phpcsFile->numTokens) { // Ignore the line check as this is the very end of the file. $difference = 1; } else { $difference = $tokens[$nextContent]['line'] - $tokens[$closeBrace]['line'] - 1; } $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, $closeBrace - 1, $stackPtr, true); if ($difference === -1 || $tokens[$lastContent]['line'] === $tokens[$closeBrace]['line']) { $error = 'Closing %s brace must be on a line by itself'; $data = array($tokens[$stackPtr]['content']); $fix = $phpcsFile->addFixableError($error, $closeBrace, 'CloseBraceSameLine', $data); if ($fix === true) { if ($difference === -1) { $phpcsFile->fixer->addNewlineBefore($nextContent); } if ($tokens[$lastContent]['line'] === $tokens[$closeBrace]['line']) { $phpcsFile->fixer->addNewlineBefore($closeBrace); } } } else { if ($tokens[$closeBrace - 1]['code'] === T_WHITESPACE) { $prevContent = $tokens[$closeBrace - 1]['content']; if ($prevContent !== $phpcsFile->eolChar) { $blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar)); $spaces = strlen($blankSpace); if ($spaces !== 0) { if ($tokens[$closeBrace - 1]['line'] !== $tokens[$closeBrace]['line']) { $error = 'Expected 0 spaces before closing brace; newline found'; $phpcsFile->addError($error, $closeBrace, 'NewLineBeforeCloseBrace'); } else { $error = 'Expected 0 spaces before closing brace; %s found'; $data = array($spaces); $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpaceBeforeCloseBrace', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($closeBrace - 1, ''); } } } } } } //end if if ($difference !== -1 && $difference !== 1) { $error = 'Closing brace of a %s must be followed by a single blank line; found %s'; $data = array($tokens[$stackPtr]['content'], $difference); $fix = $phpcsFile->addFixableError($error, $closeBrace, 'NewlinesAfterCloseBrace', $data); if ($fix === true) { if ($difference === 0) { $first = $phpcsFile->findFirstOnLine(array(), $nextContent, true); $phpcsFile->fixer->addNewlineBefore($first); } else { $phpcsFile->fixer->beginChangeset(); for ($i = $closeBrace + 1; $i < $nextContent; $i++) { if ($tokens[$i]['line'] <= $tokens[$closeBrace]['line'] + 1) { continue; } else { if ($tokens[$i]['line'] === $tokens[$nextContent]['line']) { break; } } $phpcsFile->fixer->replaceToken($i, ''); } $phpcsFile->fixer->endChangeset(); } } } //end if }
/** * Processes the function tokens within the class. * * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found. * @param int $stackPtr The position where the token was found. * * @return void */ protected function processMemberVar(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $ignore = Tokens::$methodPrefixes; $ignore[] = T_VAR; $ignore[] = T_WHITESPACE; $start = $stackPtr; $prev = $phpcsFile->findPrevious($ignore, $stackPtr - 1, null, true); if (isset(Tokens::$commentTokens[$tokens[$prev]['code']]) === true) { // Assume the comment belongs to the member var if it is on a line by itself. $prevContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, $prev - 1, null, true); if ($tokens[$prevContent]['line'] !== $tokens[$prev]['line']) { // Check the spacing, but then skip it. $foundLines = $tokens[$stackPtr]['line'] - $tokens[$prev]['line'] - 1; if ($foundLines > 0) { $error = 'Expected 0 blank lines after member var comment; %s found'; $data = array($foundLines); $fix = $phpcsFile->addFixableError($error, $prev, 'AfterComment', $data); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); // Inline comments have the newline included in the content but // docblock do not. if ($tokens[$prev]['code'] === T_COMMENT) { $phpcsFile->fixer->replaceToken($prev, rtrim($tokens[$prev]['content'])); } for ($i = $prev + 1; $i <= $stackPtr; $i++) { if ($tokens[$i]['line'] === $tokens[$stackPtr]['line']) { break; } $phpcsFile->fixer->replaceToken($i, ''); } $phpcsFile->fixer->addNewline($prev); $phpcsFile->fixer->endChangeset(); } } //end if $start = $prev; } //end if } //end if // There needs to be 1 blank line before the var, not counting comments. if ($start === $stackPtr) { // No comment found. $first = $phpcsFile->findFirstOnLine(Tokens::$emptyTokens, $start, true); if ($first === false) { $first = $start; } } else { if ($tokens[$start]['code'] === T_DOC_COMMENT_CLOSE_TAG) { $first = $tokens[$start]['comment_opener']; } else { $first = $phpcsFile->findPrevious(Tokens::$emptyTokens, $start - 1, null, true); $first = $phpcsFile->findNext(Tokens::$commentTokens, $first + 1); } } $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, $first - 1, null, true); $foundLines = $tokens[$first]['line'] - $tokens[$prev]['line'] - 1; if ($foundLines === 1) { return; } $error = 'Expected 1 blank line before member var; %s found'; $data = array($foundLines); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Incorrect', $data); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); for ($i = $prev + 1; $i < $first; $i++) { if ($tokens[$i]['line'] === $tokens[$prev]['line']) { continue; } if ($tokens[$i]['line'] === $tokens[$first]['line']) { $phpcsFile->fixer->addNewline($i - 1); break; } $phpcsFile->fixer->replaceToken($i, ''); } $phpcsFile->fixer->endChangeset(); } //end if }