/**
  * 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;
     }
     /** @var PHP_CodeSniffer_CommentParser_CommentElement $comment */
     $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) {
         $line = $newlineSpan > 1 ? 'newlines' : 'newline';
         $error = "Extra {$line} 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 . ' ');
         }
     }
 }
 /**
  * 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->helper = new M2_Sniffs_Annotations_Helper($phpcsFile);
     // if we should skip this type we should do that
     if ($this->helper->shouldFilter()) {
         return;
     }
     $tokens = $phpcsFile->getTokens();
     $find = [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']) {
             $this->helper->addMessage($stackPtr, M2_Sniffs_Annotations_Helper::MISSING, ['function']);
         } else {
             $this->helper->addMessage($stackPtr, M2_Sniffs_Annotations_Helper::WRONG_STYLE, ['function']);
         }
         return;
     } elseif ($code !== T_DOC_COMMENT) {
         $this->helper->addMessage($stackPtr, M2_Sniffs_Annotations_Helper::MISSING, ['function']);
         return;
     } elseif (trim($tokens[$commentEnd]['content']) !== '*/') {
         $this->helper->addMessage($commentEnd, M2_Sniffs_Annotations_Helper::WRONG_END, [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) {
         $this->helper->addMessage($stackPtr, M2_Sniffs_Annotations_Helper::MISSING, ['function']);
         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;
         }
     }
     // Find the first doc comment.
     $commentStart = $phpcsFile->findPrevious(T_DOC_COMMENT, $commentEnd - 1, null, true) + 1;
     $commentString = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1);
     $this->_methodName = $phpcsFile->getDeclarationName($stackPtr);
     try {
         $this->commentParser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($commentString, $phpcsFile);
         $this->commentParser->parse();
     } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
         $line = $e->getLineWithinComment() + $commentStart;
         $this->helper->addMessage($line, M2_Sniffs_Annotations_Helper::FAILED_PARSE, [$e->getMessage()]);
         return;
     }
     $comment = $this->commentParser->getComment();
     if (($comment === null) === true) {
         $this->helper->addMessage($commentStart, M2_Sniffs_Annotations_Helper::EMPTY_DOC, ['Function']);
         return;
     }
     // The first line of the comment should just be the /** code.
     $eolPos = strpos($commentString, $phpcsFile->eolChar);
     $firstLine = substr($commentString, 0, $eolPos);
     if ($firstLine !== '/**') {
         $this->helper->addMessage($commentStart, M2_Sniffs_Annotations_Helper::CONTENT_AFTER_OPEN);
     }
     // If the comment has an inherit doc note just move on
     if (preg_match('/\\{\\@inheritdoc\\}/', $commentString)) {
         return;
     } elseif (preg_match('/\\{?\\@?inherit[dD]oc\\}?/', $commentString)) {
         $this->helper->addMessage($commentStart, M2_Sniffs_Annotations_Helper::INCORRECT_INHERIT_DOC);
         return;
     }
     $this->processParams($commentStart, $commentEnd);
     $this->processSees($commentStart);
     $this->processReturn($commentStart, $commentEnd);
     $this->processThrows($commentStart);
     // Check for a comment description.
     $short = $comment->getShortComment();
     if (trim($short) === '') {
         $this->helper->addMessage($commentStart, M2_Sniffs_Annotations_Helper::MISSING_SHORT, ['function']);
         return;
     }
     // No extra newline before short description.
     $newlineCount = 0;
     $newlineSpan = strspn($short, $phpcsFile->eolChar);
     if ($short !== '' && $newlineSpan > 0) {
         $this->helper->addMessage($commentStart + 1, M2_Sniffs_Annotations_Helper::SPACING_BEFORE_SHORT, ['function']);
     }
     $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) {
             $this->helper->addMessage($commentStart + $newlineCount + 1, M2_Sniffs_Annotations_Helper::SPACING_BETWEEN, ['function']);
         }
         $newlineCount += $newlineBetween;
         $testLong = trim($long);
         if (preg_match('|\\p{Lu}|u', $testLong[0]) === 0) {
             $this->helper->addMessage($commentStart + $newlineCount, M2_Sniffs_Annotations_Helper::LONG_NOT_CAPITAL, ['Function']);
         }
     }
     // Exactly one blank line before tags.
     $params = $this->commentParser->getTagOrders();
     if (count($params) > 1) {
         $newlineSpan = $comment->getNewlineAfter();
         if ($newlineSpan !== 2) {
             if ($long !== '') {
                 $newlineCount += substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1;
             }
             $this->helper->addMessage($commentStart + $newlineCount, M2_Sniffs_Annotations_Helper::SPACING_BEFORE_TAGS, ['function']);
             $short = rtrim($short, $phpcsFile->eolChar . ' ');
         }
     }
     // 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) {
         $this->helper->addMessage($commentStart + 1, M2_Sniffs_Annotations_Helper::SHORT_SINGLE_LINE, ['Function']);
     }
     if (preg_match('|\\p{Lu}|u', $testShort[0]) === 0) {
         $this->helper->addMessage($commentStart + 1, M2_Sniffs_Annotations_Helper::SHORT_NOT_CAPITAL, ['Function']);
     }
     if ($lastChar !== '.') {
         $this->helper->addMessage($commentStart + 1, M2_Sniffs_Annotations_Helper::SHORT_FULL_STOP, ['Function']);
     }
     // Check for unknown/deprecated tags.
     // For example call: $this->processUnknownTags($commentStart, $commentEnd);
     // The last content should be a newline and the content before
     // that should not be blank. If there is more blank space
     // then they have additional blank lines at the end of the comment.
     $words = $this->commentParser->getWords();
     $lastPos = count($words) - 1;
     if (trim($words[$lastPos - 1]) !== '' || strpos($words[$lastPos - 1], $this->helper->getCurrentFile()->eolChar) === false || trim($words[$lastPos - 2]) === '') {
         $this->helper->addMessage($commentEnd, M2_Sniffs_Annotations_Helper::SPACING_AFTER, ['function']);
     }
 }
Esempio n. 3
0
 /**
  * {@inheritdoc}
  */
 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
 {
     if ($this->should_ignore_use($phpcsFile, $stackPtr) === true) {
         return;
     }
     $tokens = $phpcsFile->getTokens();
     $class_name_start = $phpcsFile->findNext(array(T_NS_SEPARATOR, T_STRING), $stackPtr + 1);
     $find = array(T_NS_SEPARATOR, T_STRING, T_WHITESPACE);
     $class_name_end = $phpcsFile->findNext($find, $stackPtr + 1, null, true);
     $aliasing_as_position = $phpcsFile->findNext(T_AS, $class_name_end, null, false, null, true);
     if ($aliasing_as_position !== false) {
         $alias_position = $phpcsFile->findNext(T_STRING, $aliasing_as_position, null, false, null, true);
         $class_name_short = $tokens[$alias_position]['content'];
         $class_name_full = $phpcsFile->getTokensAsString($class_name_start, $class_name_end - $class_name_start - 1);
     } else {
         $class_name_full = $phpcsFile->getTokensAsString($class_name_start, $class_name_end - $class_name_start);
         $class_name_short = $tokens[$class_name_end - 1]['content'];
     }
     $ok = false;
     // Checks in simple statements (new, instanceof and extends)
     foreach (array(T_INSTANCEOF, T_NEW, T_EXTENDS) as $keyword) {
         $old_simple_statement = $stackPtr;
         while (($simple_statement = $phpcsFile->findNext($keyword, $old_simple_statement + 1)) !== false) {
             $old_simple_statement = $simple_statement;
             $simple_class_name_start = $phpcsFile->findNext(array(T_NS_SEPARATOR, T_STRING), $simple_statement + 1);
             $simple_class_name_end = $phpcsFile->findNext($find, $simple_statement + 1, null, true);
             $simple_class_name = trim($phpcsFile->getTokensAsString($simple_class_name_start, $simple_class_name_end - $simple_class_name_start));
             $ok = $this->check($phpcsFile, $simple_class_name, $class_name_full, $class_name_short, $simple_statement) ? true : $ok;
         }
     }
     // Checks paamayim nekudotayim
     $old_paamayim_nekudotayim = $stackPtr;
     while (($paamayim_nekudotayim = $phpcsFile->findNext(T_PAAMAYIM_NEKUDOTAYIM, $old_paamayim_nekudotayim + 1)) !== false) {
         $old_paamayim_nekudotayim = $paamayim_nekudotayim;
         $paamayim_nekudotayim_class_name_start = $phpcsFile->findPrevious($find, $paamayim_nekudotayim - 1, null, true);
         $paamayim_nekudotayim_class_name_end = $paamayim_nekudotayim - 1;
         $paamayim_nekudotayim_class_name = trim($phpcsFile->getTokensAsString($paamayim_nekudotayim_class_name_start + 1, $paamayim_nekudotayim_class_name_end - $paamayim_nekudotayim_class_name_start));
         $ok = $this->check($phpcsFile, $paamayim_nekudotayim_class_name, $class_name_full, $class_name_short, $paamayim_nekudotayim) ? true : $ok;
     }
     // Checks in implements
     $old_implements = $stackPtr;
     while (($implements = $phpcsFile->findNext(T_IMPLEMENTS, $old_implements + 1)) !== false) {
         $old_implements = $implements;
         $old_implemented_class = $implements;
         while (($implemented_class = $phpcsFile->findNext(T_STRING, $old_implemented_class + 1, null, false, null, true)) !== false) {
             $old_implemented_class = $implemented_class;
             $implements_class_name_start = $phpcsFile->findNext(array(T_NS_SEPARATOR, T_STRING), $implemented_class - 1);
             $implements_class_name_end = $phpcsFile->findNext($find, $implemented_class - 1, null, true);
             $implements_class_name = trim($phpcsFile->getTokensAsString($implements_class_name_start, $implements_class_name_end - $implements_class_name_start));
             $ok = $this->check($phpcsFile, $implements_class_name, $class_name_full, $class_name_short, $implements) ? true : $ok;
         }
     }
     // Checks in type hinting
     $old_function_declaration = $stackPtr;
     while (($function_declaration = $phpcsFile->findNext(T_FUNCTION, $old_function_declaration + 1)) !== false) {
         $old_function_declaration = $function_declaration;
         // Check docblocks
         $find = array(T_COMMENT, T_DOC_COMMENT, T_CLASS, T_FUNCTION, T_OPEN_TAG);
         $comment_end = $phpcsFile->findPrevious($find, $function_declaration - 1);
         if ($comment_end !== false) {
             if (!$tokens[$comment_end]['code'] !== T_DOC_COMMENT) {
                 $comment_start = $phpcsFile->findPrevious(T_DOC_COMMENT, $comment_end - 1, null, true) + 1;
                 $comment = $phpcsFile->getTokensAsString($comment_start, $comment_end - $comment_start + 1);
                 try {
                     $comment_parser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($comment, $phpcsFile);
                     $comment_parser->parse();
                     // Check @param
                     foreach ($comment_parser->getParams() as $param) {
                         $type = $param->getType();
                         $types = explode('|', str_replace('[]', '', $type));
                         foreach ($types as $type) {
                             $ok = $this->check($phpcsFile, $type, $class_name_full, $class_name_short, $param->getLine() + $comment_start) ? true : $ok;
                         }
                     }
                     // Check @return
                     $return = $comment_parser->getReturn();
                     if ($return !== null) {
                         $type = $return->getValue();
                         $types = explode('|', str_replace('[]', '', $type));
                         foreach ($types as $type) {
                             $ok = $this->check($phpcsFile, $type, $class_name_full, $class_name_short, $return->getLine() + $comment_start) ? true : $ok;
                         }
                     }
                 } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
                     $line = $e->getLineWithinComment() + $comment_start;
                     $phpcsFile->addError($e->getMessage(), $line, 'FailedParse');
                 }
             }
         }
         // Check type hint
         $params = $phpcsFile->getMethodParameters($function_declaration);
         foreach ($params as $param) {
             $ok = $this->check($phpcsFile, $param['type_hint'], $class_name_full, $class_name_short, $function_declaration) ? true : $ok;
         }
     }
     if (!$ok) {
         $error = 'There must not be unused USE statements.';
         $phpcsFile->addError($error, $stackPtr, 'Unused');
     }
 }