public function Drupal_Sniffs_Commenting_FunctionCommentSniff::process in Coder 7.2
Processes this test, when one of its tokens is encountered.
Parameters
PHP_CodeSniffer_File $phpcsFile The file being scanned.:
int $stackPtr The position of the current token: in the stack passed in $tokens.
Return value
void
File
- coder_sniffer/
Drupal/ Sniffs/ Commenting/ FunctionCommentSniff.php, line 96
Class
- Drupal_Sniffs_Commenting_FunctionCommentSniff
- Parses and verifies the doc comments for functions. Largely copied from Squiz_Sniffs_Commenting_FunctionCommentSniff.
Code
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
$this->currentFile = $phpcsFile;
$tokens = $phpcsFile
->getTokens();
$find = array(
T_COMMENT,
T_DOC_COMMENT,
T_CLASS,
T_FUNCTION,
T_OPEN_TAG,
);
$commentEnd = $phpcsFile
->findPrevious($find, $stackPtr - 1);
if ($commentEnd === false) {
return;
}
// If the token that we found was a class or a function, then this
// function has no doc comment.
$code = $tokens[$commentEnd]['code'];
if ($code === T_COMMENT) {
// The function might actually be missing a comment, and this last comment
// found is just commenting a bit of code on a line. So if it is not the
// only thing on the line, assume we found nothing.
$prevContent = $phpcsFile
->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $commentEnd);
if ($tokens[$commentEnd]['line'] === $tokens[$commentEnd]['line']) {
$error = 'Missing function doc comment';
$phpcsFile
->addError($error, $stackPtr, 'Missing');
}
else {
$error = 'You must use "/**" style comments for a function comment';
$phpcsFile
->addError($error, $stackPtr, 'WrongStyle');
}
return;
}
else {
if ($code !== T_DOC_COMMENT) {
$error = 'Missing function doc comment';
$phpcsFile
->addError($error, $stackPtr, 'Missing');
return;
}
else {
if (trim($tokens[$commentEnd]['content']) !== '*/') {
$error = 'Wrong function doc comment end; expected "*/", found "%s"';
$phpcsFile
->addError($error, $commentEnd, 'WrongEnd', array(
trim($tokens[$commentEnd]['content']),
));
return;
}
}
}
// If there is any code between the function keyword and the doc block
// then the doc block is not for us.
$ignore = PHP_CodeSniffer_Tokens::$scopeModifiers;
$ignore[] = T_STATIC;
$ignore[] = T_WHITESPACE;
$ignore[] = T_ABSTRACT;
$ignore[] = T_FINAL;
$prevToken = $phpcsFile
->findPrevious($ignore, $stackPtr - 1, null, true);
if ($prevToken !== $commentEnd) {
$phpcsFile
->addError('Missing function doc comment', $stackPtr, 'Missing');
return;
}
$this->_functionToken = $stackPtr;
$this->_classToken = null;
foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condition) {
if ($condition === T_CLASS || $condition === T_INTERFACE) {
$this->_classToken = $condPtr;
break;
}
}
// If the first T_OPEN_TAG is right before the comment and does not immediately precede the function
// probably a file comment.
$commentStart = $phpcsFile
->findPrevious(T_DOC_COMMENT, $commentEnd - 1, null, true) + 1;
$prevToken = $phpcsFile
->findPrevious(T_WHITESPACE, $commentStart - 1, null, true);
if ($tokens[$prevToken]['code'] === T_OPEN_TAG) {
// Is this the first open tag
if (($stackPtr === 0 || $phpcsFile
->findPrevious(T_OPEN_TAG, $prevToken - 1) === false) && $tokens[$commentEnd]['line'] + 1 !== $tokens[$stackPtr]['line']) {
$phpcsFile
->addError('Missing function doc comment', $stackPtr, 'Missing');
return;
}
}
$commentString = $phpcsFile
->getTokensAsString($commentStart, $commentEnd - $commentStart + 1);
$this->_methodName = $phpcsFile
->getDeclarationName($stackPtr);
try {
$this->commentParser = new Drupal_CommentParser_FunctionCommentParser($commentString, $phpcsFile);
$this->commentParser
->parse();
} catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
$line = $e
->getLineWithinComment() + $commentStart;
$phpcsFile
->addError($e
->getMessage(), $line, 'FailedParse');
return;
}
$comment = $this->commentParser
->getComment();
if (is_null($comment) === true) {
$error = 'Function doc comment is empty';
$phpcsFile
->addError($error, $commentStart, 'Empty');
return;
}
// The first line of the comment should just be the /** code.
$eolPos = strpos($commentString, $phpcsFile->eolChar);
$firstLine = substr($commentString, 0, $eolPos);
if ($firstLine !== '/**') {
$error = 'The open comment tag must be the only content on the line';
$phpcsFile
->addError($error, $commentStart, 'ContentAfterOpen');
}
// Check that the comment is imidiately before the function definition.
if ($tokens[$commentEnd]['line'] + 1 !== $tokens[$stackPtr]['line']) {
$error = 'Function doc comment must end on the line before the function definition';
$phpcsFile
->addError($error, $commentEnd, 'EmptyLinesAfterDoc');
}
$this
->processParams($commentStart);
$this
->processReturn($commentStart, $commentEnd);
$this
->processThrows($commentStart);
$this
->processSees($commentStart);
// Check if hook implementation doc is formated correctly.
if (preg_match('/^[\\s]*Implement[^\\n]+?hook_[^\\n]+/i', $comment
->getShortComment(), $matches)) {
if (!strstr($matches[0], 'Implements ') || strstr($matches[0], 'Implements of') || !preg_match('/ (drush_)?hook_[a-zA-Z0-9_]+\\(\\)( for [a-z0-9_-]+(\\(\\)|\\.tpl\\.php|\\.html.twig))?\\.$/', $matches[0])) {
$phpcsFile
->addWarning('Format should be "* Implements hook_foo().", "* Implements hook_foo_BAR_ID_bar() for xyz_bar().",, "* Implements hook_foo_BAR_ID_bar() for xyz-bar.html.twig.", or "* Implements hook_foo_BAR_ID_bar() for xyz-bar.tpl.php.".', $commentStart + 1);
}
else {
// Check that a hook implementation does not duplicate @param and
// @return documentation.
$params = $this->commentParser
->getParams();
if (empty($params) === false) {
$param = array_shift($params);
$errorPos = $param
->getLine() + $commentStart;
$warn = 'Hook implementations should not duplicate @param documentation';
$phpcsFile
->addWarning($warn, $errorPos, 'HookParamDoc');
}
$return = $this->commentParser
->getReturn();
if ($return !== null) {
$errorPos = $commentStart + $this->commentParser
->getReturn()
->getLine();
$warn = 'Hook implementations should not duplicate @return documentation';
$phpcsFile
->addWarning($warn, $errorPos, 'HookReturnDoc');
}
}
//end if
}
//end if
// Check for a comment description.
$short = $comment
->getShortComment();
if (trim($short) === '') {
$error = 'Missing short description in function doc comment';
$phpcsFile
->addError($error, $commentStart, 'MissingShort');
return;
}
// No extra newline before short description.
$newlineCount = 0;
$newlineSpan = strspn($short, $phpcsFile->eolChar);
if ($short !== '' && $newlineSpan > 0) {
$line = $newlineSpan > 1 ? 'newlines' : 'newline';
$error = "Extra {$line} found before function comment short description";
$phpcsFile
->addError($error, $commentStart + 1);
return;
}
$newlineCount = substr_count($short, $phpcsFile->eolChar) + 1;
// Exactly one blank line between short and long description.
$long = $comment
->getLongComment();
if (empty($long) === false) {
$between = $comment
->getWhiteSpaceBetween();
$newlineBetween = substr_count($between, $phpcsFile->eolChar);
if ($newlineBetween !== 2) {
$error = 'There must be exactly one blank line between descriptions in function comment';
$phpcsFile
->addError($error, $commentStart + $newlineCount + 1, 'SpacingAfterShort');
}
$newlineCount += $newlineBetween;
}
// Short description must be single line and end with a full stop.
$testShort = trim($short);
$lastChar = $testShort[strlen($testShort) - 1];
if (substr_count($testShort, $phpcsFile->eolChar) !== 0) {
$error = 'Function comment short description must be on a single line, further text should be a separate paragraph';
$phpcsFile
->addError($error, $commentStart + 1, 'ShortSingleLine');
}
if (strpos($short, $testShort) !== 1) {
$error = 'Function comment short description must start with exactly one space';
$phpcsFile
->addError($error, $commentStart + 1, 'ShortStartSpace');
}
if ($testShort !== '{@inheritdoc}') {
if (preg_match('|[A-Z]|', $testShort[0]) === 0) {
$error = 'Function comment short description must start with a capital letter';
$phpcsFile
->addError($error, $commentStart + 1, 'ShortNotCapital');
}
if ($lastChar !== '.') {
$error = 'Function comment short description must end with a full stop';
$phpcsFile
->addError($error, $commentStart + 1, 'ShortFullStop');
}
}
}