*
*
*
*
*
*
*
*
*
* ```
*
* @since 1.0.0
*
* @var string[]
*/
public $ignoreAlignmentBefore = [];
/**
* Whether or not potential trailing whitespace on otherwise blank lines should be examined or ignored.
*
* Defaults to `true`, i.e. ignore blank lines.
*
* It is recommended to only set this to `false` if the standard including this sniff does not
* include the `Squiz.WhiteSpace.SuperfluousWhitespace` sniff (which is included in most standards).
*
* @since 1.0.0
*
* @var bool
*/
public $ignoreBlankLines = true;
/**
* The --tab-width CLI value that is being used.
*
* @since 1.0.0
*
* @var int
*/
private $tabWidth;
/**
* Whitespace tokens and tokens which can contain leading whitespace.
*
* A few additional tokens will be added to this list in the register() method.
*
* @since 1.0.0
*
* @var array
*/
private $tokensToCheck = [
\T_WHITESPACE => \T_WHITESPACE,
\T_INLINE_HTML => \T_INLINE_HTML,
\T_DOC_COMMENT_WHITESPACE => \T_DOC_COMMENT_WHITESPACE,
\T_COMMENT => \T_COMMENT,
\T_END_HEREDOC => \T_END_HEREDOC,
\T_END_NOWDOC => \T_END_NOWDOC,
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array
*/
public function register()
{
// Add the ignore annotation tokens to the list of tokens to check.
$this->tokensToCheck += Tokens::$phpcsCommentTokens;
return Collections::phpOpenTags();
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return int Integer stack pointer to skip the rest of the file.
*/
public function process(File $phpcsFile, $stackPtr)
{
/*
* Handle the properties.
*/
if (isset($this->tabWidth) === false || \defined('PHP_CODESNIFFER_IN_TESTS') === true) {
$this->tabWidth = Helper::getTabWidth($phpcsFile);
}
if (isset($this->indent) === true) {
$indent = (int) $this->indent;
} else {
$indent = $this->tabWidth;
}
$ignoreTokens = (array) $this->ignoreAlignmentBefore;
if (empty($ignoreTokens) === false) {
$ignoreTokens = \array_flip($ignoreTokens);
}
/*
* Check the whole file in one go.
*/
$tokens = $phpcsFile->getTokens();
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
if ($tokens[$i]['column'] !== 1) {
// Only interested in the first token on each line.
continue;
}
if (isset($this->tokensToCheck[$tokens[$i]['code']]) === false) {
// Not one of the target tokens.
continue;
}
if ($tokens[$i]['content'] === $phpcsFile->eolChar) {
// Skip completely blank lines.
continue;
}
if (isset($ignoreTokens[$tokens[$i]['type']]) === true
|| (isset($tokens[($i + 1)]) && isset($ignoreTokens[$tokens[($i + 1)]['type']]))
) {
// This is one of the tokens being ignored.
continue;
}
$origContent = null;
if (isset($tokens[$i]['orig_content']) === true) {
$origContent = $tokens[$i]['orig_content'];
}
$spaces = 0;
$length = 0;
$content = '';
$closer = '';
switch ($tokens[$i]['code']) {
case \T_WHITESPACE:
if ($this->ignoreBlankLines === true
&& isset($tokens[($i + 1)])
&& $tokens[$i]['line'] !== $tokens[($i + 1)]['line']
) {
// Skip blank lines which only contain trailing whitespace.
continue 2;
}
$spaces = ($tokens[$i]['length'] % $indent);
break;
case \T_DOC_COMMENT_WHITESPACE:
/*
* Blank lines with trailing whitespace in docblocks are tokenized as
* two T_DOC_COMMENT_WHITESPACE tokens: one for the trailing whitespace,
* one for the new line character.
*/
if ($this->ignoreBlankLines === true
&& isset($tokens[($i + 1)])
&& $tokens[($i + 1)]['content'] === $phpcsFile->eolChar
&& isset($tokens[($i + 2)])
&& $tokens[$i]['line'] !== $tokens[($i + 2)]['line']
) {
// Skip blank lines which only contain trailing whitespace.
continue 2;
}
$spaces = ($tokens[$i]['length'] % $indent);
if (isset($tokens[($i + 1)]) === true
&& ($tokens[($i + 1)]['code'] === \T_DOC_COMMENT_STAR
|| $tokens[($i + 1)]['code'] === \T_DOC_COMMENT_CLOSE_TAG)
&& $spaces !== 0
) {
// One alignment space expected before the *.
--$spaces;
}
break;
case \T_COMMENT:
case \T_INLINE_HTML:
if ($this->ignoreBlankLines === true
&& \trim($tokens[$i]['content']) === ''
&& isset($tokens[($i + 1)])
&& $tokens[$i]['line'] !== $tokens[($i + 1)]['line']
) {
// Skip blank lines which only contain trailing whitespace.
continue 2;
}
// Deliberate fall-through.
case \T_PHPCS_ENABLE:
case \T_PHPCS_DISABLE:
case \T_PHPCS_SET:
case \T_PHPCS_IGNORE:
case \T_PHPCS_IGNORE_FILE:
/*
* Indentation is included in the contents of the token for:
* - inline HTML
* - PHP 7.3+ flexible heredoc/nowdoc closer identifiers (see below);
* - subsequent lines of multi-line comments;
* - PHPCS native annotations when part of a multi-line comment.
*/
$content = \ltrim($tokens[$i]['content']);
$whitespace = \str_replace($content, '', $tokens[$i]['content']);
/*
* If there is no content, this is a blank line in a comment or in inline HTML.
* In that case, use the predetermined length as otherwise the new line character
* at the end of the whitespace will throw the count off.
*/
$length = ($content === '') ? $tokens[$i]['length'] : \strlen($whitespace);
$spaces = ($length % $indent);
/*
* For multi-line star-comments, which use (aligned) stars on subsequent
* lines, we don't want to trigger on the one extra space before the star.
*
* While not 100% correct, don't exclude inline HTML from this check as
* otherwise the sniff would trigger on multi-line /*-style inline javascript comments.
* This may cause false negatives as there is no check for being in a
*