Chris@0: getTokens(); Chris@17: $find = Tokens::$methodPrefixes; Chris@0: $find[] = T_WHITESPACE; Chris@0: Chris@0: $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true); Chris@0: if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG Chris@0: && $tokens[$commentEnd]['code'] !== T_COMMENT Chris@0: ) { Chris@0: return; Chris@0: } Chris@0: Chris@0: if ($tokens[$commentEnd]['code'] === T_COMMENT) { Chris@0: return; Chris@0: } Chris@0: Chris@0: $commentStart = $tokens[$commentEnd]['comment_opener']; Chris@0: Chris@0: $empty = array( Chris@0: T_DOC_COMMENT_WHITESPACE, Chris@0: T_DOC_COMMENT_STAR, Chris@0: ); Chris@0: Chris@0: $short = $phpcsFile->findNext($empty, ($commentStart + 1), $commentEnd, true); Chris@0: if ($short === false) { Chris@0: // No content at all. Chris@0: return; Chris@0: } Chris@0: Chris@0: // Account for the fact that a short description might cover Chris@0: // multiple lines. Chris@0: $shortContent = $tokens[$short]['content']; Chris@0: $shortEnd = $short; Chris@0: for ($i = ($short + 1); $i < $commentEnd; $i++) { Chris@0: if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) { Chris@0: if ($tokens[$i]['line'] === ($tokens[$shortEnd]['line'] + 1)) { Chris@0: $shortContent .= $tokens[$i]['content']; Chris@0: $shortEnd = $i; Chris@0: } else { Chris@0: break; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // Check if a hook implementation doc block is formatted correctly. Chris@0: if (preg_match('/^[\s]*Implement[^\n]+?hook_[^\n]+/i', $shortContent, $matches) === 1) { Chris@0: if (strstr($matches[0], 'Implements ') === false || strstr($matches[0], 'Implements of') !== false Chris@0: || preg_match('/ (drush_)?hook_[a-zA-Z0-9_]+\(\)( for .+)?\.$/', $matches[0]) !== 1 Chris@0: ) { Chris@17: $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.", "* Implements hook_foo_BAR_ID_bar() for xyz-bar.tpl.php.", or "* Implements hook_foo_BAR_ID_bar() for block templates."', $short, 'HookCommentFormat'); Chris@0: } else { Chris@0: // Check that a hook implementation does not duplicate @param and Chris@0: // @return documentation. Chris@0: foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) { Chris@0: if ($tokens[$tag]['content'] === '@param') { Chris@0: $warn = 'Hook implementations should not duplicate @param documentation'; Chris@0: $phpcsFile->addWarning($warn, $tag, 'HookParamDoc'); Chris@0: } Chris@0: Chris@0: if ($tokens[$tag]['content'] === '@return') { Chris@0: $warn = 'Hook implementations should not duplicate @return documentation'; Chris@0: $phpcsFile->addWarning($warn, $tag, 'HookReturnDoc'); Chris@0: } Chris@0: } Chris@0: }//end if Chris@18: Chris@18: return; Chris@0: }//end if Chris@0: Chris@18: // Check if the doc block just repeats the function name with Chris@18: // "Implements example_hook_name()". Chris@18: $functionName = $phpcsFile->getDeclarationName($stackPtr); Chris@18: if ($functionName !== null && preg_match("/^[\s]*Implements $functionName\(\)\.$/i", $shortContent) === 1) { Chris@18: $error = 'Hook implementations must be documented with "Implements hook_example()."'; Chris@18: $fix = $phpcsFile->addFixableError($error, $short, 'HookRepeat'); Chris@18: if ($fix === true) { Chris@18: $newComment = preg_replace('/Implements [^_]+/', 'Implements hook', $shortContent); Chris@18: $phpcsFile->fixer->replaceToken($short, $newComment); Chris@18: } Chris@18: } Chris@18: Chris@0: }//end process() Chris@0: Chris@0: Chris@0: }//end class