Exemplo n.º 1
0
 /**
  * Processes this test, when one of its tokens is encountered.
  *
  * @param PHP_CodeSniffer_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(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');
         }
     }
 }
 /**
  * Processes this test, when one of its tokens is encountered.
  *
  * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
 {
     $find = array(T_COMMENT, T_DOC_COMMENT, T_CLASS, T_FUNCTION, T_OPEN_TAG);
     $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1);
     if ($commentEnd === false) {
         return;
     }
     $this->currentFile = $phpcsFile;
     $tokens = $phpcsFile->getTokens();
     // 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) {
         $error = 'You must use "/**" style comments for a function comment';
         $phpcsFile->addError($error, $stackPtr, 'WrongStyle');
         return;
     } else {
         if ($code !== T_DOC_COMMENT) {
             $phpcsFile->addError('Missing function doc comment', $stackPtr, 'Missing');
             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)) {
         $formattingIssue = 0;
         if (!strstr($matches[0], 'Implements ')) {
             $formattingIssue++;
         }
         if (!preg_match('/ hook_[a-zA-Z0-9_]+\\(\\)( for [a-z0-9_]+\\(\\))?\\.$/', $matches[0])) {
             $formattingIssue++;
         }
         if ($formattingIssue) {
             $phpcsFile->addWarning('Format should be "* Implements hook_foo()." or "Implements hook_foo_BAR_ID_bar() for xyz_bar()."', $commentStart + 1);
         }
     }
     // No extra newline before short description.
     $short = $comment->getShortComment();
     $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);
     }
     $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;
     }
 }