Passed
Pull Request — master (#19)
by
unknown
02:19
created

GeneralDocSniff::process()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 2
crap 1
1
<?php
2
3
namespace BestIt\Sniffs\Commenting;
4
5
use PHP_CodeSniffer_File;
6
use PHP_CodeSniffer_Sniff;
7
8
/**
9
 * Class GeneralDocSniff
10
 *
11
 * @package BestIt\Sniffs\Commenting
12
 * @author Nick Lubisch <[email protected]>
13
 */
14
class GeneralDocSniff implements PHP_CodeSniffer_Sniff
15
{
16
    /**
17
     * Code that there is no line before the comment.
18
     *
19
     * @var string
20
     */
21
    const CODE_NO_LINE_BEFORE_COMMENT = 'NoLineBeforeComment';
22
23
    /**
24
     * Message that there is no line before the comment.
25
     *
26
     * @var string
27
     */
28
    const MESSAGE_NO_LINE_BEFORE_COMMENT = 'There is no line before the comment.';
29
30
    /**
31
     * Code that are too many lines before comment.
32
     *
33
     * @var string
34
     */
35
    const CODE_MANY_LINES_BEFORE_COMMENT = 'ManyLinesBeforeComment';
36
37
    /**
38
     * Message that are too many lines before comment.
39
     *
40
     * @var string
41
     */
42
    const MESSAGE_MANY_LINES_BEFORE_COMMENT = 'There are too many lines before the comment.';
43
44
    /**
45
     * Code that are too much spaces between comment tag fragments.
46
     *
47
     * @var string
48
     */
49
    const CODE_WRONG_COMMENT_TAG_SPACING = 'WrongCommentTagSpacing';
50
    
51
    /**
52
     * Message that are too much spaces between comment tag fragments.
53
     *
54
     * @var string
55
     */
56
    const MESSAGE_WRONG_COMMENT_TAG_SPACING = 'There must only be 1 space between comment tag fragments.';
57
58
    /**
59
     * The PHP_CodeSniffer file where the token was found.
60
     *
61
     * @var PHP_CodeSniffer_File CodeSniffer file.
62
     */
63
    private $phpcsFile;
64
65
    /**
66
     * The cs file token stack.
67
     *
68
     * @var array
69
     */
70
    private $tokens;
71
72
    /**
73
     * Pointer of the comment start token.
74
     *
75
     * @var int
76
     */
77
    private $commentStartPtr;
78
79
    /**
80
     * Token of the comment start
81
     *
82
     * @var array
83
     */
84
    private $commentStartToken;
85
86
    /**
87
     * Registers the tokens that this sniff wants to listen for.
88
     *
89
     * @return int[] List of tokens to listen for
90
     */
91 4
    public function register()
92
    {
93
        return [
94 4
            T_DOC_COMMENT_OPEN_TAG
95
        ];
96
    }
97
98
    /**
99
     * Called when one of the token types that this sniff is listening for is found.
100
     *
101
     * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where the token was found.
102
     * @param int $commentStartPtr The position in the PHP_CodeSniffer file's token stack where the token was found.
103
     *
104
     * @return void Optionally returns a stack pointer.
105
     */
106 4
    public function process(PHP_CodeSniffer_File $phpcsFile, $commentStartPtr)
107
    {
108 4
        $this->phpcsFile = $phpcsFile;
109 4
        $this->tokens = $phpcsFile->getTokens();
110 4
        $this->commentStartPtr = $commentStartPtr;
111 4
        $this->commentStartToken = $this->tokens[$commentStartPtr];
112
113 4
        $this->checkEmptyLineBeforeComment();
114 4
        $this->checkCommentTagsSpacing();
115 4
    }
116
117
    /**
118
     * Checks if an empty line is before the comment.
119
     *
120
     * @return void
121
     */
122 4
    private function checkEmptyLineBeforeComment()
123
    {
124 4
        $prevNonSpacePtr = $this->phpcsFile->findPrevious(T_WHITESPACE, $this->commentStartPtr - 1, null, true);
125 4
        $prevNonSpaceToken = $this->tokens[$prevNonSpacePtr];
126
127 4
        $hasPrevCurlyBrace = $prevNonSpaceToken['type'] === 'T_OPEN_CURLY_BRACKET';
128
129 4
        $whitespacePtrs = [];
130 4
        for ($i = $prevNonSpacePtr + 1; $i < $this->commentStartPtr; $i++) {
131 4
            $whitespaceToken = $this->tokens[$i];
132
133 4
            if ($whitespaceToken['column'] === 1 && $this->commentStartToken['line'] !== $whitespaceToken['line']) {
134 4
                $whitespacePtrs[] = $i;
135
            }
136
        }
137
138 4
        $expectedLines = $hasPrevCurlyBrace ? 0 : 1;
139
140
        //More than one line before comment
141 4
        if (count($whitespacePtrs) > $expectedLines) {
142 1
            $this->addFixableMuchLinesBeforeCommentError($whitespacePtrs, $hasPrevCurlyBrace);
143
144 1
            return;
145
        }
146
147 4
        if (!$hasPrevCurlyBrace && count($whitespacePtrs) === 0) {
148 1
            $this->addFixableNoLineBeforeCommentError();
149
        }
150 4
    }
151
152
    /**
153
     * Check if there is a comment tag alignment.
154
     *
155
     * @return void
156
     */
157 4
    private function checkCommentTagsSpacing()
158
    {
159 4
        $commentTagPtrs = $this->commentStartToken['comment_tags'];
160
161 4
        if (count($commentTagPtrs) === 0) {
162 3
            return;
163
        }
164
165
        /**
166
         * Array of comment tag pointers.
167
         *
168
         * @var int[] $commentTagPtrs
169
         */
170 4
        foreach ($commentTagPtrs as $commentTagPtr) {
171 4
            $this->checkCommentTagSpacing($commentTagPtr);
172
        }
173 4
    }
174
175
    /**
176
     * Checks a single comment tag alignment.
177
     *
178
     * @param int $commentTagPtr Pointer to comment tag.
179
     *
180
     * @return void
181
     */
182 4
    private function checkCommentTagSpacing($commentTagPtr)
183
    {
184 4
        $lineEndingPtr = $this->phpcsFile->findNext(
185 4
            T_DOC_COMMENT_WHITESPACE,
186
            $commentTagPtr,
187 4
            null,
188 4
            false,
189 4
            $this->phpcsFile->eolChar
190
        );
191
192 4
        for ($tagFragmentPtr = $commentTagPtr; $tagFragmentPtr < $lineEndingPtr; $tagFragmentPtr++) {
193 4
            $tagFragmentToken = $this->tokens[$tagFragmentPtr];
194
195 4
            if ($tagFragmentToken['type'] === 'T_DOC_COMMENT_STRING') {
196 4
                $this->checkCommentTagStringSpacing($tagFragmentPtr);
197
            }
198
199 4
            if ($tagFragmentToken['type'] === 'T_DOC_COMMENT_WHITESPACE' && $tagFragmentToken['length'] > 1) {
200 1
                $this->checkCommentTagWhiteSpacing($tagFragmentPtr);
201
            }
202
        }
203 4
    }
204
205
    /**
206
     * Checks comment tag strings spacing.
207
     *
208
     * @param int $tagStringPtr Pointer to the beginning of a tag
209
     *
210
     * @return void
211
     */
212 4 View Code Duplication
    private function checkCommentTagStringSpacing($tagStringPtr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
213
    {
214 4
        $tagStringToken = $this->tokens[$tagStringPtr];
215
216 4
        $tagStringContent = $tagStringToken['content'];
217
218 4
        if (preg_replace('/\s\s+/', ' ', $tagStringContent) !== $tagStringContent) {
219 1
            $fixStringAlignment = $this->phpcsFile->addFixableError(
220 1
                self::MESSAGE_WRONG_COMMENT_TAG_SPACING,
221
                $tagStringPtr,
222 1
                self::CODE_WRONG_COMMENT_TAG_SPACING
223
            );
224
225 1
            if ($fixStringAlignment) {
226 1
                $this->fixCommentTagStringSpacing($tagStringPtr);
227
            }
228
        }
229 4
    }
230
231
    /**
232
     * Fixes comment tag string spacing.
233
     *
234
     * @param int $tagStringPtr Pointer to the beginning of a tag
235
     *
236
     * @return void
237
     */
238 1
    private function fixCommentTagStringSpacing($tagStringPtr)
239
    {
240 1
        $tagStringToken = $this->tokens[$tagStringPtr];
241 1
        $tagStringContent = $tagStringToken['content'];
242
243 1
        $this->phpcsFile->fixer->replaceToken(
244
            $tagStringPtr,
245 1
            preg_replace('/\s\s+/', ' ', $tagStringContent)
246
        );
247 1
    }
248
249
    /**
250
     * Fixes too long comment tag whitespaces.
251
     *
252
     * @param int $whitespacePtr Pointer to whitespace which is too long.
253
     *
254
     * @return void
255
     */
256 1
    private function fixCommentTagSpacing($whitespacePtr)
257
    {
258 1
        $this->phpcsFile->fixer->replaceToken($whitespacePtr, ' ');
259 1
    }
260
261
    /**
262
     * Fixes lines before comment.
263
     *
264
     * @param int[] $whitespacePtrs Pointers of all whitespaces before comment.
265
     * @param bool $noWhitespace Indicator which controls if there should be minimum one whitespace or not.
266
     *
267
     * @return void
268
     */
269 1
    private function fixMuchLinesBeforeComment(array $whitespacePtrs, $noWhitespace = false)
270
    {
271 1
        $this->phpcsFile->fixer->beginChangeset();
272
273 1
        foreach ($whitespacePtrs as $whitespaceIndex => $whitespacePtr) {
274 1
            if ($whitespaceIndex === 0 && !$noWhitespace) {
275 1
                continue;
276
            }
277
278 1
            $this->phpcsFile->fixer->replaceToken($whitespacePtr, '');
279
        }
280
281 1
        $this->phpcsFile->fixer->endChangeset();
282 1
    }
283
284
    /**
285
     * Fixes no line before comment.
286
     *
287
     * @return void
288
     */
289 1
    private function fixNoLineBeforeComment()
290
    {
291 1
        $this->phpcsFile->fixer->beginChangeset();
292
293 1
        $offset = $this->commentStartToken['column'] === 1 ? 0 : 1;
294
295 1
        $this->phpcsFile->fixer->addNewlineBefore($this->commentStartPtr - $offset);
296
297 1
        $this->phpcsFile->fixer->endChangeset();
298 1
    }
299
300
    /**
301
     * Checks given whitespace for proper spacing.
302
     *
303
     * @param int $whitespacePtr Pointer to whitespace to check.
304
     *
305
     * @return void
306
     */
307 1
    private function checkCommentTagWhiteSpacing($whitespacePtr)
308
    {
309 1
        $fixWrongWhitespace = $this->phpcsFile->addFixableError(
310 1
            self::MESSAGE_WRONG_COMMENT_TAG_SPACING,
311
            $whitespacePtr,
312 1
            self::CODE_WRONG_COMMENT_TAG_SPACING
313
        );
314
315 1
        if ($fixWrongWhitespace) {
316 1
            $this->fixCommentTagSpacing($whitespacePtr);
317
        }
318 1
    }
319
320
    /**
321
     * Adds the fixable much lines before comment error.
322
     *
323
     * @param int[] $whitespacePtrs All whitespace pointers
324
     * @param bool $hasPrevCurlyBrace Indicator if there is a previous curly brace
325
     *
326
     * @return void
327
     */
328 1
    private function addFixableMuchLinesBeforeCommentError($whitespacePtrs, $hasPrevCurlyBrace)
329
    {
330 1
        $fixMuchLines = $this->phpcsFile->addFixableError(
331 1
            self::MESSAGE_MANY_LINES_BEFORE_COMMENT,
332 1
            $this->commentStartPtr,
333 1
            self::CODE_MANY_LINES_BEFORE_COMMENT
334
        );
335
336 1
        if ($fixMuchLines) {
337 1
            $this->fixMuchLinesBeforeComment(
338
                $whitespacePtrs,
339
                $hasPrevCurlyBrace
340
            );
341
        }
342 1
    }
343
344
    /**
345
     * Adds the fixable no line before comment error.
346
     *
347
     * @return void
348
     */
349 1
    private function addFixableNoLineBeforeCommentError()
350
    {
351 1
        $fixNoLine = $this->phpcsFile->addFixableError(
352 1
            self::MESSAGE_NO_LINE_BEFORE_COMMENT,
353 1
            $this->commentStartPtr,
354 1
            self::CODE_NO_LINE_BEFORE_COMMENT
355
        );
356
357 1
        if ($fixNoLine) {
358 1
            $this->fixNoLineBeforeComment();
359
        }
360 1
    }
361
}
362