3
* Verifies that a @throws tag exists for a function that throws exceptions.
4
* Verifies the number of @throws tags and the number of throw tokens matches.
5
* Verifies the exception type.
10
* @package PHP_CodeSniffer
11
* @author Greg Sherwood <gsherwood@squiz.net>
12
* @author Marc McIntyre <mmcintyre@squiz.net>
13
* @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
14
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
15
* @link http://pear.php.net/package/PHP_CodeSniffer
18
if (class_exists('PHP_CodeSniffer_Standards_AbstractScopeSniff', true) === false) {
19
$error = 'Class PHP_CodeSniffer_Standards_AbstractScopeSniff not found';
20
throw new PHP_CodeSniffer_Exception($error);
24
* Verifies that a @throws tag exists for a function that throws exceptions.
25
* Verifies the number of @throws tags and the number of throw tokens matches.
26
* Verifies the exception type.
29
* @package PHP_CodeSniffer
30
* @author Greg Sherwood <gsherwood@squiz.net>
31
* @author Marc McIntyre <mmcintyre@squiz.net>
32
* @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
33
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
34
* @version Release: 1.5.5
35
* @link http://pear.php.net/package/PHP_CodeSniffer
37
class Squiz_Sniffs_Commenting_FunctionCommentThrowTagSniff extends PHP_CodeSniffer_Standards_AbstractScopeSniff
42
* Constructs a Squiz_Sniffs_Commenting_FunctionCommentThrowTagSniff.
44
public function __construct()
46
parent::__construct(array(T_FUNCTION), array(T_THROW));
52
* Processes the function tokens within the class.
54
* @param PHP_CodeSniffer_File $phpcsFile The file where this token was found.
55
* @param int $stackPtr The position where the token was found.
56
* @param int $currScope The current scope opener token.
60
protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope)
62
// Is this the first throw token within the current function scope?
63
// If so, we have to validate other throw tokens within the same scope.
64
$previousThrow = $phpcsFile->findPrevious(T_THROW, ($stackPtr - 1), $currScope);
65
if ($previousThrow !== false) {
69
$tokens = $phpcsFile->getTokens();
79
$commentEnd = $phpcsFile->findPrevious($find, ($currScope - 1));
81
if ($commentEnd === false) {
85
if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT) {
86
// Function doesn't have a comment. Let someone else warn about that.
90
$commentStart = ($phpcsFile->findPrevious(T_DOC_COMMENT, ($commentEnd - 1), null, true) + 1);
91
$comment = $phpcsFile->getTokensAsString($commentStart, ($commentEnd - $commentStart + 1));
94
$this->commentParser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($comment, $phpcsFile);
95
$this->commentParser->parse();
96
} catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
97
$line = ($e->getLineWithinComment() + $commentStart);
98
$phpcsFile->addError($e->getMessage(), $line, 'FailedParse');
102
// Find the position where the current function scope ends.
104
if (isset($tokens[$currScope]['scope_closer']) === true) {
105
$currScopeEnd = $tokens[$currScope]['scope_closer'];
108
// Find all the exception type token within the current scope.
109
$throwTokens = array();
110
$currPos = $stackPtr;
111
if ($currScopeEnd !== 0) {
112
while ($currPos < $currScopeEnd && $currPos !== false) {
115
If we can't find a NEW, we are probably throwing
116
a variable, so we ignore it, but they still need to
117
provide at least one @throws tag, even through we
118
don't know the exception class.
121
$nextToken = $phpcsFile->findNext(T_WHITESPACE, ($currPos + 1), null, true);
122
if ($tokens[$nextToken]['code'] === T_NEW) {
123
$currException = $phpcsFile->findNext(
135
if ($currException !== false) {
136
$endException = $phpcsFile->findNext(
141
($currException + 1),
148
if ($endException === false) {
149
$throwTokens[] = $tokens[$currException]['content'];
151
$throwTokens[] = $phpcsFile->getTokensAsString($currException, ($endException - $currException));
156
$currPos = $phpcsFile->findNext(T_THROW, ($currPos + 1), $currScopeEnd);
160
// Only need one @throws tag for each type of exception thrown.
161
$throwTokens = array_unique($throwTokens);
164
$throws = $this->commentParser->getThrows();
165
if (empty($throws) === true) {
166
$error = 'Missing @throws tag in function comment';
167
$phpcsFile->addError($error, $commentEnd, 'Missing');
168
} else if (empty($throwTokens) === true) {
169
// If token count is zero, it means that only variables are being
170
// thrown, so we need at least one @throws tag (checked above).
171
// Nothing more to do.
174
$throwTags = array();
175
$lineNumber = array();
176
foreach ($throws as $throw) {
177
$throwTags[] = $throw->getValue();
178
$lineNumber[$throw->getValue()] = $throw->getLine();
181
$throwTags = array_unique($throwTags);
184
// Make sure @throws tag count matches throw token count.
185
$tokenCount = count($throwTokens);
186
$tagCount = count($throwTags);
187
if ($tokenCount !== $tagCount) {
188
$error = 'Expected %s @throws tag(s) in function comment; %s found';
193
$phpcsFile->addError($error, $commentEnd, 'WrongNumber', $data);
196
// Exception type in @throws tag must be thrown in the function.
197
foreach ($throwTags as $i => $throwTag) {
198
$errorPos = ($commentStart + $lineNumber[$throwTag]);
199
if (empty($throwTag) === false && $throwTag !== $throwTokens[$i]) {
200
$error = 'Expected "%s" but found "%s" for @throws tag exception';
205
$phpcsFile->addError($error, $errorPos, 'WrongType', $data);
211
}//end processTokenWithinScope()