class TodoCommentSniff in Coder 8.3
Same name and namespace in other branches
- 8.3.x coder_sniffer/Drupal/Sniffs/Commenting/TodoCommentSniff.php \Drupal\Sniffs\Commenting\TodoCommentSniff
Parses and verifies that comments use the correct @todo format.
@category PHP @package PHP_CodeSniffer @link http://pear.php.net/package/PHP_CodeSniffer
Hierarchy
- class \Drupal\Sniffs\Commenting\TodoCommentSniff implements \PHP_CodeSniffer\Sniffs\Sniff
Expanded class hierarchy of TodoCommentSniff
File
- coder_sniffer/
Drupal/ Sniffs/ Commenting/ TodoCommentSniff.php, line 23
Namespace
Drupal\Sniffs\CommentingView source
class TodoCommentSniff implements Sniff {
/**
* Show debug output for this sniff.
*
* Use phpcs --runtime-set todo_debug true
*
* @var boolean
*/
private $debug = false;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array<int|string>
*/
public function register() {
if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
$this->debug = false;
}
return [
T_COMMENT,
T_DOC_COMMENT_TAG,
T_DOC_COMMENT_STRING,
];
}
//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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 void
*/
public function process(File $phpcsFile, $stackPtr) {
$debug = Config::getConfigData('todo_debug');
if ($debug !== null) {
$this->debug = (bool) $debug;
}
$tokens = $phpcsFile
->getTokens();
if ($this->debug === true) {
echo "\n------\n\$tokens[{$stackPtr}] = " . print_r($tokens[$stackPtr], true) . PHP_EOL;
echo 'code = ' . $tokens[$stackPtr]['code'] . ', type = ' . $tokens[$stackPtr]['type'] . "\n";
}
// Standard comments and multi-line comments where the "@" is missing so
// it does not register as a T_DOC_COMMENT_TAG.
if ($tokens[$stackPtr]['code'] === T_COMMENT || $tokens[$stackPtr]['code'] === T_DOC_COMMENT_STRING) {
$comment = $tokens[$stackPtr]['content'];
if ($this->debug === true) {
echo "Getting \$comment from \$tokens[{$stackPtr}]['content']\n";
}
$this
->checkTodoFormat($phpcsFile, $stackPtr, $comment, $tokens);
}
else {
if ($tokens[$stackPtr]['code'] === T_DOC_COMMENT_TAG) {
// Document comment tag (i.e. comments that begin with "@").
// Determine if this is related at all and build the full comment line
// from the various segments that the line is parsed into.
$expression = '/^@to/i';
$comment = $tokens[$stackPtr]['content'];
if ((bool) preg_match($expression, $comment) === true) {
if ($this->debug === true) {
echo "Attempting to build comment\n";
}
$index = $stackPtr + 1;
while ($tokens[$index]['line'] === $tokens[$stackPtr]['line']) {
$comment .= $tokens[$index]['content'];
$index++;
}
if ($this->debug === true) {
echo "Result comment = {$comment}\n";
}
$this
->checkTodoFormat($phpcsFile, $stackPtr, $comment, $tokens);
}
//end if
}
}
//end if
}
//end process()
/**
* Checks a comment string for the correct syntax.
*
* @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.
* @param string $comment The comment text.
* @param array<array> $tokens The token data.
*
* @return void
*/
private function checkTodoFormat(File $phpcsFile, int $stackPtr, string $comment, array $tokens) {
if ($this->debug === true) {
echo "Checking \$comment = '{$comment}'\n";
}
$expression = '/(?x) # Set free-space mode to allow this commenting.
^(\\/|\\s)* # At the start optionally match any forward slashes and spaces
(?i) # set case-insensitive mode.
(?=( # Start a positive non-consuming look-ahead to find all possible todos
@+to(-|\\s|)+do # if one or more @ allow spaces and - between the to and do.
\\h*(-|:)* # Also match the trailing "-" or ":" so they can be replaced.
| # or
to(-)*do # If no @ then only accept todo or to-do or to--do, etc, no spaces.
(\\s-|:)* # Also match the trailing "-" or ":" so they can be replaced.
))
(?-i) # Reset to case-sensitive
(?! # Start another non-consuming look-ahead, this time negative
@todo\\s # It has to match lower-case @todo followed by one space
(?!-|:)\\S # and then any non-space except "-" or ":".
)/m';
if ((bool) preg_match($expression, $comment, $matches) === true) {
if ($this->debug === true) {
echo "Failed regex - give message\n";
}
$commentTrimmed = trim($comment, " /\r\n");
if ($commentTrimmed === '@todo') {
// We can't fix a comment that doesn't have any text.
$phpcsFile
->addWarning("'%s' should match the format '@todo Fix problem X here.'", $stackPtr, 'TodoFormat', [
$commentTrimmed,
]);
$fix = false;
}
else {
// Comments with description text are fixable.
$fix = $phpcsFile
->addFixableWarning("'%s' should match the format '@todo Fix problem X here.'", $stackPtr, 'TodoFormat', [
$commentTrimmed,
]);
}
if ($fix === true) {
if ($tokens[$stackPtr]['code'] === T_DOC_COMMENT_TAG) {
// Rewrite the comment past the token content to an empty
// string as part of it may be part of the match, but not in
// the token content. Then replace the token content with
// the fixed comment from the matched content.
$phpcsFile->fixer
->beginChangeset();
$index = $stackPtr + 1;
while ($tokens[$index]['line'] === $tokens[$stackPtr]['line']) {
$phpcsFile->fixer
->replaceToken($index, '');
$index++;
}
$fixedTodo = str_replace($matches[2], '@todo ', $comment);
$phpcsFile->fixer
->replaceToken($stackPtr, $fixedTodo);
$phpcsFile->fixer
->endChangeset();
}
else {
// The full comment line text is available here, so the
// replacement is fairly straightforward.
$fixedTodo = str_replace($matches[2], '@todo ', $tokens[$stackPtr]['content']);
$phpcsFile->fixer
->replaceToken($stackPtr, $fixedTodo);
}
//end if
}
//end if
}
//end if
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
TodoCommentSniff:: |
private | property | Show debug output for this sniff. | |
TodoCommentSniff:: |
private | function | Checks a comment string for the correct syntax. | |
TodoCommentSniff:: |
public | function | Processes this test, when one of its tokens is encountered. | |
TodoCommentSniff:: |
public | function | Returns an array of tokens this test wants to listen for. |