Completed
Push — master ( b79486...df4d3f )
by Alexander
02:06
created

FunctionCommentSniff::isInheritDoc()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 2
dl 0
loc 10
ccs 5
cts 5
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 34 and the first side effect is on line 17.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * CodingStandard_Sniffs_Commenting_FunctionCommentSniff.
4
 *
5
 * PHP version 5
6
 *
7
 * @category PHP
8
 * @package  PHP_CodeSniffer
9
 * @author   Greg Sherwood <[email protected]>
10
 * @author   Alexander Obuhovich <[email protected]>
11
 * @license  https://github.com/aik099/CodingStandard/blob/master/LICENSE BSD 3-Clause
12
 * @link     https://github.com/aik099/CodingStandard
13
 */
14
15
// @codeCoverageIgnoreStart
16
if (class_exists('Squiz_Sniffs_Commenting_FunctionCommentSniff', true) === false) {
17
    $error = 'Class Squiz_Sniffs_Commenting_FunctionCommentSniff not found';
18
    throw new PHP_CodeSniffer_Exception($error);
19
}
20
// @codeCoverageIgnoreEnd
21
22
/**
23
 * Parses and verifies the doc comments for functions.
24
 *
25
 * Same as the Squiz standard, but adds support for API tags.
26
 *
27
 * @category PHP
28
 * @package  PHP_CodeSniffer
29
 * @author   Greg Sherwood <[email protected]>
30
 * @author   Alexander Obuhovich <[email protected]>
31
 * @license  https://github.com/aik099/CodingStandard/blob/master/LICENSE BSD 3-Clause
32
 * @link     https://github.com/aik099/CodingStandard
33
 */
34
class CodingStandard_Sniffs_Commenting_FunctionCommentSniff extends Squiz_Sniffs_Commenting_FunctionCommentSniff
0 ignored issues
show
Coding Style introduced by
This class is not in CamelCase format.

Classes in PHP are usually named in CamelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. The whole name starts with a capital letter as well.

Thus the name database provider becomes DatabaseProvider.

Loading history...
35
{
36
37
38
    /**
39
     * Processes this test, when one of its tokens is encountered.
40
     *
41
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
42
     * @param int                  $stackPtr  The position of the current token
43
     *                                        in the stack passed in $tokens.
44
     *
45
     * @return void
46
     */
47 1
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
48
    {
49 1
        parent::process($phpcsFile, $stackPtr);
50
51 1
        $tokens = $phpcsFile->getTokens();
52 1
        $find   = PHP_CodeSniffer_Tokens::$methodPrefixes;
53 1
        $find[] = T_WHITESPACE;
54
55 1
        $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
56 1
        if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG) {
57 1
            return;
58
        }
59
60 1
        $commentStart = $tokens[$commentEnd]['comment_opener'];
61
62
        $empty = array(
63 1
                  T_DOC_COMMENT_WHITESPACE,
64 1
                  T_DOC_COMMENT_STAR,
65 1
                 );
66
67 1
        $short = $phpcsFile->findNext($empty, ($commentStart + 1), $commentEnd, true);
0 ignored issues
show
Bug introduced by
It seems like $commentEnd defined by $phpcsFile->findPrevious...ackPtr - 1, null, true) on line 55 can also be of type boolean; however, PHP_CodeSniffer_File::findNext() does only seem to accept integer|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
68 1
        if ($short === false || $tokens[$short]['code'] !== T_DOC_COMMENT_STRING) {
69 1
            return;
70
        }
71
72 1
        if ($this->isInheritDoc($phpcsFile, $commentStart) === true) {
73 1
            return;
74
        }
75
76 1
        $this->checkShort($phpcsFile, $stackPtr, $tokens[$short]['content'], $short);
77
78 1
    }//end process()
79
80
    /**
81
     * Process the function parameter comments.
82
     *
83
     * @param PHP_CodeSniffer_File $phpcsFile    The file being scanned.
84
     * @param int                  $stackPtr     The position of the current token
85
     *                                           in the stack passed in $tokens.
86
     * @param int                  $commentStart The position in the stack where the comment started.
87
     *
88
     * @return void
89
     */
90 1
    protected function processParams(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $commentStart)
91
    {
92 1
        if ($this->isInheritDoc($phpcsFile, $commentStart) === true) {
93 1
            return;
94
        }
95
96 1
        parent::processParams($phpcsFile, $stackPtr, $commentStart);
97
98 1
    }//end processParams()
99
100
101
    /**
102
     * Process the return comment of this function comment.
103
     *
104
     * @param PHP_CodeSniffer_File $phpcsFile    The file being scanned.
105
     * @param int                  $stackPtr     The position of the current token
106
     *                                           in the stack passed in $tokens.
107
     * @param int                  $commentStart The position in the stack where the comment started.
108
     *
109
     * @return void
110
     */
111 1
    protected function processReturn(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $commentStart)
112
    {
113 1
        if ($this->isInheritDoc($phpcsFile, $commentStart) === true) {
114 1
            return;
115
        }
116
117 1
        parent::processReturn($phpcsFile, $stackPtr, $commentStart);
118
119 1
        $return = null;
120 1
        $tokens = $phpcsFile->getTokens();
121
122 1
        foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
123 1
            if ($tokens[$tag]['content'] === '@return') {
124 1
                $return = $tag;
125 1
                break;
126
            }
127 1
        }
128
129 1
        if ($return !== null) {
130 1
            $returnType = $tokens[($return + 2)]['content'];
131 1
            if ($returnType === '$this') {
132 1
                $error = 'Function return type "%s" is invalid, please use "static" or "self" instead';
133 1
                $data  = array($returnType);
134 1
                $phpcsFile->addError($error, $return, 'InvalidThisReturn', $data);
135 1
            }
136 1
        }
137
138 1
    }//end processReturn()
139
140
    /**
141
     * Process the short description of a function comment.
142
     *
143
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
144
     * @param int                  $stackPtr  The position of the function token
145
     *                                        in the stack passed in $tokens.
146
     * @param string               $short     The content of the short description.
147
     * @param int                  $errorPos  The position where an error should be thrown.
148
     *
149
     * @return void
150
     */
151 1
    public function checkShort(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $short, $errorPos)
152
    {
153 1
        $tokens = $phpcsFile->getTokens();
154
155 1
        $classToken = null;
156 1
        foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condition) {
157 1
            if ($condition === T_CLASS || $condition === T_INTERFACE || $condition === T_TRAIT) {
158 1
                $classToken = $condPtr;
159 1
                break;
160
            }
161 1
        }
162
163 1
        $isEvent = false;
164 1
        if ($classToken !== null) {
165 1
            $className = $phpcsFile->getDeclarationName($classToken);
166 1
            if (strpos($className, 'EventHandler') !== false) {
167 1
                $methodName = $phpcsFile->getDeclarationName($stackPtr);
168 1
                if (substr($methodName, 0, 2) === 'On') {
169 1
                    $isEvent = true;
170 1
                }
171 1
            }
172 1
        }
173
174 1
        if ($isEvent === true && preg_match('/(\p{Lu}|\[)/u', $short[0]) === 0) {
175 1
            $error = 'Event comment short description must start with a capital letter or an [';
176 1
            $phpcsFile->addError($error, $errorPos, 'EventShortNotCapital');
177 1
        } else if ($isEvent === false && preg_match('/\p{Lu}/u', $short[0]) === 0) {
178 1
            $error = 'Doc comment short description must start with a capital letter';
179 1
            $phpcsFile->addError($error, $errorPos, 'NonEventShortNotCapital');
180 1
        }
181
182 1
    }//end checkShort()
183
184
185
    /**
186
     * Process any throw tags that this function comment has.
187
     *
188
     * @param PHP_CodeSniffer_File $phpcsFile    The file being scanned.
189
     * @param int                  $stackPtr     The position of the current token
190
     *                                           in the stack passed in $tokens.
191
     * @param int                  $commentStart The position in the stack where the comment started.
192
     *
193
     * @return void
194
     */
195 1
    protected function processThrows(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $commentStart)
196
    {
197 1
        parent::processThrows($phpcsFile, $stackPtr, $commentStart);
198
199 1
        $tokens = $phpcsFile->getTokens();
200
201 1
        foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
202
            // Only process method with scope (e.g. non-abstract methods or methods on the interface).
203 1
            if ($tokens[$tag]['content'] !== '@throws' || isset($tokens[$stackPtr]['scope_opener']) === false) {
204 1
                continue;
205
            }
206
207 1
            $throwPtr = $phpcsFile->findNext(
208 1
                T_THROW,
209 1
                $tokens[$stackPtr]['scope_opener'],
210 1
                $tokens[$stackPtr]['scope_closer']
211 1
            );
212
213 1
            if ($throwPtr !== false) {
214 1
                break;
215
            }
216
217 1
            $throwsWithString = null;
218 1
            $error            = '@throws tag found, but no exceptions are thrown by the function';
219
220 1
            if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) {
221 1
                $throwsWithString = true;
222 1
            } elseif ($tokens[($tag + 1)]['code'] === T_DOC_COMMENT_WHITESPACE
223 1
                && $tokens[($tag + 1)]['content'] === $phpcsFile->eolChar
224 1
            ) {
225 1
                $throwsWithString = false;
226 1
            }
227
228 1
            if ($tokens[($tag - 2)]['code'] === T_DOC_COMMENT_STAR && isset($throwsWithString) === true) {
229 1
                $fix = $phpcsFile->addFixableError($error, $tag, 'ExcessiveThrows');
230
231 1
                if ($fix === true) {
232 1
                    $phpcsFile->fixer->beginChangeset();
233
234 1
                    $removeEndPtr = $throwsWithString === true ? ($tag + 3) : ($tag + 1);
235
236 1
                    for ($i = ($tag - 4); $i < $removeEndPtr; $i++) {
237 1
                        $phpcsFile->fixer->replaceToken($i, '');
238 1
                    }
239
240 1
                    $phpcsFile->fixer->endChangeset();
241 1
                }
242 1
            } else {
243
                $phpcsFile->addError($error, $tag, 'ExcessiveThrows');
244
            }
245 1
        }//end foreach
246
247 1
    }//end processThrows()
248
249
250
    /**
251
     * Is the comment an inheritdoc?
252
     *
253
     * @param PHP_CodeSniffer_File $phpcsFile    The file being scanned.
254
     * @param int                  $commentStart The position in the stack where the comment started.
255
     *
256
     * @return bool
257
     */
258 1
    protected function isInheritDoc(PHP_CodeSniffer_File $phpcsFile, $commentStart)
259
    {
260 1
        $tokens = $phpcsFile->getTokens();
261
262 1
        $commentEnd = $tokens[$commentStart]['comment_closer'];
263 1
        $commentText = $phpcsFile->getTokensAsString($commentStart, ($commentEnd - $commentStart + 1));
264
265 1
        return stripos($commentText, '{@inheritdoc}') !== false;
266
267
    }// end isInheritDoc()
268
269
}//end class
270