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

DocDescriptionHelper   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 333
Duplicated Lines 29.13 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 4
dl 97
loc 333
ccs 123
cts 123
cp 1
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
C checkCommentDescription() 0 71 9
A checkCommentDescriptionUcFirst() 20 20 3
B checkCommentDescriptionLineLength() 0 27 5
B getCommentDescriptionEndPointer() 25 25 2
B getCommentDescriptionStartPointer() 25 25 2
A addDescriptionNotFoundError() 0 10 2
A fixNoLineAfterDescription() 14 14 1
A fixMuchLinesAfterDescription() 0 8 1
A fixDescriptionUcFirst() 13 13 1

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
namespace BestIt\CodeSniffer\Helper;
4
5
use BestIt\CodeSniffer\File;
6
use BestIt\Sniffs\Commenting\AbstractDocSniff;
7
8
/**
9
 * Class DocDescriptionHelper
10
 *
11
 * @package BestIt\Helper
12
 * @author Nick Lubisch <[email protected]>
13
 */
14
class DocDescriptionHelper
15
{
16
    /**
17
     * The php code sniffer file.
18
     *
19
     * @var File
20
     */
21
    private $file;
22
23
    /**
24
     * The doc comment helper.
25
     *
26
     * @var DocHelper
27
     */
28
    private $docHelper;
29
30
    /**
31
     * The token stack of the cs file.
32
     *
33
     * @var array
34
     */
35
    private $tokens;
36
37
    /**
38
     * The doc comment summary helper.
39
     *
40
     * @var DocSummaryHelper
41
     */
42
    private $summaryHelper;
43
44
    /**
45
     * Indicator if a description is required.
46
     *
47
     * @var bool
48
     */
49
    private $descriptionRequired;
50
51
    /**
52
     * DocSummaryHelper constructor.
53
     *
54
     * @param File $file The php cs file
55
     * @param DocHelper $docHelper The doc comment helper
56
     * @param DocSummaryHelper $summaryHelper The doc comment summary helper
57
     */
58 109
    public function __construct(File $file, DocHelper $docHelper, DocSummaryHelper $summaryHelper)
59
    {
60 109
        $this->file = $file;
61 109
        $this->tokens = $file->getTokens();
62 109
        $this->docHelper = $docHelper;
63 109
        $this->summaryHelper = $summaryHelper;
64 109
    }
65
66
    /**
67
     * Checks for comment description.
68
     *
69
     * @param bool $descriptionRequired Indicator if the description is required.
70
     *
71
     * @return void
72
     */
73 101
    public function checkCommentDescription($descriptionRequired)
74
    {
75 101
        $this->descriptionRequired = $descriptionRequired;
76
77 101
        $commentStartToken = $this->docHelper->getCommentStartToken();
78 101
        $commentEndToken = $this->docHelper->getCommentEndToken();
79
80 101
        $summaryPtr = $this->summaryHelper->getCommentSummaryPointer();
81
82 101
        if ($summaryPtr === -1) {
83 4
            return;
84
        }
85
86 97
        $hasTags = count($commentStartToken['comment_tags']) > 0;
87
88 97
        $descriptionStartPtr = $this->getCommentDescriptionStartPointer();
89
90 97
        if ($descriptionStartPtr === -1) {
91 11
            $this->addDescriptionNotFoundError($summaryPtr);
92
93 11
            return;
94
        }
95
96 86
        $descriptionEndPtr = $this->getCommentDescriptionEndPointer();
97 86
        $descEndToken = $this->tokens[$descriptionEndPtr];
98
99 86
        $this->checkCommentDescriptionUcFirst($descriptionStartPtr);
100 86
        $this->checkCommentDescriptionLineLength($descriptionStartPtr, $descriptionEndPtr);
0 ignored issues
show
Bug introduced by
It seems like $descriptionEndPtr defined by $this->getCommentDescriptionEndPointer() on line 96 can also be of type boolean; however, BestIt\CodeSniffer\Helpe...DescriptionLineLength() does only seem to accept integer, 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...
101
102
        //Fix no or too much lines after description.
103 86
        $toLine = $commentEndToken['line'];
104 86
        $expectedLines = 0;
105
106 86
        if ($hasTags) {
107 77
            $firstTagPtr = array_shift($commentStartToken['comment_tags']);
108 77
            $firstTagToken = $this->tokens[$firstTagPtr];
109 77
            $toLine = $firstTagToken['line'];
110
111 77
            $expectedLines = 1;
112
        }
113
114 86
        $diffLines = $toLine - $descEndToken['line'] - 1;
115
116 86
        if ($diffLines === $expectedLines) {
117 75
            return;
118
        }
119
120 24
        if ($diffLines < $expectedLines && $hasTags) {
121 12
            $fixNoLine = $this->file->addFixableError(
122 12
                AbstractDocSniff::MESSAGE_NO_LINE_AFTER_DESCRIPTION,
123
                $descriptionEndPtr,
0 ignored issues
show
Bug introduced by
It seems like $descriptionEndPtr defined by $this->getCommentDescriptionEndPointer() on line 96 can also be of type boolean; however, BestIt\CodeSniffer\File::addFixableError() does only seem to accept integer, 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...
124 12
                AbstractDocSniff::CODE_NO_LINE_AFTER_DESCRIPTION
125
            );
126
127 12
            if ($fixNoLine) {
128 8
                $this->fixNoLineAfterDescription();
129
            }
130
131 12
            return;
132
        }
133
134 16
        $fixMuchLines = $this->file->addFixableError(
135 16
            AbstractDocSniff::MESSAGE_MUCH_LINES_AFTER_DESCRIPTION,
136
            $descriptionEndPtr,
0 ignored issues
show
Bug introduced by
It seems like $descriptionEndPtr defined by $this->getCommentDescriptionEndPointer() on line 96 can also be of type boolean; however, BestIt\CodeSniffer\File::addFixableError() does only seem to accept integer, 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...
137 16
            AbstractDocSniff::CODE_MUCH_LINES_AFTER_DESCRIPTION
138
        );
139
140 16
        if ($fixMuchLines) {
141 8
            $this->fixMuchLinesAfterDescription($descEndToken['line'] + 1, $toLine - 1);
142
        }
143 16
    }
144
145
    /**
146
     * Checks if the description starts with a capital letter.
147
     *
148
     * @param int $descriptionStartPtr Pointer to the start of the description.
149
     *
150
     * @return void
151
     */
152 86 View Code Duplication
    private function checkCommentDescriptionUcFirst($descriptionStartPtr)
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...
153
    {
154 86
        $descStartToken = $this->tokens[$descriptionStartPtr];
155
156 86
        $descriptionContent = $descStartToken['content'];
157
158 86
        if (ucfirst($descriptionContent) === $descriptionContent) {
159 82
            return;
160
        }
161
162 8
        $fixUcFirst = $this->file->addFixableError(
163 8
            AbstractDocSniff::MESSAGE_DESCRIPTION_UC_FIRST,
164
            $descriptionStartPtr,
165 8
            AbstractDocSniff::CODE_DESCRIPTION_UC_FIRST
166
        );
167
168 8
        if ($fixUcFirst) {
169 4
            $this->fixDescriptionUcFirst($descriptionStartPtr);
170
        }
171 8
    }
172
173
    /**
174
     * Checks the line length of each line of the comment description.
175
     *
176
     * @param int $descriptionStartPtr Pointer to the start of the description.
177
     * @param int $descriptionEndPtr Pointer to the end of the description.
178
     *
179
     * @return void
180
     */
181 86
    private function checkCommentDescriptionLineLength($descriptionStartPtr, $descriptionEndPtr)
182
    {
183 86
        $diffTokens = array_slice(
184 86
            $this->tokens,
185
            $descriptionStartPtr,
186 86
            $descriptionEndPtr - $descriptionStartPtr + 2,
187 86
            true
188
        );
189
190 86
        foreach ($diffTokens as $diffToken) {
191 86
            if ($diffToken['type'] !== 'T_DOC_COMMENT_WHITESPACE') {
192 86
                continue;
193
            }
194
195 86
            if ($diffToken['content'] !== $this->file->getEolChar()) {
196 86
                continue;
197
            }
198
199 86
            if ($diffToken['column'] > AbstractDocSniff::MAX_LINE_LENGTH) {
200 4
                $this->file->addErrorOnLine(
201 4
                    AbstractDocSniff::MESSAGE_DESCRIPTION_TOO_LONG,
202 4
                    $diffToken['line'],
203 86
                    AbstractDocSniff::CODE_DESCRIPTION_TOO_LONG
204
                );
205
            }
206
        }
207 86
    }
208
209
    /**
210
     * Returns pointer to the end of the description.
211
     *
212
     * @return int Pointer to the end of the description or false
213
     */
214 86 View Code Duplication
    private function getCommentDescriptionEndPointer()
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...
215
    {
216 86
        $descriptionStartPtr = $this->getCommentDescriptionStartPointer();
217
218 86
        $commentStartToken = $this->docHelper->getCommentStartToken();
219 86
        $commentEndPtr = $this->docHelper->getCommentEndPointer();
220
221
        //If no tags found, possible end of search is the starting tag of the doc comment.
222 86
        if (count($commentStartToken['comment_tags']) === 0) {
223 11
            return $this->file->findPrevious(
224 11
                [T_DOC_COMMENT_STRING],
225 11
                $commentEndPtr - 1,
226
                $descriptionStartPtr
227
            );
228
        }
229
230
        //else its the pointer of the first comment tag found.
231 77
        $firstTagPtr = array_shift($commentStartToken['comment_tags']);
232
233 77
        return $this->file->findPrevious(
234 77
            [T_DOC_COMMENT_STRING],
235 77
            $firstTagPtr - 1,
236
            $descriptionStartPtr
237
        );
238
    }
239
240
    /**
241
     * Returns pointer to the start of the long description or false if not found.
242
     *
243
     * @return int Pointer to the start of the description or -1
244
     */
245 97 View Code Duplication
    private function getCommentDescriptionStartPointer()
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...
246
    {
247 97
        $commentStartToken = $this->docHelper->getCommentStartToken();
248 97
        $commentEndPtr = $this->docHelper->getCommentEndPointer();
249
250 97
        $summaryPtr = $this->summaryHelper->getCommentSummaryPointer();
251
252
        //If no tags the possible end of search is the closing tag of the doc comment.
253 97
        if (count($commentStartToken['comment_tags']) === 0) {
254 11
            return $this->file->findNext(
255 11
                [T_DOC_COMMENT_STRING],
256 11
                $summaryPtr + 1,
257
                $commentEndPtr
0 ignored issues
show
Bug introduced by
It seems like $commentEndPtr defined by $this->docHelper->getCommentEndPointer() on line 248 can also be of type boolean; however, BestIt\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...
258
            );
259
        }
260
261
        //else its the pointer of the first comment tag found.
262 88
        $firstTagPtr = array_shift($commentStartToken['comment_tags']);
263
264 88
        return $this->file->findNext(
265 88
            [T_DOC_COMMENT_STRING],
266 88
            $summaryPtr + 1,
267 88
            $firstTagPtr - 1
268
        );
269
    }
270
271
    /**
272
     * Adds error when description is not found.
273
     *
274
     * @param int $summaryPtr Pointer to summary token.
275
     *
276
     * @return void
277
     */
278 11
    private function addDescriptionNotFoundError($summaryPtr)
279
    {
280 11
        if ($this->descriptionRequired) {
281 4
            $this->file->addError(
282 4
                AbstractDocSniff::MESSAGE_DESCRIPTION_NOT_FOUND,
283
                $summaryPtr,
284 4
                AbstractDocSniff::CODE_DESCRIPTION_NOT_FOUND
285
            );
286
        }
287 11
    }
288
289
    /**
290
     * Fixes no line after description.
291
     *
292
     * @return void
293
     */
294 8 View Code Duplication
    private function fixNoLineAfterDescription()
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...
295
    {
296 8
        $descEndPtr = $this->getCommentDescriptionEndPointer();
297 8
        $descEndToken = $this->tokens[$descEndPtr];
298
299 8
        $this->file->getFixer()->beginChangeset();
300
301 8
        $this->file->getFixer()->addContent(
302
            $descEndPtr,
0 ignored issues
show
Bug introduced by
It seems like $descEndPtr defined by $this->getCommentDescriptionEndPointer() on line 296 can also be of type boolean; however, BestIt\CodeSniffer\Fixer::addContent() does only seem to accept integer, 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...
303 8
            $this->file->getEolChar() . str_repeat('    ', $descEndToken['level']) . ' *'
304
        );
305
306 8
        $this->file->getFixer()->endChangeset();
307 8
    }
308
309
    /**
310
     * Fixes much lines after description.
311
     *
312
     * @param int $startLine Line to start removing
313
     * @param int $endLine Line to end removing
314
     *
315
     * @return void
316
     */
317 8
    private function fixMuchLinesAfterDescription($startLine, $endLine)
318
    {
319 8
        $this->file->getFixer()->beginChangeset();
320
321 8
        $this->file->getFixer()->removeLines($startLine, $endLine);
322
323 8
        $this->file->getFixer()->endChangeset();
324 8
    }
325
326
    /**
327
     * Fixes the description uc first.
328
     *
329
     * @param int $descriptionStartPtr Pointer to the description start
330
     *
331
     * @return void
332
     */
333 4 View Code Duplication
    private function fixDescriptionUcFirst($descriptionStartPtr)
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...
334
    {
335 4
        $descStartToken = $this->tokens[$descriptionStartPtr];
336
337 4
        $this->file->getFixer()->beginChangeset();
338
339 4
        $this->file->getFixer()->replaceToken(
340
            $descriptionStartPtr,
341 4
            ucfirst($descStartToken['content'])
342
        );
343
344 4
        $this->file->getFixer()->endChangeset();
345 4
    }
346
}
347