/** * Process the see tags. * * @param int $commentStart The position in the stack where the comment started. * * @return void */ protected function processSees($commentStart) { $sees = $this->commentParser->getSees(); if (empty($sees) === false) { $tagOrder = $this->commentParser->getTagOrders(); $index = array_keys($this->commentParser->getTagOrders(), 'see'); foreach ($sees as $i => $see) { $errorPos = $commentStart + $see->getLine(); $since = array_keys($tagOrder, 'since'); if (count($since) === 1 && $this->_tagIndex !== 0) { $this->_tagIndex++; if ($index[$i] !== $this->_tagIndex) { $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::SEE_ORDER); } } $content = $see->getContent(); if (empty($content) === true) { $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::EMPTY_SEE, ['function']); continue; } } } }
/** * 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']; $missing_doc = 'Missing function doc comment. Or use "@see parent::methodName"'; 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_doc, $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_doc, $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_doc, $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; } // Handle @sees, if there is one like "parent::methodeName" then it's enough $sees = $this->commentParser->getSees(); if (count($sees) === 1) { $see = reset($sees); $see_parent = "parent::{$this->_methodName}"; if (strpos($see->getContent(), $see_parent) === 0) { 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 . ' '); } } }