Completed
Pull Request — master (#57)
by
unknown
29:06 queued 11:37
created

EmptyLinesDocSniff::processToken()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BestIt\Sniffs\Commenting;
6
7
use BestIt\CodeSniffer\Helper\LineHelper;
8
use BestIt\Sniffs\AbstractSniff;
9
use PHP_CodeSniffer\Files\File;
10
11
/**
12
 * Class EmptyLinesDocSniff.
13
 *
14
 * @author Mika Bertels <[email protected]>
15
 * @package BestIt\Sniffs\Commenting
16
 */
17
class EmptyLinesDocSniff extends AbstractSniff
18
{
19
    /**
20
     * Code when an empty line is detected.
21
     *
22
     * @var string CODE_EMPTY_LINES_FOUND
23
     */
24
    public const CODE_EMPTY_LINES_FOUND = 'EmptyLinesFound';
25
26
    /**
27
     * Error message when empty line is detected.
28
     *
29
     * @var string ERROR_EMPTY_LINES_FOUND
30
     */
31
    private const ERROR_EMPTY_LINES_FOUND = 'There are too many empty lines in your doc-block!';
32
33
    /**
34
     * Process token within scope.
35
     *
36
     * @return void
37
     */
38
    protected function processToken(): void
39
    {
40
        $this->searchEmptyLines($this->file, $this->stackPos);
0 ignored issues
show
Bug introduced by
It seems like $this->file can be null; however, searchEmptyLines() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
41
    }
42
43
    /**
44
     * Registers the tokens that this sniff wants to listen for.
45
     *
46
     * An example return value for a sniff that wants to listen for whitespace
47
     * and any comments would be:
48
     *
49
     * <code>
50
     *    return array(
51
     *            T_WHITESPACE,
52
     *            T_DOC_COMMENT,
53
     *            T_COMMENT,
54
     *           );
55
     * </code>
56
     *
57
     * @return int[]
58
     * @see    Tokens.php
59
     */
60
    public function register(): array
61
    {
62
        return [T_DOC_COMMENT_OPEN_TAG];
63
    }
64
65
    /**
66
     * Remove unnecessary lines from doc block.
67
     *
68
     * @param File $phpcsFile
69
     * @param array $nextToken
70
     * @param array $currentToken
71
     *
72
     * @return void
73
     */
74
    private function removeUnnecessaryLines(File $phpcsFile, array $nextToken, array $currentToken): void
75
    {
76
        $movement = 2;
77
78
        if ($nextToken['code'] === T_DOC_COMMENT_CLOSE_TAG) {
79
            $movement = 1;
80
        }
81
82
        $phpcsFile->fixer->beginChangeset();
83
84
        (new LineHelper($this->file))->removeLines(
0 ignored issues
show
Bug introduced by
It seems like $this->file can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
85
            $currentToken['line'] + $movement,
86
            $nextToken['line'] - 1
87
        );
88
89
        $phpcsFile->fixer->endChangeset();
90
    }
91
92
    /**
93
     * Process method for tokens within scope and also outside scope.
94
     *
95
     * @param File $phpcsFile The sniffed file.
96
     * @param int $searchPosition
97
     *
98
     * @return void
99
     */
100
    private function searchEmptyLines(File $phpcsFile, int $searchPosition): void
101
    {
102
        $endOfDoc = $phpcsFile->findEndOfStatement($searchPosition);
103
104
        do {
105
            $currentToken = $phpcsFile->getTokens()[$searchPosition];
106
            $nextTokenPosition = (int) $phpcsFile->findNext(
107
                [T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR],
108
                $searchPosition + 1,
109
                $endOfDoc,
110
                true
111
            );
112
113
            if ($hasToken = ($nextTokenPosition > 0)) {
114
                $nextToken = $phpcsFile->getTokens()[$nextTokenPosition];
115
                $hasTooManyLines = ($nextToken['line'] - $currentToken['line']) > 2;
116
117
                if ($hasTooManyLines) {
118
                    $isFixing = $phpcsFile->addFixableError(
119
                        self::ERROR_EMPTY_LINES_FOUND,
120
                        $nextTokenPosition,
121
                        self::CODE_EMPTY_LINES_FOUND
122
                    );
123
124
                    if ($isFixing) {
125
                        $this->removeUnnecessaryLines($phpcsFile, $nextToken, $currentToken);
126
                    }
127
                }
128
129
                $phpcsFile->recordMetric(
130
                    $searchPosition,
131
                    'DocBlock has too many lines',
132
                    $hasTooManyLines ? 'yes' : 'no'
133
                );
134
135
                $searchPosition = $nextTokenPosition;
136
137
            }
138
        } while ($hasToken);
139
    }
140
}
141