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

PHPCSStandards / PHP_CodeSniffer / 7211360172

14 Dec 2023 02:17PM UTC coverage: 63.963% (-0.05%) from 64.01%
7211360172

push

github

jrfnl
Improve test coverage for the AssignmentInCondition sniff

This commit improves the test coverage for the AssignmentInCondition
sniff by adding a few more test case files to exercise the parts of the
sniff code that checks for invalid syntax.

15110 of 23623 relevant lines covered (63.96%)

5.38 hits per line

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

88.1
/src/Standards/Generic/Sniffs/ControlStructures/InlineControlStructureSniff.php
1
<?php
2
/**
3
 * Verifies that inline control statements are not present.
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\Generic\Sniffs\ControlStructures;
11

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

16
class InlineControlStructureSniff implements Sniff
17
{
18

19
    /**
20
     * If true, an error will be thrown; otherwise a warning.
21
     *
22
     * @var boolean
23
     */
24
    public $error = true;
25

26

27
    /**
28
     * Returns an array of tokens this test wants to listen for.
29
     *
30
     * @return array
31
     */
32
    public function register()
3✔
33
    {
34
        return [
2✔
35
            T_IF,
3✔
36
            T_ELSE,
3✔
37
            T_ELSEIF,
3✔
38
            T_FOREACH,
3✔
39
            T_WHILE,
3✔
40
            T_DO,
3✔
41
            T_SWITCH,
3✔
42
            T_FOR,
3✔
43
        ];
2✔
44

45
    }//end register()
46

47

48
    /**
49
     * Processes this test, when one of its tokens is encountered.
50
     *
51
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
52
     * @param int                         $stackPtr  The position of the current token in the
53
     *                                               stack passed in $tokens.
54
     *
55
     * @return void|int
56
     */
57
    public function process(File $phpcsFile, $stackPtr)
3✔
58
    {
59
        $tokens = $phpcsFile->getTokens();
3✔
60

61
        if (isset($tokens[$stackPtr]['scope_opener']) === true) {
3✔
62
            $phpcsFile->recordMetric($stackPtr, 'Control structure defined inline', 'no');
3✔
63
            return;
3✔
64
        }
65

66
        // Ignore the ELSE in ELSE IF. We'll process the IF part later.
67
        if ($tokens[$stackPtr]['code'] === T_ELSE) {
3✔
68
            $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
3✔
69
            if ($tokens[$next]['code'] === T_IF) {
3✔
70
                return;
3✔
71
            }
72
        }
73

74
        if ($tokens[$stackPtr]['code'] === T_WHILE || $tokens[$stackPtr]['code'] === T_FOR) {
3✔
75
            // This could be from a DO WHILE, which doesn't have an opening brace or a while/for without body.
76
            if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
3✔
77
                $afterParensCloser = $phpcsFile->findNext(Tokens::$emptyTokens, ($tokens[$stackPtr]['parenthesis_closer'] + 1), null, true);
3✔
78
                if ($afterParensCloser === false) {
3✔
79
                    // Live coding.
80
                    return;
×
81
                }
82

83
                if ($tokens[$afterParensCloser]['code'] === T_SEMICOLON) {
3✔
84
                    $phpcsFile->recordMetric($stackPtr, 'Control structure defined inline', 'no');
3✔
85
                    return;
3✔
86
                }
87
            }
88
        }//end if
89

90
        if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false
3✔
91
            && $tokens[$stackPtr]['code'] !== T_ELSE
3✔
92
        ) {
93
            if ($tokens[$stackPtr]['code'] !== T_DO) {
3✔
94
                // Live coding or parse error.
95
                return;
3✔
96
            }
97

98
            $nextWhile = $phpcsFile->findNext(T_WHILE, ($stackPtr + 1));
3✔
99
            if ($nextWhile !== false
3✔
100
                && isset($tokens[$nextWhile]['parenthesis_opener'], $tokens[$nextWhile]['parenthesis_closer']) === false
3✔
101
            ) {
102
                // Live coding or parse error.
103
                return;
3✔
104
            }
105

106
            unset($nextWhile);
×
107
        }
108

109
        $start = $stackPtr;
3✔
110
        if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
3✔
111
            $start = $tokens[$stackPtr]['parenthesis_closer'];
3✔
112
        }
113

114
        $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($start + 1), null, true);
3✔
115
        if ($nextNonEmpty === false) {
3✔
116
            // Live coding or parse error.
117
            return;
3✔
118
        }
119

120
        if ($tokens[$nextNonEmpty]['code'] === T_OPEN_CURLY_BRACKET
3✔
121
            || $tokens[$nextNonEmpty]['code'] === T_COLON
3✔
122
        ) {
123
            // T_CLOSE_CURLY_BRACKET missing, or alternative control structure with
124
            // T_END... missing. Either live coding, parse error or end
125
            // tag in short open tags and scan run with short_open_tag=Off.
126
            // Bow out completely as any further detection will be unreliable
127
            // and create incorrect fixes or cause fixer conflicts.
128
            return ($phpcsFile->numTokens + 1);
2✔
129
        }
130

131
        unset($nextNonEmpty, $start);
3✔
132

133
        // This is a control structure without an opening brace,
134
        // so it is an inline statement.
135
        if ($this->error === true) {
3✔
136
            $fix = $phpcsFile->addFixableError('Inline control structures are not allowed', $stackPtr, 'NotAllowed');
3✔
137
        } else {
138
            $fix = $phpcsFile->addFixableWarning('Inline control structures are discouraged', $stackPtr, 'Discouraged');
×
139
        }
140

141
        $phpcsFile->recordMetric($stackPtr, 'Control structure defined inline', 'yes');
3✔
142

143
        // Stop here if we are not fixing the error.
144
        if ($fix !== true) {
3✔
145
            return;
3✔
146
        }
147

148
        $phpcsFile->fixer->beginChangeset();
3✔
149
        if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
3✔
150
            $closer = $tokens[$stackPtr]['parenthesis_closer'];
3✔
151
        } else {
152
            $closer = $stackPtr;
3✔
153
        }
154

155
        if ($tokens[($closer + 1)]['code'] === T_WHITESPACE
3✔
156
            || $tokens[($closer + 1)]['code'] === T_SEMICOLON
3✔
157
        ) {
158
            $phpcsFile->fixer->addContent($closer, ' {');
3✔
159
        } else {
160
            $phpcsFile->fixer->addContent($closer, ' { ');
×
161
        }
162

163
        $fixableScopeOpeners = $this->register();
3✔
164

165
        $lastNonEmpty = $closer;
3✔
166
        for ($end = ($closer + 1); $end < $phpcsFile->numTokens; $end++) {
3✔
167
            if ($tokens[$end]['code'] === T_SEMICOLON) {
3✔
168
                break;
3✔
169
            }
170

171
            if ($tokens[$end]['code'] === T_CLOSE_TAG) {
3✔
172
                $end = $lastNonEmpty;
3✔
173
                break;
3✔
174
            }
175

176
            if (in_array($tokens[$end]['code'], $fixableScopeOpeners, true) === true
3✔
177
                && isset($tokens[$end]['scope_opener']) === false
3✔
178
            ) {
179
                // The best way to fix nested inline scopes is middle-out.
180
                // So skip this one. It will be detected and fixed on a future loop.
181
                $phpcsFile->fixer->rollbackChangeset();
3✔
182
                return;
3✔
183
            }
184

185
            if (isset($tokens[$end]['scope_opener']) === true) {
3✔
186
                $type = $tokens[$end]['code'];
3✔
187
                $end  = $tokens[$end]['scope_closer'];
3✔
188
                if ($type === T_DO
3✔
189
                    || $type === T_IF || $type === T_ELSEIF
3✔
190
                    || $type === T_TRY || $type === T_CATCH || $type === T_FINALLY
3✔
191
                ) {
192
                    $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
3✔
193
                    if ($next === false) {
3✔
194
                        break;
×
195
                    }
196

197
                    $nextType = $tokens[$next]['code'];
3✔
198

199
                    // Let additional conditions loop and find their ending.
200
                    if (($type === T_IF
3✔
201
                        || $type === T_ELSEIF)
3✔
202
                        && ($nextType === T_ELSEIF
3✔
203
                        || $nextType === T_ELSE)
3✔
204
                    ) {
205
                        continue;
3✔
206
                    }
207

208
                    // Account for TRY... CATCH/FINALLY statements.
209
                    if (($type === T_TRY
3✔
210
                        || $type === T_CATCH
3✔
211
                        || $type === T_FINALLY)
3✔
212
                        && ($nextType === T_CATCH
3✔
213
                        || $nextType === T_FINALLY)
3✔
214
                    ) {
215
                        continue;
3✔
216
                    }
217

218
                    // Account for DO... WHILE conditions.
219
                    if ($type === T_DO && $nextType === T_WHILE) {
3✔
220
                        $end = $phpcsFile->findNext(T_SEMICOLON, ($next + 1));
3✔
221
                    }
222
                } else if ($type === T_CLOSURE) {
3✔
223
                    // There should be a semicolon after the closing brace.
224
                    $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
3✔
225
                    if ($next !== false && $tokens[$next]['code'] === T_SEMICOLON) {
3✔
226
                        $end = $next;
3✔
227
                    }
228
                }//end if
229

230
                if ($tokens[$end]['code'] !== T_END_HEREDOC
3✔
231
                    && $tokens[$end]['code'] !== T_END_NOWDOC
3✔
232
                ) {
233
                    break;
3✔
234
                }
235
            }//end if
236

237
            if (isset($tokens[$end]['parenthesis_closer']) === true) {
3✔
238
                $end          = $tokens[$end]['parenthesis_closer'];
3✔
239
                $lastNonEmpty = $end;
3✔
240
                continue;
3✔
241
            }
242

243
            if ($tokens[$end]['code'] !== T_WHITESPACE) {
3✔
244
                $lastNonEmpty = $end;
3✔
245
            }
246
        }//end for
247

248
        if ($end === $phpcsFile->numTokens) {
3✔
249
            $end = $lastNonEmpty;
×
250
        }
251

252
        $nextContent = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
3✔
253
        if ($nextContent === false || $tokens[$nextContent]['line'] !== $tokens[$end]['line']) {
3✔
254
            // Looks for completely empty statements.
255
            $next = $phpcsFile->findNext(T_WHITESPACE, ($closer + 1), ($end + 1), true);
3✔
256
        } else {
257
            $next    = ($end + 1);
3✔
258
            $endLine = $end;
3✔
259
        }
260

261
        if ($next !== $end) {
3✔
262
            if ($nextContent === false || $tokens[$nextContent]['line'] !== $tokens[$end]['line']) {
3✔
263
                // Account for a comment on the end of the line.
264
                for ($endLine = $end; $endLine < $phpcsFile->numTokens; $endLine++) {
3✔
265
                    if (isset($tokens[($endLine + 1)]) === false
3✔
266
                        || $tokens[$endLine]['line'] !== $tokens[($endLine + 1)]['line']
3✔
267
                    ) {
268
                        break;
3✔
269
                    }
270
                }
271

272
                if (isset(Tokens::$commentTokens[$tokens[$endLine]['code']]) === false
3✔
273
                    && ($tokens[$endLine]['code'] !== T_WHITESPACE
3✔
274
                    || isset(Tokens::$commentTokens[$tokens[($endLine - 1)]['code']]) === false)
3✔
275
                ) {
276
                    $endLine = $end;
3✔
277
                }
278
            }
279

280
            if ($endLine !== $end) {
3✔
281
                $endToken     = $endLine;
3✔
282
                $addedContent = '';
3✔
283
            } else {
284
                $endToken     = $end;
3✔
285
                $addedContent = $phpcsFile->eolChar;
3✔
286

287
                if ($tokens[$end]['code'] !== T_SEMICOLON
3✔
288
                    && $tokens[$end]['code'] !== T_CLOSE_CURLY_BRACKET
3✔
289
                ) {
290
                    $phpcsFile->fixer->addContent($end, '; ');
3✔
291
                }
292
            }
293

294
            $next = $phpcsFile->findNext(T_WHITESPACE, ($endToken + 1), null, true);
3✔
295
            if ($next !== false
3✔
296
                && ($tokens[$next]['code'] === T_ELSE
3✔
297
                || $tokens[$next]['code'] === T_ELSEIF)
3✔
298
            ) {
299
                $phpcsFile->fixer->addContentBefore($next, '} ');
3✔
300
            } else {
301
                $indent = '';
3✔
302
                for ($first = $stackPtr; $first > 0; $first--) {
3✔
303
                    if ($tokens[$first]['column'] === 1) {
3✔
304
                        break;
3✔
305
                    }
306
                }
307

308
                if ($tokens[$first]['code'] === T_WHITESPACE) {
3✔
309
                    $indent = $tokens[$first]['content'];
3✔
310
                } else if ($tokens[$first]['code'] === T_INLINE_HTML
3✔
311
                    || $tokens[$first]['code'] === T_OPEN_TAG
3✔
312
                ) {
313
                    $addedContent = '';
3✔
314
                }
315

316
                $addedContent .= $indent.'}';
3✔
317
                if ($next !== false && $tokens[$endToken]['code'] === T_COMMENT) {
3✔
318
                    $addedContent .= $phpcsFile->eolChar;
3✔
319
                }
320

321
                $phpcsFile->fixer->addContent($endToken, $addedContent);
3✔
322
            }//end if
323
        } else {
324
            if ($nextContent === false || $tokens[$nextContent]['line'] !== $tokens[$end]['line']) {
×
325
                // Account for a comment on the end of the line.
326
                for ($endLine = $end; $endLine < $phpcsFile->numTokens; $endLine++) {
×
327
                    if (isset($tokens[($endLine + 1)]) === false
×
328
                        || $tokens[$endLine]['line'] !== $tokens[($endLine + 1)]['line']
×
329
                    ) {
330
                        break;
×
331
                    }
332
                }
333

334
                if ($tokens[$endLine]['code'] !== T_COMMENT
×
335
                    && ($tokens[$endLine]['code'] !== T_WHITESPACE
×
336
                    || $tokens[($endLine - 1)]['code'] !== T_COMMENT)
×
337
                ) {
338
                    $endLine = $end;
×
339
                }
340
            }
341

342
            if ($endLine !== $end) {
×
343
                $phpcsFile->fixer->replaceToken($end, '');
×
344
                $phpcsFile->fixer->addNewlineBefore($endLine);
×
345
                $phpcsFile->fixer->addContent($endLine, '}');
×
346
            } else {
347
                $phpcsFile->fixer->replaceToken($end, '}');
×
348
            }
349
        }//end if
350

351
        $phpcsFile->fixer->endChangeset();
3✔
352

353
    }//end process()
1✔
354

355

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