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

PHPCSStandards / PHP_CodeSniffer / 14454424553

14 Apr 2025 07:49PM UTC coverage: 77.579% (+2.3%) from 75.291%
14454424553

push

github

web-flow
Merge pull request #983 from PHPCSStandards/phpcs-4.0/feature/sq-2448-remove-support-js-css

Remove CSS/JS support (HUGE PR, reviews welcome!)

119 of 126 new or added lines in 14 files covered. (94.44%)

26 existing lines in 10 files now uncovered.

19384 of 24986 relevant lines covered (77.58%)

78.47 hits per line

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

98.7
/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
            $error = 'Possible parse error: no opening/closing parenthesis for FOR keyword';
3✔
71
            $phpcsFile->addWarning($error, $stackPtr, 'NoOpenBracket');
3✔
72
            return;
3✔
73
        }
74

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

231
            ++$semicolonCount;
3✔
232

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

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

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

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

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

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

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

303
    }//end process()
1✔
304

305

306
}//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