• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPCSStandards / PHP_CodeSniffer / 15253296250

26 May 2025 11:55AM UTC coverage: 78.632% (+0.3%) from 78.375%
15253296250

Pull #1105

github

web-flow
Merge d9441d98f into caf806050
Pull Request #1105: Skip tests when 'git' command is not available

19665 of 25009 relevant lines covered (78.63%)

88.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.68
/src/Standards/Squiz/Sniffs/ControlStructures/ForLoopDeclarationSniff.php
1
<?php
2
/**
3
 * Verifies that there is a space between each condition of for loops.
4
 *
5
 * @author    Greg Sherwood <gsherwood@squiz.net>
6
 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
7
 * @license   https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8
 */
9

10
namespace PHP_CodeSniffer\Standards\Squiz\Sniffs\ControlStructures;
11

12
use PHP_CodeSniffer\Files\File;
13
use PHP_CodeSniffer\Sniffs\Sniff;
14
use PHP_CodeSniffer\Util\Tokens;
15

16
class ForLoopDeclarationSniff implements Sniff
17
{
18

19
    /**
20
     * How many spaces should follow the opening bracket.
21
     *
22
     * @var integer
23
     */
24
    public $requiredSpacesAfterOpen = 0;
25

26
    /**
27
     * How many spaces should precede the closing bracket.
28
     *
29
     * @var integer
30
     */
31
    public $requiredSpacesBeforeClose = 0;
32

33
    /**
34
     * Allow newlines instead of spaces.
35
     *
36
     * @var boolean
37
     */
38
    public $ignoreNewlines = false;
39

40

41
    /**
42
     * Returns an array of tokens this test wants to listen for.
43
     *
44
     * @return array<int|string>
45
     */
46
    public function register()
3✔
47
    {
48
        return [T_FOR];
3✔
49

50
    }//end register()
51

52

53
    /**
54
     * Processes this test, when one of its tokens is encountered.
55
     *
56
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
57
     * @param int                         $stackPtr  The position of the current token in the
58
     *                                               stack passed in $tokens.
59
     *
60
     * @return void
61
     */
62
    public function process(File $phpcsFile, $stackPtr)
3✔
63
    {
64
        $this->requiredSpacesAfterOpen   = (int) $this->requiredSpacesAfterOpen;
3✔
65
        $this->requiredSpacesBeforeClose = (int) $this->requiredSpacesBeforeClose;
3✔
66
        $tokens = $phpcsFile->getTokens();
3✔
67

68
        $openingBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr);
3✔
69
        if ($openingBracket === false || isset($tokens[$openingBracket]['parenthesis_closer']) === false) {
3✔
70
            // Parse error or live coding.
71
            return;
3✔
72
        }
73

74
        $closingBracket = $tokens[$openingBracket]['parenthesis_closer'];
3✔
75

76
        if ($this->requiredSpacesAfterOpen === 0
3✔
77
            && $tokens[($openingBracket + 1)]['code'] === T_WHITESPACE
3✔
78
        ) {
79
            $nextNonWhiteSpace = $phpcsFile->findNext(T_WHITESPACE, ($openingBracket + 1), $closingBracket, true);
3✔
80
            if ($this->ignoreNewlines === false
3✔
81
                || $tokens[$nextNonWhiteSpace]['line'] === $tokens[$openingBracket]['line']
3✔
82
            ) {
83
                $error = 'Whitespace found after opening bracket of FOR loop';
3✔
84
                $fix   = $phpcsFile->addFixableError($error, $openingBracket, 'SpacingAfterOpen');
3✔
85
                if ($fix === true) {
3✔
86
                    $phpcsFile->fixer->beginChangeset();
3✔
87
                    for ($i = ($openingBracket + 1); $i < $closingBracket; $i++) {
3✔
88
                        if ($tokens[$i]['code'] !== T_WHITESPACE) {
3✔
89
                            break;
3✔
90
                        }
91

92
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
93
                    }
94

95
                    $phpcsFile->fixer->endChangeset();
3✔
96
                }
97
            }
98
        } else if ($this->requiredSpacesAfterOpen > 0) {
3✔
99
            $nextNonWhiteSpace = $phpcsFile->findNext(T_WHITESPACE, ($openingBracket + 1), $closingBracket, true);
3✔
100
            $spaceAfterOpen    = 0;
3✔
101
            if ($tokens[$openingBracket]['line'] !== $tokens[$nextNonWhiteSpace]['line']) {
3✔
102
                $spaceAfterOpen = 'newline';
3✔
103
            } else if ($tokens[($openingBracket + 1)]['code'] === T_WHITESPACE) {
3✔
104
                $spaceAfterOpen = $tokens[($openingBracket + 1)]['length'];
3✔
105
            }
106

107
            if ($spaceAfterOpen !== $this->requiredSpacesAfterOpen
3✔
108
                && ($this->ignoreNewlines === false
3✔
109
                || $spaceAfterOpen !== 'newline')
3✔
110
            ) {
111
                $error = 'Expected %s spaces after opening bracket; %s found';
3✔
112
                $data  = [
2✔
113
                    $this->requiredSpacesAfterOpen,
3✔
114
                    $spaceAfterOpen,
3✔
115
                ];
2✔
116
                $fix   = $phpcsFile->addFixableError($error, $openingBracket, 'SpacingAfterOpen', $data);
3✔
117
                if ($fix === true) {
3✔
118
                    $padding = str_repeat(' ', $this->requiredSpacesAfterOpen);
3✔
119
                    if ($spaceAfterOpen === 0) {
3✔
120
                        $phpcsFile->fixer->addContent($openingBracket, $padding);
3✔
121
                    } else {
122
                        $phpcsFile->fixer->beginChangeset();
3✔
123
                        $phpcsFile->fixer->replaceToken(($openingBracket + 1), $padding);
3✔
124
                        for ($i = ($openingBracket + 2); $i < $nextNonWhiteSpace; $i++) {
3✔
125
                            $phpcsFile->fixer->replaceToken($i, '');
3✔
126
                        }
127

128
                        $phpcsFile->fixer->endChangeset();
3✔
129
                    }
130
                }
131
            }//end if
132
        }//end if
133

134
        $prevNonWhiteSpace  = $phpcsFile->findPrevious(T_WHITESPACE, ($closingBracket - 1), $openingBracket, true);
3✔
135
        $beforeClosefixable = true;
3✔
136
        if ($tokens[$prevNonWhiteSpace]['line'] !== $tokens[$closingBracket]['line']
3✔
137
            && isset(Tokens::EMPTY_TOKENS[$tokens[$prevNonWhiteSpace]['code']]) === true
3✔
138
        ) {
139
            $beforeClosefixable = false;
3✔
140
        }
141

142
        if ($this->requiredSpacesBeforeClose === 0
3✔
143
            && $tokens[($closingBracket - 1)]['code'] === T_WHITESPACE
3✔
144
            && ($this->ignoreNewlines === false
3✔
145
            || $tokens[$prevNonWhiteSpace]['line'] === $tokens[$closingBracket]['line'])
3✔
146
        ) {
147
            $error = 'Whitespace found before closing bracket of FOR loop';
3✔
148

149
            if ($beforeClosefixable === false) {
3✔
150
                $phpcsFile->addError($error, $closingBracket, 'SpacingBeforeClose');
3✔
151
            } else {
152
                $fix = $phpcsFile->addFixableError($error, $closingBracket, 'SpacingBeforeClose');
3✔
153
                if ($fix === true) {
3✔
154
                    $phpcsFile->fixer->beginChangeset();
3✔
155
                    for ($i = ($closingBracket - 1); $i > $openingBracket; $i--) {
3✔
156
                        if ($tokens[$i]['code'] !== T_WHITESPACE) {
3✔
157
                            break;
3✔
158
                        }
159

160
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
161
                    }
162

163
                    $phpcsFile->fixer->endChangeset();
3✔
164
                }
165
            }
166
        } else if ($this->requiredSpacesBeforeClose > 0) {
3✔
167
            $spaceBeforeClose = 0;
3✔
168
            if ($tokens[$closingBracket]['line'] !== $tokens[$prevNonWhiteSpace]['line']) {
3✔
169
                $spaceBeforeClose = 'newline';
3✔
170
            } else if ($tokens[($closingBracket - 1)]['code'] === T_WHITESPACE) {
3✔
171
                $spaceBeforeClose = $tokens[($closingBracket - 1)]['length'];
3✔
172
            }
173

174
            if ($this->requiredSpacesBeforeClose !== $spaceBeforeClose
3✔
175
                && ($this->ignoreNewlines === false
3✔
176
                || $spaceBeforeClose !== 'newline')
3✔
177
            ) {
178
                $error = 'Expected %s spaces before closing bracket; %s found';
3✔
179
                $data  = [
2✔
180
                    $this->requiredSpacesBeforeClose,
3✔
181
                    $spaceBeforeClose,
3✔
182
                ];
2✔
183

184
                if ($beforeClosefixable === false) {
3✔
185
                    $phpcsFile->addError($error, $closingBracket, 'SpacingBeforeClose', $data);
×
186
                } else {
187
                    $fix = $phpcsFile->addFixableError($error, $closingBracket, 'SpacingBeforeClose', $data);
3✔
188
                    if ($fix === true) {
3✔
189
                        $padding = str_repeat(' ', $this->requiredSpacesBeforeClose);
3✔
190
                        if ($spaceBeforeClose === 0) {
3✔
191
                            $phpcsFile->fixer->addContentBefore($closingBracket, $padding);
3✔
192
                        } else {
193
                            $phpcsFile->fixer->beginChangeset();
3✔
194
                            $phpcsFile->fixer->replaceToken(($closingBracket - 1), $padding);
3✔
195
                            for ($i = ($closingBracket - 2); $i > $prevNonWhiteSpace; $i--) {
3✔
196
                                $phpcsFile->fixer->replaceToken($i, '');
3✔
197
                            }
198

199
                            $phpcsFile->fixer->endChangeset();
3✔
200
                        }
201
                    }
202
                }
203
            }//end if
204
        }//end if
205

206
        /*
207
         * Check whitespace around each of the semicolon tokens.
208
         */
209

210
        $semicolonCount     = 0;
3✔
211
        $semicolon          = $openingBracket;
3✔
212
        $targetNestinglevel = 0;
3✔
213
        if (isset($tokens[$openingBracket]['conditions']) === true) {
3✔
214
            $targetNestinglevel = count($tokens[$openingBracket]['conditions']);
3✔
215
        }
216

217
        do {
218
            $semicolon = $phpcsFile->findNext(T_SEMICOLON, ($semicolon + 1), $closingBracket);
3✔
219
            if ($semicolon === false) {
3✔
220
                break;
×
221
            }
222

223
            if (isset($tokens[$semicolon]['conditions']) === true
3✔
224
                && count($tokens[$semicolon]['conditions']) > $targetNestinglevel
3✔
225
            ) {
226
                // Semicolon doesn't belong to the for().
227
                continue;
3✔
228
            }
229

230
            ++$semicolonCount;
3✔
231

232
            $humanReadableCount = 'first';
3✔
233
            if ($semicolonCount !== 1) {
3✔
234
                $humanReadableCount = 'second';
3✔
235
            }
236

237
            $humanReadableCode = ucfirst($humanReadableCount);
3✔
238
            $data = [$humanReadableCount];
3✔
239

240
            // Only examine the space before the first semicolon if the first expression is not empty.
241
            // If it *is* empty, leave it up to the `SpacingAfterOpen` logic.
242
            $prevNonWhiteSpace = $phpcsFile->findPrevious(T_WHITESPACE, ($semicolon - 1), $openingBracket, true);
3✔
243
            if ($semicolonCount !== 1 || $prevNonWhiteSpace !== $openingBracket) {
3✔
244
                if ($tokens[($semicolon - 1)]['code'] === T_WHITESPACE) {
3✔
245
                    $error     = 'Whitespace found before %s semicolon of FOR loop';
3✔
246
                    $errorCode = 'SpacingBefore'.$humanReadableCode;
3✔
247
                    $fix       = $phpcsFile->addFixableError($error, $semicolon, $errorCode, $data);
3✔
248
                    if ($fix === true) {
3✔
249
                        $phpcsFile->fixer->beginChangeset();
3✔
250
                        for ($i = ($semicolon - 1); $i > $prevNonWhiteSpace; $i--) {
3✔
251
                            $phpcsFile->fixer->replaceToken($i, '');
3✔
252
                        }
253

254
                        $phpcsFile->fixer->endChangeset();
3✔
255
                    }
256
                }
257
            }
258

259
            // Only examine the space after the second semicolon if the last expression is not empty.
260
            // If it *is* empty, leave it up to the `SpacingBeforeClose` logic.
261
            $nextNonWhiteSpace = $phpcsFile->findNext(T_WHITESPACE, ($semicolon + 1), ($closingBracket + 1), true);
3✔
262
            if ($semicolonCount !== 2 || $nextNonWhiteSpace !== $closingBracket) {
3✔
263
                if ($tokens[($semicolon + 1)]['code'] !== T_WHITESPACE
3✔
264
                    && $tokens[($semicolon + 1)]['code'] !== T_SEMICOLON
3✔
265
                ) {
266
                    $error     = 'Expected 1 space after %s semicolon of FOR loop; 0 found';
3✔
267
                    $errorCode = 'NoSpaceAfter'.$humanReadableCode;
3✔
268
                    $fix       = $phpcsFile->addFixableError($error, $semicolon, $errorCode, $data);
3✔
269
                    if ($fix === true) {
3✔
270
                        $phpcsFile->fixer->addContent($semicolon, ' ');
3✔
271
                    }
272
                } else if ($tokens[($semicolon + 1)]['code'] === T_WHITESPACE
3✔
273
                    && $tokens[$nextNonWhiteSpace]['code'] !== T_SEMICOLON
3✔
274
                ) {
275
                    $spaces = $tokens[($semicolon + 1)]['length'];
3✔
276
                    if ($tokens[$semicolon]['line'] !== $tokens[$nextNonWhiteSpace]['line']) {
3✔
277
                        $spaces = 'newline';
3✔
278
                    }
279

280
                    if ($spaces !== 1
3✔
281
                        && ($this->ignoreNewlines === false
3✔
282
                        || $spaces !== 'newline')
3✔
283
                    ) {
284
                        $error     = 'Expected 1 space after %s semicolon of FOR loop; %s found';
3✔
285
                        $errorCode = 'SpacingAfter'.$humanReadableCode;
3✔
286
                        $data[]    = $spaces;
3✔
287
                        $fix       = $phpcsFile->addFixableError($error, $semicolon, $errorCode, $data);
3✔
288
                        if ($fix === true) {
3✔
289
                            $phpcsFile->fixer->beginChangeset();
3✔
290
                            $phpcsFile->fixer->replaceToken(($semicolon + 1), ' ');
3✔
291
                            for ($i = ($semicolon + 2); $i < $nextNonWhiteSpace; $i++) {
3✔
292
                                $phpcsFile->fixer->replaceToken($i, '');
3✔
293
                            }
294

295
                            $phpcsFile->fixer->endChangeset();
3✔
296
                        }
297
                    }
298
                }//end if
299
            }//end if
300
        } while ($semicolonCount < 2);
3✔
301

302
    }//end process()
1✔
303

304

305
}//end class
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc