SpaceAfterDeclareSniff   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 2
dl 0
loc 187
ccs 39
cts 39
cp 1
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A register() 0 6 1
B process() 0 47 7
A handleNoWhitespaceFound() 0 14 2
A handleMuchWhitespacesFound() 0 20 3
A handleBlankLineInGroup() 0 20 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BestIt\Sniffs\Formatting;
6
7
use PHP_CodeSniffer\Files\File;
8
use PHP_CodeSniffer\Sniffs\Sniff;
9
10
/**
11
 * Class SpaceAfterDeclareSniff
12
 *
13
 * @author Nick Lubisch <[email protected]>
14
 * @package BestIt\Sniffs\Formatting
15
 */
16
class SpaceAfterDeclareSniff implements Sniff
17
{
18
    /**
19
     * Error message when no whitespace is found.
20
     */
21
    public const MESSAGE_NO_WHITESPACE_FOUND = 'There is no whitespace after declare-statement.';
22
23
    /**
24
     * There MUST be one empty line after declare-statement.
25
     */
26
    public const CODE_NO_WHITESPACE_FOUND = 'NoWhitespaceFound';
27
28
    /**
29
     * Error message when more than one whitespaces are found.
30
     */
31
    public const MESSAGE_MUCH_WHITESPACE_FOUND = 'There are more than one whitespaces after declare-statement.';
32
33
    /**
34
     * THERE MUST be just one single line after the declare statement.
35
     */
36
    public const CODE_MUCH_WHITESPACE_FOUND = 'MuchWhitespaceFound';
37
38
    /**
39
     * Error message when blank lines in a group are found.
40
     */
41
    public const MESSAGE_GROUP_BLANK_LINE_FOUND = 'Multpile declare-statements should be grouped without a blank line.';
42
43
    /**
44
     * Multiple declare-statements SHOULD be grouped without a blank line.
45
     */
46
    public const CODE_GROUP_BLANK_LINE_FOUND = 'GroupBlankLineFound';
47
48
    /**
49
     * Registers the tokens that this sniff wants to listen for.
50
     *
51
     * @return int[] List of tokens to listen for
52 5
     */
53
    public function register(): array
54
    {
55 5
        return [
56
            T_DECLARE
57
        ];
58
    }
59
60
    /**
61
     * Called when one of the token types that this sniff is listening for is found.
62
     *
63
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
64
     *
65
     * @param File $phpcsFile The PHP_CodeSniffer file where the token was found.
66
     * @param int $stackPtr The position in the PHP_CodeSniffer file's token stack where the token was found.
67 4
     *
68
     * @return void Optionally returns a stack pointer.
69 4
     */
70
    public function process(File $phpcsFile, $stackPtr): void
71 4
    {
72
        $tokens = $phpcsFile->getTokens();
73 4
74 4
        $semicolonPtr = $phpcsFile->findEndOfStatement($stackPtr);
75
76 4
        $secondSpacePtr = $semicolonPtr + 2;
77 3
        $secondSpaceToken = $tokens[$secondSpacePtr];
78
79 3
        $nextDeclarePtr = $phpcsFile->findNext(T_DECLARE, $semicolonPtr, null, false);
80
81
        $whiteSpaceInGroupPtr = $phpcsFile->findNext(T_WHITESPACE, $secondSpacePtr, $nextDeclarePtr, false);
0 ignored issues
show
Security Bug introduced by
It seems like $nextDeclarePtr defined by $phpcsFile->findNext(T_D...icolonPtr, null, false) on line 79 can also be of type false; however, PHP_CodeSniffer\Files\File::findNext() does only seem to accept integer|null, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
82 4
83
84 4
        //Declare statement group detected
85 1
        if ($secondSpaceToken['code'] === T_DECLARE) {
86
            return;
87
        }
88 3
89
        //Declare statement group with blank lines detected
90 3
        if ($nextDeclarePtr && $whiteSpaceInGroupPtr) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nextDeclarePtr of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $whiteSpaceInGroupPtr of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
91 1
            $this->handleBlankLineInGroup($phpcsFile, $semicolonPtr, $whiteSpaceInGroupPtr, $nextDeclarePtr);
92
            return;
93 1
        }
94
95 3
        //Single declare statement with no following whitespace detected
96
        if ($secondSpaceToken['code'] !== T_WHITESPACE) {
97
            $this->handleNoWhitespaceFound($phpcsFile, $semicolonPtr);
98
99
            return;
100
        }
101
102
        $nextNonSpacePtr = $phpcsFile->findNext(T_WHITESPACE, $secondSpacePtr, null, true);
103
104
        if ($nextNonSpacePtr === false) {
105 3
            return;
106
        }
107 3
108 3
        $nextNonSpaceToken = $tokens[$nextNonSpacePtr];
109 3
110 3
        //Detect too many whitespaces after declare statement
111
        if (($nextNonSpaceToken['line'] - $secondSpaceToken['line']) > 1) {
112
            $this->handleMuchWhitespacesFound($phpcsFile, $semicolonPtr, $secondSpacePtr, $nextNonSpacePtr);
113 3
114 3
            return;
115 3
        }
116 3
    }
117
118 3
    /**
119
     * Handles when no whitespace is found.
120
     *
121
     * @param File $phpcsFile The php cs file
122
     * @param int $semicolonPtr Pointer to the semicolon token
123
     *
124
     * @return void
125
     */
126
    private function handleNoWhitespaceFound(File $phpcsFile, int $semicolonPtr): void
127
    {
128
        $fixNoWhitespace = $phpcsFile->addFixableError(
129
            self::MESSAGE_NO_WHITESPACE_FOUND,
130 1
            $semicolonPtr,
131
            static::CODE_NO_WHITESPACE_FOUND
132
        );
133
134
        if ($fixNoWhitespace) {
135
            $phpcsFile->fixer->beginChangeset();
136 1
            $phpcsFile->fixer->addNewline($semicolonPtr);
137 1
            $phpcsFile->fixer->endChangeset();
138 1
        }
139 1
    }
140
141
    /**
142 1
     * Handles when more than one whitespaces are found.
143 1
     *
144 1
     * @param File $phpcsFile The php cs file
145 1
     * @param int $semicolonPtr Pointer to the semicolon
146
     * @param int $secondSpacePtr Pointer to the second space
147 1
     * @param int $nextNonSpacePtr Pointer to the next non space token
148
     *
149 1
     * @return void
150
     */
151
    private function handleMuchWhitespacesFound(
152
        File $phpcsFile,
153
        int $semicolonPtr,
154
        int $secondSpacePtr,
155
        int $nextNonSpacePtr
156
    ): void {
157
        $fixMuchWhitespaces = $phpcsFile->addFixableError(
158
            self::MESSAGE_MUCH_WHITESPACE_FOUND,
159
            $semicolonPtr,
160
            static::CODE_MUCH_WHITESPACE_FOUND
161
        );
162
163
        if ($fixMuchWhitespaces) {
164
            $phpcsFile->fixer->beginChangeset();
165
            for ($i = $secondSpacePtr; $i < $nextNonSpacePtr; $i++) {
166
                $phpcsFile->fixer->replaceToken($i, '');
167
            }
168
            $phpcsFile->fixer->endChangeset();
169
        }
170
    }
171
172
    /**
173
     * Handles blank lines found in declare group.
174
     *
175
     * @param File $phpcsFile The php cs file
176
     * @param int $semicolonPtr Pointer to the semicolon
177
     * @param int $secondSpacePtr Pointer to the second space
178
     * @param int $nextNonSpacePtr Pointer to the next non space token
179
     *
180
     * @return void
181
     */
182
    private function handleBlankLineInGroup(
183
        File $phpcsFile,
184
        int $semicolonPtr,
185
        int $secondSpacePtr,
186
        int $nextNonSpacePtr
187
    ): void {
188
        $fixGroupBlankLines = $phpcsFile->addFixableError(
189
            self::MESSAGE_GROUP_BLANK_LINE_FOUND,
190
            $semicolonPtr,
191
            static::CODE_GROUP_BLANK_LINE_FOUND
192
        );
193
194
        if ($fixGroupBlankLines) {
195
            $phpcsFile->fixer->beginChangeset();
196
            for ($i = $secondSpacePtr; $i < $nextNonSpacePtr; $i++) {
197
                $phpcsFile->fixer->replaceToken($i, '');
198
            }
199
            $phpcsFile->fixer->endChangeset();
200
        }
201
    }
202
}
203