/**
  * 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']);
     }
 }
 /**
  * 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 . ' ');
         }
     }
 }