class FileCommentSniff in Coder 8.2
Same name and namespace in other branches
- 8.3 coder_sniffer/Drupal/Sniffs/Commenting/FileCommentSniff.php \Drupal\Sniffs\Commenting\FileCommentSniff
- 8.3.x coder_sniffer/Drupal/Sniffs/Commenting/FileCommentSniff.php \Drupal\Sniffs\Commenting\FileCommentSniff
Parses and verifies the doc comments for files.
Verifies that : <ul> <li>A doc comment exists.</li> <li>There is a blank newline after the @file statement.</li> </ul>
@category PHP @package PHP_CodeSniffer @link http://pear.php.net/package/PHP_CodeSniffer
Hierarchy
- class \Drupal\Sniffs\Commenting\FileCommentSniff implements \PHP_CodeSniffer\Sniffs\Sniff
Expanded class hierarchy of FileCommentSniff
File
- coder_sniffer/
Drupal/ Sniffs/ Commenting/ FileCommentSniff.php, line 29 - Parses and verifies the doc comments for files.
Namespace
Drupal\Sniffs\CommentingView source
class FileCommentSniff implements Sniff {
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = array(
'PHP',
'JS',
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register() {
return array(
T_OPEN_TAG,
);
}
//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 int
*/
public function process(File $phpcsFile, $stackPtr) {
$this->currentFile = $phpcsFile;
$tokens = $phpcsFile
->getTokens();
$commentStart = $phpcsFile
->findNext(T_WHITESPACE, $stackPtr + 1, null, true);
// Files containing exactly one class, interface or trait are allowed to
// ommit a file doc block. If a namespace is used then the file comment must
// be omitted.
$oopKeyword = $phpcsFile
->findNext([
T_CLASS,
T_INTERFACE,
T_TRAIT,
], $stackPtr);
if ($oopKeyword !== false) {
$namespace = $phpcsFile
->findNext(T_NAMESPACE, $stackPtr);
// Check if the file contains multiple classes/interfaces/traits - then a
// file doc block is allowed.
$secondOopKeyword = $phpcsFile
->findNext([
T_CLASS,
T_INTERFACE,
T_TRAIT,
], $oopKeyword + 1);
// Namespaced classes, interfaces and traits should not have an @file doc
// block.
if (($tokens[$commentStart]['code'] === T_DOC_COMMENT_OPEN_TAG || $tokens[$commentStart]['code'] === T_COMMENT) && $secondOopKeyword === false && $namespace !== false) {
$fix = $phpcsFile
->addFixableError('Namespaced classes, interfaces and traits should not begin with a file doc comment', $commentStart, 'NamespaceNoFileDoc');
if ($fix === true) {
$phpcsFile->fixer
->beginChangeset();
for ($i = $commentStart; $i <= $tokens[$commentStart]['comment_closer'] + 1; $i++) {
$phpcsFile->fixer
->replaceToken($i, '');
}
// If, after removing the comment, there are two new lines
// remove them.
if ($tokens[$commentStart - 1]['content'] === "\n" && $tokens[$i]['content'] === "\n") {
$phpcsFile->fixer
->replaceToken($i, '');
}
$phpcsFile->fixer
->endChangeset();
}
}
if ($namespace !== false) {
return $phpcsFile->numTokens + 1;
}
// Search for global functions before and after the class.
$function = $phpcsFile
->findPrevious(T_FUNCTION, $oopKeyword - 1);
if ($function === false) {
$function = $phpcsFile
->findNext(T_FUNCTION, $tokens[$oopKeyword]['scope_closer'] + 1);
}
$fileTag = $phpcsFile
->findNext(T_DOC_COMMENT_TAG, $commentStart + 1, null, false, '@file');
// No other classes, no other global functions and no explicit @file tag
// anywhere means it is ok to skip the file comment.
if ($secondOopKeyword === false && $function === false && $fileTag === false) {
return $phpcsFile->numTokens + 1;
}
}
//end if
if ($tokens[$commentStart]['code'] === T_COMMENT) {
$fix = $phpcsFile
->addFixableError('You must use "/**" style comments for a file comment', $commentStart, 'WrongStyle');
if ($fix === true) {
$content = $tokens[$commentStart]['content'];
// If the comment starts with something like "/**" then we just
// insert a space after the stars.
if (strpos($content, '/**') === 0) {
$phpcsFile->fixer
->replaceToken($commentStart, str_replace('/**', '/** ', $content));
}
else {
if (strpos($content, '/*') === 0) {
// Just turn the /* ... */ style comment into a /** ... */ style
// comment.
$phpcsFile->fixer
->replaceToken($commentStart, str_replace('/*', '/**', $content));
}
else {
$content = trim(ltrim($tokens[$commentStart]['content'], '/# '));
$phpcsFile->fixer
->replaceToken($commentStart, "/**\n * @file\n * {$content}\n */\n");
}
}
}
return $phpcsFile->numTokens + 1;
}
else {
if ($commentStart === false || $tokens[$commentStart]['code'] !== T_DOC_COMMENT_OPEN_TAG) {
$fix = $phpcsFile
->addFixableError('Missing file doc comment', 0, 'Missing');
if ($fix === true) {
// Only PHP has a real opening tag, additional newline at the
// beginning here.
if ($phpcsFile->tokenizerType === 'PHP') {
// In templates add the file doc block to the very beginning of
// the file.
if ($tokens[0]['code'] === T_INLINE_HTML) {
$phpcsFile->fixer
->addContentBefore(0, "<?php\n\n/**\n * @file\n */\n?>\n");
}
else {
$phpcsFile->fixer
->addContent($stackPtr, "\n/**\n * @file\n */\n");
}
}
else {
$phpcsFile->fixer
->addContent($stackPtr, "/**\n * @file\n */\n");
}
}
return $phpcsFile->numTokens + 1;
}
}
//end if
$commentEnd = $tokens[$commentStart]['comment_closer'];
$fileTag = $phpcsFile
->findNext(T_DOC_COMMENT_TAG, $commentStart + 1, $commentEnd, false, '@file');
$next = $phpcsFile
->findNext(T_WHITESPACE, $commentEnd + 1, null, true);
// If there is no @file tag and the next line is a function or class
// definition then the file docblock is mising.
if ($tokens[$next]['line'] === $tokens[$commentEnd]['line'] + 1 && $tokens[$next]['code'] === T_FUNCTION) {
if ($fileTag === false) {
$fix = $phpcsFile
->addFixableError('Missing file doc comment', $stackPtr, 'Missing');
if ($fix === true) {
// Only PHP has a real opening tag, additional newline at the
// beginning here.
if ($phpcsFile->tokenizerType === 'PHP') {
$phpcsFile->fixer
->addContent($stackPtr, "\n/**\n * @file\n */\n");
}
else {
$phpcsFile->fixer
->addContent($stackPtr, "/**\n * @file\n */\n");
}
}
return $phpcsFile->numTokens + 1;
}
}
//end if
if ($fileTag === false || $tokens[$fileTag]['line'] !== $tokens[$commentStart]['line'] + 1) {
$second_line = $phpcsFile
->findNext(array(
T_DOC_COMMENT_STAR,
T_DOC_COMMENT_CLOSE_TAG,
), $commentStart + 1, $commentEnd);
$fix = $phpcsFile
->addFixableError('The second line in the file doc comment must be "@file"', $second_line, 'FileTag');
if ($fix === true) {
if ($fileTag === false) {
$phpcsFile->fixer
->addContent($commentStart, "\n * @file");
}
else {
// Delete the @file tag at its current position and insert one
// after the beginning of the comment.
$phpcsFile->fixer
->beginChangeset();
$phpcsFile->fixer
->addContent($commentStart, "\n * @file");
$phpcsFile->fixer
->replaceToken($fileTag, '');
$phpcsFile->fixer
->endChangeset();
}
}
return $phpcsFile->numTokens + 1;
}
// Exactly one blank line after the file comment.
if ($tokens[$next]['line'] !== $tokens[$commentEnd]['line'] + 2 && $next !== false && $tokens[$next]['code'] !== T_CLOSE_TAG) {
$error = 'There must be exactly one blank line after the file comment';
$fix = $phpcsFile
->addFixableError($error, $commentEnd, 'SpacingAfterComment');
if ($fix === true) {
$phpcsFile->fixer
->beginChangeset();
$uselessLine = $commentEnd + 1;
while ($uselessLine < $next) {
$phpcsFile->fixer
->replaceToken($uselessLine, '');
$uselessLine++;
}
$phpcsFile->fixer
->addContent($commentEnd, "\n\n");
$phpcsFile->fixer
->endChangeset();
}
return $phpcsFile->numTokens + 1;
}
// Template file: no blank line after the file comment.
if ($tokens[$next]['line'] !== $tokens[$commentEnd]['line'] + 1 && $tokens[$next]['line'] > $tokens[$commentEnd]['line'] && $tokens[$next]['code'] === T_CLOSE_TAG) {
$error = 'There must be no blank line after the file comment in a template';
$fix = $phpcsFile
->addFixableError($error, $commentEnd, 'TeamplateSpacingAfterComment');
if ($fix === true) {
$phpcsFile->fixer
->beginChangeset();
$uselessLine = $commentEnd + 1;
while ($uselessLine < $next) {
$phpcsFile->fixer
->replaceToken($uselessLine, '');
$uselessLine++;
}
$phpcsFile->fixer
->addContent($commentEnd, "\n");
$phpcsFile->fixer
->endChangeset();
}
}
// Ignore the rest of the file.
return $phpcsFile->numTokens + 1;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
FileCommentSniff:: |
public | property | A list of tokenizers this sniff supports. | |
FileCommentSniff:: |
public | function | Processes this test, when one of its tokens is encountered. | |
FileCommentSniff:: |
public | function | Returns an array of tokens this test wants to listen for. |