Completed
Pull Request — master (#19)
by
unknown
07:50
created

GeneralDocSniff   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 350
Duplicated Lines 7.71 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 2
dl 27
loc 350
ccs 108
cts 108
cp 1
rs 9
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A register() 0 6 1
A process() 0 10 1
C checkEmptyLineBeforeComment() 0 29 8
A checkCommentTagsSpacing() 0 17 3
B checkCommentTagSpacing() 0 22 5
A checkCommentTagStringSpacing() 0 18 3
A fixCommentTagStringSpacing() 0 10 1
A fixCommentTagSpacing() 0 4 1
A fixMuchLinesBeforeComment() 0 14 4
A fixNoLineBeforeComment() 0 10 2
A checkCommentTagWhiteSpacing() 0 12 2
A addFixableMuchLinesBeforeCommentError() 15 15 2
A addFixableNoLineBeforeCommentError() 12 12 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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