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

keradus / PHP-CS-Fixer / 16253708939

13 Jul 2025 09:37PM UTC coverage: 94.807% (+0.001%) from 94.806%
16253708939

push

github

web-flow
Merge branch 'master' into native_constant_invocation__CS

2259 of 2334 new or added lines in 338 files covered. (96.79%)

7 existing lines in 5 files now uncovered.

28260 of 29808 relevant lines covered (94.81%)

45.39 hits per line

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

66.96
/src/Fixer/ControlStructure/ControlStructureBracesFixer.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of PHP CS Fixer.
7
 *
8
 * (c) Fabien Potencier <fabien@symfony.com>
9
 *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14

15
namespace PhpCsFixer\Fixer\ControlStructure;
16

17
use PhpCsFixer\AbstractFixer;
18
use PhpCsFixer\FixerDefinition\CodeSample;
19
use PhpCsFixer\FixerDefinition\FixerDefinition;
20
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
21
use PhpCsFixer\Tokenizer\Analyzer\AlternativeSyntaxAnalyzer;
22
use PhpCsFixer\Tokenizer\Token;
23
use PhpCsFixer\Tokenizer\Tokens;
24

25
final class ControlStructureBracesFixer extends AbstractFixer
26
{
27
    public function getDefinition(): FixerDefinitionInterface
28
    {
29
        return new FixerDefinition(
3✔
30
            'The body of each control structure MUST be enclosed within braces.',
3✔
31
            [new CodeSample("<?php\nif (foo()) echo 'Hello!';\n")]
3✔
32
        );
3✔
33
    }
34

35
    public function isCandidate(Tokens $tokens): bool
36
    {
37
        return true;
23✔
38
    }
39

40
    /**
41
     * {@inheritdoc}
42
     *
43
     * Must run before BracesPositionFixer, ControlStructureContinuationPositionFixer, CurlyBracesPositionFixer, NoMultipleStatementsPerLineFixer.
44
     */
45
    public function getPriority(): int
46
    {
47
        return 1;
1✔
48
    }
49

50
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
51
    {
52
        $alternativeSyntaxAnalyzer = new AlternativeSyntaxAnalyzer();
23✔
53
        $controlTokens = $this->getControlTokens();
23✔
54

55
        for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
23✔
56
            $token = $tokens[$index];
23✔
57

58
            if (!$token->isGivenKind($controlTokens)) {
23✔
59
                continue;
23✔
60
            }
61

62
            if (
63
                $token->isGivenKind(\T_ELSE)
23✔
64
                && $tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(\T_IF)
23✔
65
            ) {
66
                continue;
2✔
67
            }
68

69
            $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
23✔
70
            $nextAfterParenthesisEndIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
23✔
71
            $tokenAfterParenthesis = $tokens[$nextAfterParenthesisEndIndex];
23✔
72

73
            if ($tokenAfterParenthesis->equalsAny([';', '{', ':', [\T_CLOSE_TAG]])) {
23✔
74
                continue;
22✔
75
            }
76

77
            $statementEndIndex = null;
14✔
78

79
            if ($tokenAfterParenthesis->isGivenKind([\T_IF, \T_FOR, \T_FOREACH, \T_SWITCH, \T_WHILE])) {
14✔
80
                $tokenAfterParenthesisBlockEnd = $tokens->findBlockEnd( // go to ')'
5✔
81
                    Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
5✔
82
                    $tokens->getNextMeaningfulToken($nextAfterParenthesisEndIndex)
5✔
83
                );
5✔
84

85
                if ($tokens[$tokens->getNextMeaningfulToken($tokenAfterParenthesisBlockEnd)]->equals(':')) {
5✔
86
                    $statementEndIndex = $alternativeSyntaxAnalyzer->findAlternativeSyntaxBlockEnd($tokens, $nextAfterParenthesisEndIndex);
5✔
87

88
                    $tokenAfterStatementEndIndex = $tokens->getNextMeaningfulToken($statementEndIndex);
5✔
89
                    if ($tokens[$tokenAfterStatementEndIndex]->equals(';')) {
5✔
90
                        $statementEndIndex = $tokenAfterStatementEndIndex;
5✔
91
                    }
92
                }
93
            }
94

95
            if (null === $statementEndIndex) {
14✔
96
                $statementEndIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
9✔
97
            }
98

99
            $tokensToInsertAfterStatement = [
14✔
100
                new Token([\T_WHITESPACE, ' ']),
14✔
101
                new Token('}'),
14✔
102
            ];
14✔
103

104
            if (!$tokens[$statementEndIndex]->equalsAny([';', '}'])) {
14✔
105
                array_unshift($tokensToInsertAfterStatement, new Token(';'));
×
106
            }
107

108
            $tokens->insertSlices([$statementEndIndex + 1 => $tokensToInsertAfterStatement]);
14✔
109

110
            // insert opening brace
111
            $tokens->insertSlices([$parenthesisEndIndex + 1 => [
14✔
112
                new Token([\T_WHITESPACE, ' ']),
14✔
113
                new Token('{'),
14✔
114
            ]]);
14✔
115
        }
116
    }
117

118
    private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
119
    {
120
        $nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex);
23✔
121
        $nextToken = $tokens[$nextIndex];
23✔
122

123
        if (!$nextToken->equals('(')) {
23✔
124
            return $structureTokenIndex;
4✔
125
        }
126

127
        return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
23✔
128
    }
129

130
    private function findStatementEnd(Tokens $tokens, int $parenthesisEndIndex): int
131
    {
132
        $nextIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
9✔
133
        if (null === $nextIndex) {
9✔
134
            return $parenthesisEndIndex;
×
135
        }
136

137
        $nextToken = $tokens[$nextIndex];
9✔
138

139
        if ($nextToken->equals('{')) {
9✔
140
            return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextIndex);
×
141
        }
142

143
        if ($nextToken->isGivenKind($this->getControlTokens())) {
9✔
144
            $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex);
×
145

146
            $endIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
×
147

NEW
148
            if ($nextToken->isGivenKind([\T_IF, \T_TRY, \T_DO])) {
×
149
                $openingTokenKind = $nextToken->getId();
×
150

151
                while (true) {
×
152
                    $nextIndex = $tokens->getNextMeaningfulToken($endIndex);
×
153
                    if (null !== $nextIndex && $tokens[$nextIndex]->isGivenKind($this->getControlContinuationTokensForOpeningToken($openingTokenKind))) {
×
154
                        $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex);
×
155

156
                        $endIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
×
157

158
                        if ($tokens[$nextIndex]->isGivenKind($this->getFinalControlContinuationTokensForOpeningToken($openingTokenKind))) {
×
159
                            return $endIndex;
×
160
                        }
161
                    } else {
162
                        break;
×
163
                    }
164
                }
165
            }
166

167
            return $endIndex;
×
168
        }
169

170
        $index = $parenthesisEndIndex;
9✔
171

172
        while (true) {
9✔
173
            $token = $tokens[++$index];
9✔
174

175
            // if there is some block in statement (eg lambda function) we need to skip it
176
            if ($token->equals('{')) {
9✔
177
                $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
×
178

179
                continue;
×
180
            }
181

182
            if ($token->equals(';')) {
9✔
183
                return $index;
9✔
184
            }
185

186
            if ($token->isGivenKind(\T_CLOSE_TAG)) {
9✔
187
                return $tokens->getPrevNonWhitespace($index);
×
188
            }
189
        }
190
    }
191

192
    /**
193
     * @return list<int>
194
     */
195
    private function getControlTokens(): array
196
    {
197
        static $tokens = [
23✔
198
            \T_DECLARE,
23✔
199
            \T_DO,
23✔
200
            \T_ELSE,
23✔
201
            \T_ELSEIF,
23✔
202
            \T_FINALLY,
23✔
203
            \T_FOR,
23✔
204
            \T_FOREACH,
23✔
205
            \T_IF,
23✔
206
            \T_WHILE,
23✔
207
            \T_TRY,
23✔
208
            \T_CATCH,
23✔
209
            \T_SWITCH,
23✔
210
        ];
23✔
211

212
        return $tokens;
23✔
213
    }
214

215
    /**
216
     * @return list<int>
217
     */
218
    private function getControlContinuationTokensForOpeningToken(int $openingTokenKind): array
219
    {
NEW
220
        if (\T_IF === $openingTokenKind) {
×
221
            return [
×
NEW
222
                \T_ELSE,
×
NEW
223
                \T_ELSEIF,
×
UNCOV
224
            ];
×
225
        }
226

NEW
227
        if (\T_DO === $openingTokenKind) {
×
NEW
228
            return [\T_WHILE];
×
229
        }
230

NEW
231
        if (\T_TRY === $openingTokenKind) {
×
232
            return [
×
NEW
233
                \T_CATCH,
×
NEW
234
                \T_FINALLY,
×
UNCOV
235
            ];
×
236
        }
237

238
        return [];
×
239
    }
240

241
    /**
242
     * @return list<int>
243
     */
244
    private function getFinalControlContinuationTokensForOpeningToken(int $openingTokenKind): array
245
    {
NEW
246
        if (\T_IF === $openingTokenKind) {
×
NEW
247
            return [\T_ELSE];
×
248
        }
249

NEW
250
        if (\T_TRY === $openingTokenKind) {
×
NEW
251
            return [\T_FINALLY];
×
252
        }
253

254
        return [];
×
255
    }
256
}
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

© 2026 Coveralls, Inc