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

keradus / PHP-CS-Fixer / 17319949156

29 Aug 2025 09:20AM UTC coverage: 94.696% (-0.05%) from 94.744%
17319949156

push

github

keradus
CS

28333 of 29920 relevant lines covered (94.7%)

45.63 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
/**
26
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
27
 */
28
final class ControlStructureBracesFixer extends AbstractFixer
29
{
30
    private const CONTROL_TOKENS = [
31
        \T_DECLARE,
32
        \T_DO,
33
        \T_ELSE,
34
        \T_ELSEIF,
35
        \T_FINALLY,
36
        \T_FOR,
37
        \T_FOREACH,
38
        \T_IF,
39
        \T_WHILE,
40
        \T_TRY,
41
        \T_CATCH,
42
        \T_SWITCH,
43
    ];
44

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

53
    public function isCandidate(Tokens $tokens): bool
54
    {
55
        return true;
23✔
56
    }
57

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

68
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
69
    {
70
        $alternativeSyntaxAnalyzer = new AlternativeSyntaxAnalyzer();
23✔
71

72
        for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
23✔
73
            $token = $tokens[$index];
23✔
74

75
            if (!$token->isGivenKind(self::CONTROL_TOKENS)) {
23✔
76
                continue;
23✔
77
            }
78

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

86
            $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
23✔
87
            $nextAfterParenthesisEndIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
23✔
88
            $tokenAfterParenthesis = $tokens[$nextAfterParenthesisEndIndex];
23✔
89

90
            if ($tokenAfterParenthesis->equalsAny([';', '{', ':', [\T_CLOSE_TAG]])) {
23✔
91
                continue;
22✔
92
            }
93

94
            $statementEndIndex = null;
14✔
95

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

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

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

112
            if (null === $statementEndIndex) {
14✔
113
                $statementEndIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
9✔
114
            }
115

116
            $tokensToInsertAfterStatement = [
14✔
117
                new Token([\T_WHITESPACE, ' ']),
14✔
118
                new Token('}'),
14✔
119
            ];
14✔
120

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

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

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

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

140
        if (!$nextToken->equals('(')) {
23✔
141
            return $structureTokenIndex;
4✔
142
        }
143

144
        return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
23✔
145
    }
146

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

154
        $nextToken = $tokens[$nextIndex];
9✔
155

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

160
        if ($nextToken->isGivenKind(self::CONTROL_TOKENS)) {
9✔
161
            $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex);
×
162

163
            $endIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
×
164

165
            if ($nextToken->isGivenKind([\T_IF, \T_TRY, \T_DO])) {
×
166
                $openingTokenKind = $nextToken->getId();
×
167

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

173
                        $endIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
×
174

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

184
            return $endIndex;
×
185
        }
186

187
        $index = $parenthesisEndIndex;
9✔
188

189
        while (true) {
9✔
190
            $token = $tokens[++$index];
9✔
191

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

196
                continue;
×
197
            }
198

199
            if ($token->equals(';')) {
9✔
200
                return $index;
9✔
201
            }
202

203
            if ($token->isGivenKind(\T_CLOSE_TAG)) {
9✔
204
                return $tokens->getPrevNonWhitespace($index);
×
205
            }
206
        }
207
    }
208

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

221
        if (\T_DO === $openingTokenKind) {
×
222
            return [\T_WHILE];
×
223
        }
224

225
        if (\T_TRY === $openingTokenKind) {
×
226
            return [
×
227
                \T_CATCH,
×
228
                \T_FINALLY,
×
229
            ];
×
230
        }
231

232
        return [];
×
233
    }
234

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

244
        if (\T_TRY === $openingTokenKind) {
×
245
            return [\T_FINALLY];
×
246
        }
247

248
        return [];
×
249
    }
250
}
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