/**
  * Process any throw tags that this function comment has.
  *
  * @param int $commentStart The position in the stack where the comment started.
  *
  * @return void
  */
 protected function processThrows($commentStart)
 {
     if (count($this->commentParser->getThrows()) === 0) {
         return;
     }
     $tagOrder = $this->commentParser->getTagOrders();
     $index = array_keys($this->commentParser->getTagOrders(), 'throws');
     foreach ($this->commentParser->getThrows() as $i => $throw) {
         $exception = $throw->getValue();
         $content = trim($throw->getComment());
         $errorPos = $commentStart + $throw->getLine();
         if (empty($exception) === true) {
             $error = 'Exception type and comment missing for @throws tag in function comment';
             $this->currentFile->addError($error, $errorPos, 'InvalidThrows');
         } else {
             if (empty($content) === true) {
                 $error = 'Comment missing for @throws tag in function comment';
                 $this->currentFile->addError($error, $errorPos, 'EmptyThrows');
             } else {
                 // Starts with a capital letter and ends with a fullstop.
                 $firstChar = $content[0];
                 if (strtoupper($firstChar) !== $firstChar) {
                     $error = '@throws tag comment must start with a capital letter';
                     $this->currentFile->addError($error, $errorPos, 'ThrowsNotCapital');
                 }
                 $lastChar = $content[strlen($content) - 1];
                 if ($lastChar !== '.') {
                     $error = '@throws tag comment must end with a full stop';
                     $this->currentFile->addError($error, $errorPos, 'ThrowsNoFullStop');
                 }
             }
         }
         $since = array_keys($tagOrder, 'since');
         if (count($since) === 1 && $this->_tagIndex !== 0) {
             $this->_tagIndex++;
             if ($index[$i] !== $this->_tagIndex) {
                 $error = 'The @throws tag is in the wrong order; the tag follows @return';
                 $this->currentFile->addError($error, $errorPos, 'ThrowsOrder');
             }
         }
     }
     //end foreach
 }
 /**
  * Process any throw tags that this function comment has
  *
  * @param  integer $commentStart The position in the stack where the comment started
  * @return void
  */
 protected function _processThrows($commentStart)
 {
     if (count($this->_commentParser->getThrows()) === 0) {
         return;
     }
     $tagOrder = $this->_commentParser->getTagOrders();
     $index = array_keys($this->_commentParser->getTagOrders(), 'throws');
     foreach ($this->_commentParser->getThrows() as $i => $throw) {
         $exception = $throw->getValue();
         $content = trim($throw->getComment());
         $errorPos = $commentStart + $throw->getLine();
         if (empty($exception) === true) {
             $error = 'Exception type and comment missing for @throws tag in function comment';
             $this->_currentFile->addError($error, $errorPos, 'ThrowTagMustContainExceptionTypeCommentFunctionComment');
         } else {
             if (empty($content) === true) {
                 $error = 'Comment missing for @throws tag in function comment';
                 $this->_currentFile->addError($error, $errorPos, 'ThrowTagNotEmptyFunctionComment');
             } else {
                 // Starts with a capital letter and ends with a fullstop
                 $firstChar = $content[0];
                 if (strtoupper($firstChar) !== $firstChar) {
                     $error = '@throws tag comment must start with a capital letter';
                     $this->_currentFile->addError($error, $errorPos, 'ThrowTagFunctionCommentStartsWithCapitalLetter');
                 }
             }
         }
         $since = array_keys($tagOrder, 'since');
         if (count($since) === 1 and $this->_tagIndex !== 0) {
             $this->_tagIndex++;
             if ($index[$i] !== $this->_tagIndex) {
                 $error = 'The @throws tag is in the wrong order; the tag follows @return';
                 $this->_currentFile->addError($error, $errorPos, 'TagsOrderFunctionComment');
             }
         }
     }
 }
 /**
  * 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, it is 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) {
             $phpcsFile->addError('Missing function doc comment', $stackPtr, 'Missing');
             return;
         }
     }
     $comment = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1);
     $this->_methodName = $phpcsFile->getDeclarationName($stackPtr);
     try {
         $this->commentParser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($comment, $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;
     }
     $this->processParams($commentStart);
     $this->processReturn($commentStart, $commentEnd);
     $this->processThrows($commentStart);
     // No extra newline before short description.
     $short = $comment->getShortComment();
     $newlineCount = 0;
     $newlineSpan = strspn($short, $phpcsFile->eolChar);
     if ($short !== '' && $newlineSpan > 0) {
         $error = 'Extra newline(s) found before function comment short description';
         $phpcsFile->addError($error, $commentStart + 1, 'SpacingBeforeShort');
     }
     $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;
     }
     // Exactly one blank line before tags.
     $params = $this->commentParser->getTagOrders();
     if (count($params) > 1) {
         $newlineSpan = $comment->getNewlineAfter();
         if ($newlineSpan !== 2) {
             $error = 'There must be exactly one blank line before the tags in function comment';
             if ($long !== '') {
                 $newlineCount += substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1;
             }
             $phpcsFile->addError($error, $commentStart + $newlineCount, 'SpacingBeforeTags');
             $short = rtrim($short, $phpcsFile->eolChar . ' ');
         }
     }
 }
示例#4
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)
 {
     $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 (isset($tokens[$stackPtr]['nested_parenthesis'])) {
         return;
     }
     if (!isset($tokens[$stackPtr]['conditions'])) {
         return;
     }
     end($tokens[$stackPtr]['conditions']);
     $ckey = key($tokens[$stackPtr]['conditions']);
     if (!isset($tokens[$ckey]) || $tokens[$ckey]['code'] !== T_CLASS && $tokens[$ckey]['code'] !== T_INTERFACE) {
         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) {
         $error = 'You must use "/**" style comments for a variable comment';
         $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.6.4') . $error, $stackPtr);
         return;
     } else {
         if ($code !== T_DOC_COMMENT) {
             $error = 'Missing variable doc comment';
             $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.6.1') . $error, $stackPtr);
             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($this->getReqPrefix('REQ.PHP.4.6.1') . 'Missing function doc comment', $stackPtr);
         return;
     }
     // If the first T_OPEN_TAG is right before the comment, it is 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) {
             $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.6.1') . 'Missing function doc comment', $stackPtr);
             return;
         }
     }
     $comment = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1);
     try {
         $this->commentParser = new PHP_CodeSniffer_CommentParser_MemberCommentParser($comment, $phpcsFile);
         $this->commentParser->parse();
     } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
         $line = $e->getLineWithinComment() + $commentStart;
         $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.1.1') . $e->getMessage(), $line);
         return;
     }
     $comment = $this->commentParser->getComment();
     if (is_null($comment) === true) {
         $error = 'Variable doc comment is empty';
         $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.6.2') . $error, $commentStart);
         return;
     }
     $this->processTags($commentStart, $commentEnd);
     // 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 variable comment short description";
         $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.1.7') . $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 variable comment';
             $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.1.19') . $error, $commentStart + $newlineCount + 1);
         }
         $newlineCount += $newlineBetween;
     }
     // Exactly one blank line before tags.
     $params = $this->commentParser->getTagOrders();
     if (count($params) > 1) {
         $newlineSpan = $comment->getNewlineAfter();
         if ($newlineSpan !== 2) {
             $error = 'There must be exactly one blank line before the tags in function comment';
             if ($long !== '') {
                 $newlineCount += substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1;
             }
             $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.1.18') . $error, $commentStart + $newlineCount);
             $short = rtrim($short, $phpcsFile->eolChar . ' ');
         }
     }
 }
 /**
  * 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();
     $commentEnd = $this->findDocComment($stackPtr, $phpcsFile);
     if ($commentEnd === FALSE) {
         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;
     $ignore[] = T_COMMENT;
     $prevToken = $phpcsFile->findPrevious($ignore, $stackPtr - 1, null, true);
     if ($prevToken !== $commentEnd) {
         //$phpcsFile->addError('Missing function doc comment', $stackPtr);
         $phpcsFile->addEvent('XP_FUNCTION_COMMENT_MISSING', array(), $stackPtr);
         return;
     }
     // Check for empty lines between method comment and declaration
     $newlineToken = $phpcsFile->findNext(T_WHITESPACE, $commentEnd + 1, $stackPtr, false, $phpcsFile->eolChar);
     if ($newlineToken !== false) {
         $newlineToken = $phpcsFile->findNext(T_WHITESPACE, $newlineToken + 1, $stackPtr, false, $phpcsFile->eolChar);
         if ($newlineToken !== false) {
             // Blank line between the class and the doc block.
             // The doc block is most likely a file comment.
             //$error = "Missing $type doc comment";
             //$phpcsFile->addError($error, ($stackPtr + 1));
             $phpcsFile->addEvent('XP_FUNCTION_COMMENT_MISSING', array(), $stackPtr + 1);
             return;
         }
     }
     //end if
     $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, it is 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) {
             //$phpcsFile->addError('Missing function doc comment', $stackPtr);
             $phpcsFile->addEvent('XP_FUNCTION_COMMENT_MISSING', array(), $stackPtr);
             return;
         }
     }
     $comment = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1);
     $this->_methodName = $phpcsFile->getDeclarationName($stackPtr);
     try {
         $this->commentParser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($comment, $phpcsFile);
         $this->commentParser->parse();
     } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
         $line = $e->getLineWithinComment() + $commentStart;
         $phpcsFile->addError($e->getMessage(), $line);
         return;
     }
     $comment = $this->commentParser->getComment();
     if (is_null($comment) === true) {
         //$error = 'Function doc comment is empty';
         //$phpcsFile->addError($error, $commentStart);
         $phpcsFile->addEvent('XP_FUNCTION_COMMENT_EMPTY', array(), $commentStart);
         return;
     }
     $this->processParams($commentStart);
     $this->processThrows($commentStart);
     $functionToken = $tokens[$this->_functionToken];
     if ($phpcsFile->findNext(T_RETURN, $functionToken['scope_opener'], $functionToken['scope_closer'])) {
         $this->processReturn($commentStart, $commentEnd);
     }
     // 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));
         $phpcsFile->addEvent('XP_FUNCTION_COMMENT_NEWLINE_BEFORE_SHORT_DESCRIPTION', array(), $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));
             $phpcsFile->addEvent('XP_FUNCTION_COMMENT_EXTRA_NEWLINE_BETWEEN_DESCRIPTION', array(), $commentStart + $newlineCount + 1);
         }
         $newlineCount += $newlineBetween;
     }
     // Exactly one blank line before tags.
     $params = $this->commentParser->getTagOrders();
     if (count($params) > 1) {
         $newlineSpan = $comment->getNewlineAfter();
         if ($newlineSpan !== 2) {
             $error = 'There must be exactly one blank line before the tags in function comment';
             if ($long !== '') {
                 $newlineCount += substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1;
             }
             //$phpcsFile->addError($error, ($commentStart + $newlineCount));
             $phpcsFile->addEvent('XP_FUNCTION_COMMENT_EXTRA_NEWLINE_BEFORE_TAGS', array(), $commentStart + $newlineCount);
             $short = rtrim($short, $phpcsFile->eolChar . ' ');
         }
     }
 }
 /**
  * Process the return comment of this function comment
  *
  * @param  integer $commentStart The position in the stack where the comment started
  * @param  integer $commentEnd   The position in the stack where the comment ended
  * @return void
  */
 protected function _processReturn($commentStart, $commentEnd)
 {
     // Skip constructor and destructor
     $className = '';
     if ($this->_classToken !== null) {
         $className = $this->_currentFile->getDeclarationName($this->_classToken);
         $className = strtolower(ltrim($className, '_'));
     }
     $methodName = strtolower(ltrim($this->_methodName, '_'));
     $isSpecialMethod = ($this->_methodName === '__construct' or $this->_methodName === '__destruct');
     $return = $this->_commentParser->getReturn();
     if ($methodName === $className or $isSpecialMethod !== false) {
         // No return tag for constructor and destructor
         if ($return !== null) {
             $errorPos = $commentStart + $return->getLine();
             $this->_currentFile->addEvent('TAG_RETURN_NOT_REQUIRED_FUNCTION_COMMENT', array(), $errorPos);
         }
         return;
     }
     if ($return === null) {
         $error = 'Missing @return tag in function comment';
         //            $this->_currentFile->addError($error, $commentEnd);
         $this->_currentFile->addEvent('TAG_RETURN_MISSING_FUNCTION_COMMENT', array(), $commentEnd);
         return;
     }
     $tagOrder = $this->_commentParser->getTagOrders();
     $index = array_keys($tagOrder, 'return');
     $errorPos = $commentStart + $return->getLine();
     $content = trim($return->getRawContent());
     if (count($index) > 1) {
         $this->_currentFile->addEvent('ONE_VERSION_TAG_FUNCTION_COMMENT', array(), $errorPos);
         return;
     }
     //        $since = array_keys($tagOrder, 'since');
     //        if (count($since) === 1 and $this->_tagIndex !== 0) {
     //            $this->_tagIndex++;
     //            if ($index[0] !== $this->_tagIndex) {
     //                $error = 'The @return tag is in the wrong order; the tag follows @see (if used) or @since';
     //                $this->_currentFile->addError($error, $errorPos);
     //            }
     //        }
     if (empty($content) === true) {
         $this->_currentFile->addEvent('TYPE_MISSING_TAG_RETURN_FUNCTION_COMMENT', array(), $errorPos);
     } else {
         // Check return type (can be multiple, separated by '|')
         $typeNames = explode('|', $content);
         $suggestedNames = array();
         foreach ($typeNames as $i => $typeName) {
             $suggestedName = PHP_CodeSniffer::suggestType($typeName);
             if (in_array($suggestedName, $suggestedNames) === false) {
                 $suggestedNames[] = $suggestedName;
             }
         }
         $suggestedType = implode('|', $suggestedNames);
         if ($content !== $suggestedType) {
             $this->_currentFile->addEvent('TYPE_RETURN_INVALID_FUNCTION_COMMENT', array('content' => $content), $errorPos);
         }
         $tokens = $this->_currentFile->getTokens();
         // If the return type is void, make sure there is
         // no return statement in the function
         if ($content === 'void') {
             if (isset($tokens[$this->_functionToken]['scope_closer']) === true) {
                 $endToken = $tokens[$this->_functionToken]['scope_closer'];
                 $return = $this->_currentFile->findNext(T_RETURN, $this->_functionToken, $endToken);
                 if ($return !== false) {
                     // If the function is not returning anything, just
                     // exiting, then there is no problem
                     $semicolon = $this->_currentFile->findNext(T_WHITESPACE, $return + 1, null, true);
                     if ($tokens[$semicolon]['code'] !== T_SEMICOLON) {
                         $this->_currentFile->addEvent('TYPE_RETURN_VOID_FUNCTION_COMMENT', array(), $errorPos);
                     }
                 }
             }
         } else {
             // If return type is not void, there needs to be a
             // returns statement somewhere in the function that
             // returns something
             if (isset($tokens[$this->_functionToken]['scope_closer']) === true) {
                 $endToken = $tokens[$this->_functionToken]['scope_closer'];
                 $return = $this->_currentFile->findNext(T_RETURN, $this->_functionToken, $endToken);
                 if ($return === false) {
                     $this->_currentFile->addEvent('TYPE_RETURN_NOT_VOID_NO_STATEMENT_FUNCTION_COMMENT', array(), $errorPos);
                 } else {
                     $semicolon = $this->_currentFile->findNext(T_WHITESPACE, $return + 1, null, true);
                     if ($tokens[$semicolon]['code'] === T_SEMICOLON) {
                         $this->_currentFile->addEvent('TYPE_RETURN_NOT_VOID_FUNCTION_COMMENT', array(), $errorPos);
                     }
                 }
             }
         }
     }
 }
 /**
  * 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) {
         $error = 'You must use "/**" style comments for a function comment';
         $phpcsFile->addError($error, $stackPtr);
         return;
     } else {
         if ($code !== T_DOC_COMMENT) {
             $error = 'Missing function doc comment';
             $phpcsFile->addError($error, $stackPtr);
             return;
         }
     }
     $this->_functionToken = $stackPtr;
     $classToken = $phpcsFile->findPrevious(array(T_CLASS, T_INTERFACE), $stackPtr - 1);
     if ($classToken !== false) {
         $this->_classToken = $classToken;
     }
     // Find the first doc comment.
     $commentStart = $phpcsFile->findPrevious(T_DOC_COMMENT, $commentEnd - 1, null, true) + 1;
     $comment = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1);
     $this->_methodName = $phpcsFile->getDeclarationName($stackPtr);
     try {
         $this->commentParser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($comment);
         $this->commentParser->parse();
     } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
         $line = $e->getLineWithinComment() + $commentStart;
         $phpcsFile->addError($e->getMessage(), $line);
         return;
     }
     $comment = $this->commentParser->getComment();
     if (is_null($comment) === true) {
         $error = 'Function doc comment is empty';
         $phpcsFile->addError($error, $commentStart);
         return;
     }
     $this->_processParams($commentStart);
     $this->_processReturn($commentStart, $commentEnd);
     $this->_processThrows($commentStart);
     // No extra newline before short description.
     $short = $comment->getShortComment();
     $newlineCount = 0;
     $newlineSpan = strspn($short, "\n");
     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, "\n") + 1;
     // Exactly one blank line between short and long description.
     $long = $comment->getLongComment();
     if (empty($long) === false) {
         $between = $comment->getWhiteSpaceBetween();
         $newlineBetween = substr_count($between, "\n");
         if ($newlineBetween !== 2) {
             $error = 'There must be exactly one blank line between descriptions in function comment';
             $phpcsFile->addError($error, $commentStart + $newlineCount + 1);
         }
         $newlineCount += $newlineBetween;
     }
     // Exactly one blank line before tags.
     $params = $this->commentParser->getTagOrders();
     if (count($params) > 1) {
         $newlineSpan = $comment->getNewlineAfter();
         if ($newlineSpan !== 2) {
             $error = 'There must be exactly one blank line before the tags in function comment';
             if ($long !== '') {
                 $newlineCount += substr_count($long, "\n") - $newlineSpan + 1;
             }
             $phpcsFile->addError($error, $commentStart + $newlineCount);
             $short = rtrim($short, "\n ");
         }
     }
     // Check for unknown/deprecated tags.
     $unknownTags = $this->commentParser->getUnknown();
     foreach ($unknownTags as $errorTag) {
         $error = "@{$errorTag['tag']} tag is not allowed in function comment";
         $phpcsFile->addWarning($error, $commentStart + $errorTag['line']);
     }
 }
 /**
  * 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);
         return;
     } else {
         if ($code !== T_DOC_COMMENT) {
             $phpcsFile->addError('Missing function doc comment', $stackPtr);
             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);
         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, it is 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) {
             $phpcsFile->addError('Missing function doc comment', $stackPtr);
             return;
         }
     }
     $comment = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1);
     $this->_methodName = $phpcsFile->getDeclarationName($stackPtr);
     try {
         $this->commentParser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($comment, $phpcsFile);
         $this->commentParser->parse();
     } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
         $line = $e->getLineWithinComment() + $commentStart;
         $phpcsFile->addError($e->getMessage(), $line);
         return;
     }
     $comment = $this->commentParser->getComment();
     if (is_null($comment) === true) {
         $error = 'Function doc comment is empty';
         $phpcsFile->addError($error, $commentStart);
         return;
     }
     $this->processParams($commentStart);
     $this->processReturn($commentStart, $commentEnd);
     $this->processThrows($commentStart);
     // Check if hook_ implementation is formated correctly
     if (preg_match('/(Implement[^\\n]+?hook_[^\\n]+)/', $comment->getShortComment(), $matches)) {
         $formattingIssue = 0;
         if (!strstr($matches[0], 'Implements ')) {
             $formattingIssue++;
         }
         if (!preg_match('/ hook_[\\S\\(\\)]+\\.$/', $matches[0])) {
             $formattingIssue++;
         }
         if ($formattingIssue) {
             $phpcsFile->addWarning('Format should be * Implements hook_foo().', $commentStart + 1);
         }
     }
     //getLongComment
     // 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);
         }
         $newlineCount += $newlineBetween;
     }
     // Exactly one blank line before tags.
     $params = $this->commentParser->getTagOrders();
     if (count($params) > 1) {
         //@TODO implement Drupal/Doxygen specific sniffs
         /*$newlineSpan = $comment->getNewlineAfter();
                     if ($newlineSpan !== 2) {
                         $error = 'There must be exactly one blank line before the tags in function comment';
                         if ($long !== '') {
                             $newlineCount += (substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1);
                         }
         
                         $phpcsFile->addError($error, ($commentStart + $newlineCount));
                         $short = rtrim($short, $phpcsFile->eolChar.' ');
                     }*/
     }
 }
 /**
  * Process the return comment of this function comment
  *
  * @param  integer $commentStart The position in the stack where the comment started
  * @param  integer $commentEnd   The position in the stack where the comment ended
  * @return void
  */
 protected function _processReturn($commentStart, $commentEnd)
 {
     // Skip constructor and destructor
     $className = '';
     if ($this->_classToken !== null) {
         $className = $this->_currentFile->getDeclarationName($this->_classToken);
         $className = strtolower(ltrim($className, '_'));
     }
     $methodName = strtolower(ltrim($this->_methodName, '_'));
     $isSpecialMethod = ($this->_methodName === '__construct' or $this->_methodName === '__destruct');
     $return = $this->_commentParser->getReturn();
     if ($methodName === $className or $isSpecialMethod !== false) {
         // No return tag for constructor and destructor
         if ($return !== null) {
             $errorPos = $commentStart + $return->getLine();
             $this->_currentFile->addError("Le tag @return n'est pas requis dans le constructeur et le destructeur", $errorPos, 'TagReturnNotRequiredFunctionComment');
         }
         return;
     }
     if ($return === null) {
         $this->_currentFile->addError("Le tag @return est manquant dans le commentaire de fonction", $commentEnd, 'TagReturnMissingFunctionComment');
         return;
     }
     $tagOrder = $this->_commentParser->getTagOrders();
     $index = array_keys($tagOrder, 'return');
     $errorPos = $commentStart + $return->getLine();
     $content = trim($return->getRawContent());
     if (count($index) > 1) {
         $this->_currentFile->addError("Une seule version du tag @return est admise dans un commentaire de fonction", $errorPos, 'OneVersionTagFunctionComment');
         return;
     }
     //        $since = array_keys($tagOrder, 'since');
     //        if (count($since) === 1 and $this->_tagIndex !== 0) {
     //            $this->_tagIndex++;
     //            if ($index[0] !== $this->_tagIndex) {
     //                $error = 'The @return tag is in the wrong order; the tag follows @see (if used) or @since';
     //                $this->_currentFile->addError($error, $errorPos);
     //            }
     //        }
     if (empty($content) === true) {
         $this->_currentFile->addError("Type manquant dans le tag @return dans le commentaire de fonction", $errorPos, 'TypeMissingTagReturnFunctionComment');
     } else {
         // Check return type (can be multiple, separated by '|')
         $typeNames = explode('|', $content);
         $suggestedNames = array();
         foreach ($typeNames as $i => $typeName) {
             $suggestedName = PHP_CodeSniffer::suggestType($typeName);
             if (in_array($suggestedName, $suggestedNames) === false) {
                 $suggestedNames[] = $suggestedName;
             }
         }
         $suggestedType = implode('|', $suggestedNames);
         if ($content !== $suggestedType) {
             $this->_currentFile->addError("Function return type {$content} is invalid", $errorPos, 'TypeReturnInvalidFunctionComment');
         }
         $tokens = $this->_currentFile->getTokens();
         // If the return type is void, make sure there is
         // no return statement in the function
         if ($content === 'void') {
             if (isset($tokens[$this->_functionToken]['scope_closer']) === true) {
                 $endToken = $tokens[$this->_functionToken]['scope_closer'];
                 $return = $this->_currentFile->findNext(T_RETURN, $this->_functionToken, $endToken);
                 if ($return !== false) {
                     // If the function is not returning anything, just
                     // exiting, then there is no problem
                     $semicolon = $this->_currentFile->findNext(T_WHITESPACE, $return + 1, null, true);
                     if ($tokens[$semicolon]['code'] !== T_SEMICOLON) {
                         $this->_currentFile->addError("Function return type is void, but function contains return statement", $errorPos, 'TypeReturnVoidFunctionComment');
                     }
                 }
             }
         } else {
             // If return type is not void, there needs to be a
             // returns statement somewhere in the function that
             // returns something
             if (isset($tokens[$this->_functionToken]['scope_closer']) === true) {
                 $endToken = $tokens[$this->_functionToken]['scope_closer'];
                 $return = $this->_currentFile->findNext(T_RETURN, $this->_functionToken, $endToken);
                 if ($return === false) {
                     $this->_currentFile->addError("Function return type is not void, but function has no return statement", $errorPos, 'TypeReturnNotVoidNoStatementFunctionComment');
                 } else {
                     $semicolon = $this->_currentFile->findNext(T_WHITESPACE, $return + 1, null, true);
                     if ($tokens[$semicolon]['code'] === T_SEMICOLON) {
                         $this->_currentFile->addError("Function return type is not void, but function has no return statement", $errorPos, 'TypeReturnNotVoidFunctionComment');
                     }
                 }
             }
         }
     }
 }
示例#10
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)
 {
     $tokens = $phpcsFile->getTokens();
     $next = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true);
     if ($tokens[$next]['code'] !== T_STRING) {
         return;
     }
     $throwExists = isset($tokens[$stackPtr]['scope_closer']) ? $phpcsFile->findNext(T_THROW, $stackPtr + 1, $tokens[$stackPtr]['scope_closer'] - 1) : false;
     if (false === $throwExists) {
         if (isset($this->tags['throws'])) {
             unset($this->tags['throws']);
         }
     } else {
         $tags = $this->tags;
         $this->tags = array_slice($tags, 0, 2);
         $this->tags['throws'] = array('required' => true, 'allow_multiple' => true, 'order_text' => 'follows @return');
         $this->tags += array_slice($tags, 2);
     }
     $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;
     // 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($this->getReqPrefix('REQ.PHP.4.5.6') . $error, $stackPtr);
         return;
     } else {
         if ($code !== T_DOC_COMMENT) {
             $error = 'Missing function doc comment';
             $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.5.1') . $error, $stackPtr);
             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($this->getReqPrefix('REQ.PHP.4.5.1') . 'Missing function doc comment', $stackPtr);
         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, it is 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) {
             $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.5.1') . 'Missing function doc comment', $stackPtr);
             return;
         }
     }
     $comment = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1);
     $this->_methodName = $phpcsFile->getDeclarationName($stackPtr);
     try {
         $this->commentParser = new XLite_FunctionCommentParser($comment, $phpcsFile);
         $this->commentParser->parse();
     } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
         $line = $e->getLineWithinComment() + $commentStart;
         $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.1.1') . $e->getMessage(), $line);
         return;
     }
     $comment = $this->commentParser->getComment();
     if (is_null($comment) === true) {
         $error = 'Function doc comment is empty';
         $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.5.2') . $error, $commentStart);
         return;
     }
     $this->checkAccess($stackPtr, $commentStart, $commentEnd);
     $this->processTags($commentStart, $commentEnd);
     // 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($this->getReqPrefix('REQ.PHP.4.1.7') . $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($this->getReqPrefix('REQ.PHP.4.1.19') . $error, $commentStart + $newlineCount + 1);
         }
         $newlineCount += $newlineBetween;
     }
     // Exactly one blank line before tags.
     $params = $this->commentParser->getTagOrders();
     if (count($params) > 1) {
         $newlineSpan = $comment->getNewlineAfter();
         if ($newlineSpan !== 2) {
             $error = 'There must be exactly one blank line before the tags in function comment';
             if ($long !== '') {
                 $newlineCount += substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1;
             }
             $phpcsFile->addError($this->getReqPrefix('REQ.PHP.4.1.18') . $error, $commentStart + $newlineCount);
             $short = rtrim($short, $phpcsFile->eolChar . ' ');
         }
     }
 }