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

DocSummaryHelper::checkSummaryIsFirstLine()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 3
crap 3
1
<?php
2
3
namespace BestIt\CodeSniffer\Helper;
4
5
use BestIt\CodeSniffer\File;
6
use BestIt\Sniffs\Commenting\AbstractDocSniff;
7
8
/**
9
 * Class DocSummaryHelper
10
 *
11
 * @package BestIt\Helper
12
 * @author Nick Lubisch <[email protected]>
13
 */
14
class DocSummaryHelper
15
{
16
    /**
17
     * The php cs 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
     * Token stack of the current file.
32
     *
33
     * @var array
34
     */
35
    private $tokens;
36
37
    /**
38
     * DocSummaryHelper constructor.
39
     *
40
     * @param File $file The php cs file
41
     * @param DocHelper $docHelper The doc comment helper
42
     */
43 109
    public function __construct(File $file, DocHelper $docHelper)
44
    {
45 109
        $this->file = $file;
46 109
        $this->tokens = $file->getTokens();
47 109
        $this->docHelper = $docHelper;
48 109
    }
49
50
    /**
51
     * Returns pointer to the comment summary.
52
     *
53
     * @return int Pointer to the token or -1
54
     */
55 101
    public function getCommentSummaryPointer()
56
    {
57 101
        $commentStartPtr = $this->docHelper->getCommentStartPointer();
58 101
        $commentEndPtr = $this->docHelper->getCommentEndPointer();
59
60 101
        $summaryPtr = $this->file->findNext(
61
            [
62 101
                T_DOC_COMMENT_WHITESPACE,
63 101
                T_DOC_COMMENT_STAR
64
            ],
65 101
            $commentStartPtr + 1,
66
            $commentEndPtr,
0 ignored issues
show
Bug introduced by
It seems like $commentEndPtr defined by $this->docHelper->getCommentEndPointer() on line 58 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...
67 101
            true
68
        );
69
70 101
        $summaryToken = $this->tokens[$summaryPtr];
71
72 101
        return $summaryToken['code'] === T_DOC_COMMENT_STRING ? $summaryPtr : -1;
73
    }
74
75
    /**
76
     * Returns comment summary token.
77
     *
78
     * @return array Summary token array
79
     */
80 97
    public function getCommentSummaryToken()
81
    {
82 97
        return $this->tokens[$this->getCommentSummaryPointer()];
83
    }
84
85
    /**
86
     * Check class comment summary.
87
     *
88
     * @return void
89
     */
90 101
    public function checkCommentSummary()
91
    {
92 101
        $commentEndPtr = $this->docHelper->getCommentEndPointer();
93 101
        $commentStartPtr = $this->docHelper->getCommentStartPointer();
94 101
        $commentStartToken = $this->docHelper->getCommentStartToken();
95
96 101
        $summaryPtr = $this->getCommentSummaryPointer();
97
98 101
        if ($summaryPtr === -1) {
99 4
            $this->file->addError(
100 4
                AbstractDocSniff::MESSAGE_NO_SUMMARY,
101
                $commentStartPtr,
102 4
                AbstractDocSniff::CODE_NO_SUMMARY
103
            );
104
105 4
            return;
106
        }
107
108 97
        $summaryToken = $this->tokens[$summaryPtr];
109
110 97
        $this->checkSummaryIsFirstLine($commentStartToken, $summaryToken, $summaryPtr);
111 97
        $this->checkSummaryCapitalLetter($summaryToken, $summaryPtr);
112 97
        $this->checkSummaryLineLength($summaryToken, $summaryPtr);
113 97
        $this->checkLineAfterSummary($summaryPtr, $commentEndPtr);
0 ignored issues
show
Bug introduced by
It seems like $commentEndPtr defined by $this->docHelper->getCommentEndPointer() on line 92 can also be of type boolean; however, BestIt\CodeSniffer\Helpe...checkLineAfterSummary() 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...
114 97
    }
115
116
    /**
117
     * Checks that the first character of the summary is upper case.
118
     *
119
     * @param array $summaryToken Token of the summary
120
     * @param int $summaryPtr Pointer to the summary
121
     *
122
     * @return void
123
     */
124 97
    private function checkSummaryCapitalLetter(array $summaryToken, $summaryPtr)
125
    {
126 97
        $summaryText = $summaryToken['content'];
127
128 97
        if (ucfirst($summaryText) === $summaryText) {
129 93
            return;
130
        }
131
132 8
        $fixUcFirst = $this->file->addFixableError(
133 8
            AbstractDocSniff::MESSAGE_SUMMARY_UC_FIRST,
134
            $summaryPtr,
135 8
            AbstractDocSniff::CODE_SUMMARY_UC_FIRST
136
        );
137
138 8
        if ($fixUcFirst) {
139 4
            $this->fixSummaryUcFirst($summaryToken, $summaryPtr);
140
        }
141 8
    }
142
143
    /**
144
     * Checks if the line length of the summary is maximum 120 chars.
145
     *
146
     * @param array $summaryToken Token array of the summary
147
     * @param int $summaryPtr Pointer of the summary
148
     *
149
     * @return void
150
     */
151 97
    private function checkSummaryLineLength($summaryToken, $summaryPtr)
152
    {
153 97
        $summaryLineLength = $summaryToken['column'] + $summaryToken['length'];
154
155 97
        if ($summaryLineLength > AbstractDocSniff::MAX_LINE_LENGTH) {
156 4
            $this->file->addError(
157 4
                AbstractDocSniff::MESSAGE_SUMMARY_TOO_LONG,
158
                $summaryPtr,
159 4
                AbstractDocSniff::CODE_SUMMARY_TOO_LONG
160
            );
161
        }
162 97
    }
163
164
    /**
165
     * Checks if the summary is the first line of the comment.
166
     *
167
     * @param array $commentStartToken Token array of the comment start
168
     * @param array $summaryToken Token of the summary
169
     * @param int $summaryPtr Pointer to the summary
170
     *
171
     * @return void
172
     */
173 97
    private function checkSummaryIsFirstLine($commentStartToken, $summaryToken, $summaryPtr)
174
    {
175 97
        if ($summaryToken['line'] !== $commentStartToken['line'] + 1) {
176 8
            $fixSummaryNotFirst = $this->file->addFixableError(
177 8
                AbstractDocSniff::MESSAGE_SUMMARY_NOT_FIRST,
178
                $summaryPtr,
179 8
                AbstractDocSniff::CODE_SUMMARY_NOT_FIRST
180
            );
181
182 8
            if ($fixSummaryNotFirst) {
183 4
                $this->fixSummaryNotFirst();
184
            }
185
        }
186 97
    }
187
188
    /**
189
     * Checks the line after the summary.
190
     *
191
     * @param int $summaryPtr Pointer to the summary
192
     * @param int $commentEndPtr Pointer to the end of the doc comment
193
     *
194
     * @return void
195
     */
196 97
    private function checkLineAfterSummary($summaryPtr, $commentEndPtr)
197
    {
198 97
        $summaryToken = $this->getCommentSummaryToken();
199
200 97
        $nextRelevantPtr = $this->file->findNext(
201
            [
202 97
                T_DOC_COMMENT_WHITESPACE,
203 97
                T_DOC_COMMENT_STAR
204
            ],
205 97
            $summaryPtr + 1,
206
            $commentEndPtr,
207 97
            true
208
        );
209
210 97
        if ($nextRelevantPtr === -1) {
211
            return;
212
        }
213
214 97
        $nextRelevantToken = $this->tokens[$nextRelevantPtr];
215
216 97
        if (($nextRelevantToken['line'] - $summaryToken['line']) === 1) {
217 8
            $fixLineAfterSummary = $this->file->addFixableError(
218 8
                AbstractDocSniff::MESSAGE_NO_LINE_AFTER_SUMMARY,
219
                $summaryPtr,
220 8
                AbstractDocSniff::CODE_NO_LINE_AFTER_SUMMARY
221
            );
222
223 8
            if ($fixLineAfterSummary) {
224 4
                $this->fixNoLineAfterSummary();
225
            }
226
        }
227 97
    }
228
229
    /**
230
     * Fixes no line after summary.
231
     *
232
     * @return void
233
     */
234 4 View Code Duplication
    private function fixNoLineAfterSummary()
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...
235
    {
236 4
        $summaryPtr = $this->getCommentSummaryPointer();
237 4
        $summaryToken = $this->getCommentSummaryToken();
238
239 4
        $this->file->getFixer()->beginChangeset();
240
241 4
        $this->file->getFixer()->addContent(
242
            $summaryPtr,
243 4
            $this->file->getEolChar() . str_repeat('    ', $summaryToken['level']) . ' *'
244
        );
245
246 4
        $this->file->getFixer()->endChangeset();
247 4
    }
248
249
    /**
250
     * Fixes summary not first statement.
251
     *
252
     * @return void
253
     */
254 4
    private function fixSummaryNotFirst()
255
    {
256 4
        $commentStartToken = $this->docHelper->getCommentStartToken();
257 4
        $summaryStartToken = $this->getCommentSummaryToken();
258
259 4
        $startLine = $commentStartToken['line'] + 1;
260 4
        $endLine = $summaryStartToken['line'] - 1;
261
262 4
        $this->file->getFixer()->beginChangeset();
263 4
        $this->file->getFixer()->removeLines($startLine, $endLine);
264 4
        $this->file->getFixer()->endChangeset();
265 4
    }
266
267
    /**
268
     * Fixes the first letter of the summary to be uppercase.
269
     *
270
     * @param array $summaryToken Token array of the summary
271
     * @param int $summaryPtr Pointer to the summary
272
     *
273
     * @return void
274
     */
275 4 View Code Duplication
    private function fixSummaryUcFirst(array $summaryToken, $summaryPtr)
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...
276
    {
277 4
        $this->file->getFixer()->beginChangeset();
278
279 4
        $this->file->getFixer()->replaceToken($summaryPtr, ucfirst($summaryToken['content']));
280
281 4
        $this->file->getFixer()->endChangeset();
282 4
    }
283
}
284