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

keradus / PHP-CS-Fixer / 16999983712

15 Aug 2025 09:42PM UTC coverage: 94.75% (-0.09%) from 94.839%
16999983712

push

github

keradus
ci: more self-fixing checks on lowest/highest PHP

28263 of 29829 relevant lines covered (94.75%)

45.88 hits per line

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

61.46
/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
    private const CONTROL_TOKENS = [
28
        \T_DECLARE,
29
        \T_DO,
30
        \T_ELSE,
31
        \T_ELSEIF,
32
        \T_FINALLY,
33
        \T_FOR,
34
        \T_FOREACH,
35
        \T_IF,
36
        \T_WHILE,
37
        \T_TRY,
38
        \T_CATCH,
39
        \T_SWITCH,
40
    ];
41

42
    public function getDefinition(): FixerDefinitionInterface
43
    {
44
        return new FixerDefinition(
3✔
45
            'The body of each control structure MUST be enclosed within braces.',
3✔
46
            [new CodeSample("<?php\nif (foo()) echo 'Hello!';\n")]
3✔
47
        );
3✔
48
    }
49

50
    public function isCandidate(Tokens $tokens): bool
51
    {
52
        return true;
23✔
53
    }
54

55
    /**
56
     * {@inheritdoc}
57
     *
58
     * Must run before BracesPositionFixer, ControlStructureContinuationPositionFixer, CurlyBracesPositionFixer, NoMultipleStatementsPerLineFixer.
59
     */
60
    public function getPriority(): int
61
    {
62
        return 1;
1✔
63
    }
64

65
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
66
    {
67
        $alternativeSyntaxAnalyzer = new AlternativeSyntaxAnalyzer();
23✔
68

69
        for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
23✔
70
            $token = $tokens[$index];
23✔
71

72
            if (!$token->isGivenKind(self::CONTROL_TOKENS)) {
23✔
73
                continue;
23✔
74
            }
75

76
            if (
77
                $token->isGivenKind(\T_ELSE)
23✔
78
                && $tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(\T_IF)
23✔
79
            ) {
80
                continue;
2✔
81
            }
82

83
            $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
23✔
84
            $nextAfterParenthesisEndIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
23✔
85
            $tokenAfterParenthesis = $tokens[$nextAfterParenthesisEndIndex];
23✔
86

87
            if ($tokenAfterParenthesis->equalsAny([';', '{', ':', [\T_CLOSE_TAG]])) {
23✔
88
                continue;
22✔
89
            }
90

91
            $statementEndIndex = null;
14✔
92

93
            if ($tokenAfterParenthesis->isGivenKind([\T_IF, \T_FOR, \T_FOREACH, \T_SWITCH, \T_WHILE])) {
14✔
94
                $tokenAfterParenthesisBlockEnd = $tokens->findBlockEnd( // go to ')'
5✔
95
                    Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
5✔
96
                    $tokens->getNextMeaningfulToken($nextAfterParenthesisEndIndex)
5✔
97
                );
5✔
98

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

102
                    $tokenAfterStatementEndIndex = $tokens->getNextMeaningfulToken($statementEndIndex);
5✔
103
                    if ($tokens[$tokenAfterStatementEndIndex]->equals(';')) {
5✔
104
                        $statementEndIndex = $tokenAfterStatementEndIndex;
5✔
105
                    }
106
                }
107
            }
108

109
            if (null === $statementEndIndex) {
14✔
110
                $statementEndIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
9✔
111
            }
112

113
            $tokensToInsertAfterStatement = [
14✔
114
                new Token([\T_WHITESPACE, ' ']),
14✔
115
                new Token('}'),
14✔
116
            ];
14✔
117

118
            if (!$tokens[$statementEndIndex]->equalsAny([';', '}'])) {
14✔
119
                array_unshift($tokensToInsertAfterStatement, new Token(';'));
×
120
            }
121

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

124
            // insert opening brace
125
            $tokens->insertSlices([$parenthesisEndIndex + 1 => [
14✔
126
                new Token([\T_WHITESPACE, ' ']),
14✔
127
                new Token('{'),
14✔
128
            ]]);
14✔
129
        }
130
    }
131

132
    private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
133
    {
134
        $nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex);
23✔
135
        $nextToken = $tokens[$nextIndex];
23✔
136

137
        if (!$nextToken->equals('(')) {
23✔
138
            return $structureTokenIndex;
4✔
139
        }
140

141
        return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
23✔
142
    }
143

144
    private function findStatementEnd(Tokens $tokens, int $parenthesisEndIndex): int
145
    {
146
        $nextIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
9✔
147
        if (null === $nextIndex) {
9✔
148
            return $parenthesisEndIndex;
×
149
        }
150

151
        $nextToken = $tokens[$nextIndex];
9✔
152

153
        if ($nextToken->equals('{')) {
9✔
154
            return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextIndex);
×
155
        }
156

157
        if ($nextToken->isGivenKind(self::CONTROL_TOKENS)) {
9✔
158
            $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex);
×
159

160
            $endIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
×
161

162
            if ($nextToken->isGivenKind([\T_IF, \T_TRY, \T_DO])) {
×
163
                $openingTokenKind = $nextToken->getId();
×
164

165
                while (true) {
×
166
                    $nextIndex = $tokens->getNextMeaningfulToken($endIndex);
×
167
                    if (null !== $nextIndex && $tokens[$nextIndex]->isGivenKind($this->getControlContinuationTokensForOpeningToken($openingTokenKind))) {
×
168
                        $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex);
×
169

170
                        $endIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
×
171

172
                        if ($tokens[$nextIndex]->isGivenKind($this->getFinalControlContinuationTokensForOpeningToken($openingTokenKind))) {
×
173
                            return $endIndex;
×
174
                        }
175
                    } else {
176
                        break;
×
177
                    }
178
                }
179
            }
180

181
            return $endIndex;
×
182
        }
183

184
        $index = $parenthesisEndIndex;
9✔
185

186
        while (true) {
9✔
187
            $token = $tokens[++$index];
9✔
188

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

193
                continue;
×
194
            }
195

196
            if ($token->equals(';')) {
9✔
197
                return $index;
9✔
198
            }
199

200
            if ($token->isGivenKind(\T_CLOSE_TAG)) {
9✔
201
                return $tokens->getPrevNonWhitespace($index);
×
202
            }
203
        }
204
    }
205

206
    /**
207
     * @return list<int>
208
     */
209
    private function getControlContinuationTokensForOpeningToken(int $openingTokenKind): array
210
    {
211
        if (\T_IF === $openingTokenKind) {
×
212
            return [
×
213
                \T_ELSE,
×
214
                \T_ELSEIF,
×
215
            ];
×
216
        }
217

218
        if (\T_DO === $openingTokenKind) {
×
219
            return [\T_WHILE];
×
220
        }
221

222
        if (\T_TRY === $openingTokenKind) {
×
223
            return [
×
224
                \T_CATCH,
×
225
                \T_FINALLY,
×
226
            ];
×
227
        }
228

229
        return [];
×
230
    }
231

232
    /**
233
     * @return list<int>
234
     */
235
    private function getFinalControlContinuationTokensForOpeningToken(int $openingTokenKind): array
236
    {
237
        if (\T_IF === $openingTokenKind) {
×
238
            return [\T_ELSE];
×
239
        }
240

241
        if (\T_TRY === $openingTokenKind) {
×
242
            return [\T_FINALLY];
×
243
        }
244

245
        return [];
×
246
    }
247
}
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