Passed
Pull Request — master (#13)
by Tim
03:16 queued 01:27
created

MethodDocSniff::process()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
ccs 11
cts 11
cp 1
rs 8.8571
cc 2
eloc 18
nc 2
nop 2
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
6
namespace BestIt\Sniffs\Commenting;
7
8
use PHP_CodeSniffer_File;
9
use PHP_CodeSniffer_Sniff;
10
11
/**
12
 * This Sniff checks the method phpdoc
13
 * It is a modified version of the Generic DocCommentSniff adjusted for the bestit codestyle
14
 *
15
 * @package BestIt\Sniffs\Commenting
16
 * @author Nils Hardeweg <[email protected]>
17
 */
18
class MethodDocSniff implements PHP_CodeSniffer_Sniff
19
{
20
    #region constants
21
    /**
22
     * Code for an empty doc block
23
     *
24
     * @var string
25
     */
26
    const CODE_EMPTY = 'Empty';
27
28
    /**
29
     * Message for an empty doc block
30
     *
31
     * @var string
32
     */
33
    const MESSAGE_DOC_EMPTY = 'Doc comment is empty';
34
35
    /**
36
     * Code for a Spacing Error after the Summary
37
     *
38
     * @var string
39
     */
40
    const CODE_SPACING_AFTER = 'SpacingAfter';
41
42
    /**
43
     * Message for a Spacing Error after the Summary
44
     *
45
     * @var string
46
     */
47
    const MESSAGE_SPACING_AFTER = 'Additional blank lines found at end of doc comment';
48
49
    /**
50
     * Code for missing spacing between descriptions
51
     *
52
     * @var string
53
     */
54
    const CODE_SPACING_BETWEEN = 'SpacingBetween';
55
56
    /**
57
     * Message for missing spacing between descriptions
58
     *
59
     * @var string
60
     */
61
    const MESSAGE_SPACING_BETWEEN = 'There must be exactly one blank line between descriptions in a doc comment';
62
63
    /**
64
     * Code for missing spacing before the tags
65
     *
66
     * @var string
67
     */
68
    const CODE_SPACING_BEFORE_TAGS = 'SpacingBeforeTags';
69
70
    /**
71
     * Message for missing spacing before the tags
72
     *
73
     * @var string
74
     */
75
    const MESSAGE_SPACING_BEFORE_TAGS = 'There must be exactly one blank line before the tags in a doc comment';
76
77
    /**
78
     * Code for missing short description (Summary)
79
     *
80
     * @var string
81
     */
82
    const CODE_MISSING_SHORT = 'MissingShort';
83
84
    /**
85
     * Message for missing short description (Summary)
86
     *
87
     * @var string
88
     */
89
    const MESSAGE_MISSING_SHORT = 'Missing short description in doc comment';
90
91
    /**
92
     * Code for not capitalized short description
93
     *
94
     * @var string
95
     */
96
    const CODE_SHORT_NOT_CAPITAL = 'ShortNotCapital';
97
98
    /**
99
     * Message for not capitalized short description
100
     *
101
     * @var string
102
     */
103
    const MESSAGE_SHORT_NOT_CAPITAL = 'Doc comment short description must start with a capital letter';
104
105
    /**
106
     * Code for not Capitalized long description
107
     *
108
     * @var string
109
     */
110
    const CODE_LONG_NOT_CAPITAL = 'LongNotCapital';
111
112
    /**
113
     * Message for not Capitalized long description
114
     *
115
     * @var string
116
     */
117
    const MESSAGE_LONG_NOT_CAPITAL = 'Doc comment long description must start with a capital letter';
118
119
    /**
120
     * Code for empty lines before the short description
121
     *
122
     * @var string
123
     */
124
    const CODE_SPACING_BEFORE_SHORT = 'SpacingBeforeShort';
125
126
    /**
127
     * Message for empty lines before the short description
128
     *
129
     * @var string
130
     */
131
    const MESSAGE_CODE_SPACING_BEFORE = 'Doc comment short description must be on the first line';
132
    #endregion
133
134
    /**
135
     * A list of tokenizers this sniff supports.
136
     *
137
     * @var array
138
     */
139
    public $supportedTokenizers = ['PHP', 'JS'];
140
141
    /**
142
     * Returns an array of tokens this test wants to listen for.
143
     *
144
     * @return array
145
     */
146 8
    public function register()
147
    {
148 8
        return [T_DOC_COMMENT_OPEN_TAG];
149
    }
150
151 7
    private function checkBlankLines(PHP_CodeSniffer_File $phpcsFile, $tokens, $commentEnd, $prev)
152
    {
153
        // Check for additional blank lines at the end of the comment.
154 7 View Code Duplication
        if ($tokens[$prev]['line'] < ($tokens[$commentEnd]['line'] - 1)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
155
            $error = self::MESSAGE_SPACING_AFTER;
156
            $phpcsFile->addError($error, $commentEnd, self::CODE_SPACING_AFTER);
157
        }
158 7
    }
159
160 6
    private function checkSummaryAndLongDescription(
161
        PHP_CodeSniffer_File $phpcsFile,
162
        $stackPtr,
163
        $short,
164
        $tokens,
165
        $empty,
166
        $commentStart,
167
        $commentEnd
168
    ) {
169
        // No extra newline before short description.
170 6
        if ($tokens[$short]['line'] !== ($tokens[$stackPtr]['line'] + 1) && $tokens[$short]['content'] !== '@var') {
171 1
            $error = self::MESSAGE_CODE_SPACING_BEFORE;
172 1
            $phpcsFile->addError($error, $short, self::CODE_SPACING_BEFORE_SHORT);
173
        }
174
175 6
        $shortContent = $tokens[$short]['content'];
176
        /** @var int $shortEnd */
177 6
        $shortEnd = $short;
178
179 6
        if (preg_match('/^\p{Ll}/u', $shortContent) === 1) {
180 1
            $error = self::MESSAGE_SHORT_NOT_CAPITAL;
181 1
            $phpcsFile->addError($error, $short, self::CODE_SHORT_NOT_CAPITAL);
182
        }
183
184 6
        $long = $phpcsFile->findNext($empty, $shortEnd + 1, $commentEnd - 1, true);
185 6
        if ($long !== false && $tokens[$long]['code'] === T_DOC_COMMENT_STRING && preg_match('/^\p{Ll}/u',
186 6
                $tokens[$long]['content']) === 1
187
        ) {
188 1
            $error = self::MESSAGE_LONG_NOT_CAPITAL;
189 1
            $phpcsFile->addError($error, $long, self::CODE_LONG_NOT_CAPITAL);
190
        }
191
192 6
        $long = $phpcsFile->findNext($empty, $shortEnd + 1, $commentEnd - 1, true);
193 6
        if ($long !== false && $tokens[$long]['code'] === T_DOC_COMMENT_STRING) {
194 2 View Code Duplication
            if ($tokens[$long]['line'] !== ($tokens[$shortEnd]['line'] + 2)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
195 1
                $error = self::MESSAGE_SPACING_BETWEEN;
196 1
                $phpcsFile->addError($error, $long, self::CODE_SPACING_BETWEEN);
197
            }
198
199 2
            if (preg_match('/^\p{Ll}/u', $tokens[$long]['content']) === 1) {
200 1
                $error = self::MESSAGE_LONG_NOT_CAPITAL;
201 1
                $phpcsFile->addError($error, $long, self::CODE_LONG_NOT_CAPITAL);
202
            }
203
        }
204
205 6
        if (!empty($tokens[$commentStart]['comment_tags'])) {
206 6
            $firstTag = $tokens[$commentStart]['comment_tags'][0];
207 6
            $prev = $phpcsFile->findPrevious($empty, $firstTag - 1, $stackPtr, true);
208 6
            if ($tokens[$firstTag]['line'] !== ($tokens[$prev]['line'] + 2)
209 6
                && $tokens[$short]['content'] !== '@inheritdoc'
210 6
                && $tokens[$short]['content'] !== '@var'
211
            ) {
212 1
                $error = self::MESSAGE_SPACING_BEFORE_TAGS;
213 1
                $phpcsFile->addError($error, $firstTag, self::CODE_SPACING_BEFORE_TAGS);
214
            }
215
        }
216 6
    }
217
218 7
    private function checkMethodPhpDoc(
219
        PHP_CodeSniffer_File $phpcsFile,
220
        $stackPtr,
221
        $commentStart,
222
        $commentEnd,
223
        $empty,
224
        $tokens,
225
        $short
226
    ) {
227
        // The last line of the comment should just be the */ code.
228 7
        $prev = $phpcsFile->findPrevious($empty, $commentEnd - 1, $stackPtr, true);
229
230 7
        $this->checkBlankLines($phpcsFile, $tokens, $commentEnd, $prev);
231
232
        // Check for a comment description.
233 7
        if ($tokens[$short]['code'] !== T_DOC_COMMENT_STRING
234 7
            && $tokens[$short]['content'] !== '@inheritdoc'
235 7
            && $tokens[$short]['content'] !== '@var'
236
        ) {
237 1
            $error = self::MESSAGE_MISSING_SHORT;
238 1
            $phpcsFile->addError($error, $stackPtr, self::CODE_MISSING_SHORT);
239
        } else {
240 6
            $this->checkSummaryAndLongDescription(
241
                $phpcsFile,
242
                $stackPtr,
243
                $short,
244
                $tokens,
245
                $empty,
246
                $commentStart,
247
                $commentEnd
248
            );
249
        }
250 7
    }
251
252
    /**
253
     * Processes this test, when one of its tokens is encountered.
254
     *
255
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
256
     * @param int $stackPtr The position of the current token in the stack passed in $tokens.
257
     *
258
     * @return void
259
     */
260 8
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
261
    {
262 8
        $tokens = $phpcsFile->getTokens();
263 8
        $commentStart = $stackPtr;
264 8
        $commentEnd = $tokens[$stackPtr]['comment_closer'];
265
266 8
        $empty = [T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR];
267
268 8
        $short = $phpcsFile->findNext($empty, $stackPtr + 1, $commentEnd, true);
269
270 8
        if ($short === false) {
271
            // No content at all.
272 1
            $error = self::MESSAGE_DOC_EMPTY;
273 1
            $phpcsFile->addError($error, $stackPtr, self::CODE_EMPTY);
274
        } else {
275 7
            $this->checkMethodPhpDoc(
276
                $phpcsFile,
277
                $stackPtr,
278
                $commentStart,
279
                $commentEnd,
280
                $empty,
281
                $tokens,
282
                $short
283
            );
284
        }
285 8
    }
286
}
287