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 |
|
|
|
|
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 |
|
|
|
|
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
|
|
|
|
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.