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

PHP-CS-Fixer / PHP-CS-Fixer / 3721300657

pending completion
3721300657

push

github

GitHub
minor: Follow PSR12 ordered imports in Symfony ruleset (#6712)

9 of 9 new or added lines in 2 files covered. (100.0%)

22674 of 24281 relevant lines covered (93.38%)

39.08 hits per line

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

66.37
/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
    /**
28
     * {@inheritdoc}
29
     */
30
    public function getDefinition(): FixerDefinitionInterface
31
    {
32
        return new FixerDefinition(
3✔
33
            'The body of each control structure MUST be enclosed within braces.',
3✔
34
            [new CodeSample("<?php\nif (foo()) echo 'Hello!';\n")]
3✔
35
        );
3✔
36
    }
37

38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function isCandidate(Tokens $tokens): bool
42
    {
43
        return true;
22✔
44
    }
45

46
    /**
47
     * {@inheritdoc}
48
     *
49
     * Must run before ControlStructureContinuationPositionFixer, CurlyBracesPositionFixer, NoMultipleStatementsPerLineFixer.
50
     */
51
    public function getPriority(): int
52
    {
53
        return 1;
1✔
54
    }
55

56
    /**
57
     * {@inheritdoc}
58
     */
59
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
60
    {
61
        $alternativeSyntaxAnalyzer = new AlternativeSyntaxAnalyzer();
22✔
62
        $controlTokens = $this->getControlTokens();
22✔
63

64
        for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
22✔
65
            $token = $tokens[$index];
22✔
66

67
            if (!$token->isGivenKind($controlTokens)) {
22✔
68
                continue;
22✔
69
            }
70

71
            if (
72
                $token->isGivenKind(T_ELSE)
22✔
73
                && $tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(T_IF)
22✔
74
            ) {
75
                continue;
2✔
76
            }
77

78
            $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
22✔
79
            $nextAfterParenthesisEndIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
22✔
80
            $tokenAfterParenthesis = $tokens[$nextAfterParenthesisEndIndex];
22✔
81

82
            if ($tokenAfterParenthesis->equalsAny([';', '{', ':'])) {
22✔
83
                continue;
21✔
84
            }
85

86
            $statementEndIndex = null;
14✔
87

88
            if ($tokenAfterParenthesis->isGivenKind([T_IF, T_FOR, T_FOREACH, T_SWITCH, T_WHILE])) {
14✔
89
                $tokenAfterParenthesisBlockEnd = $tokens->findBlockEnd( // go to ')'
5✔
90
                    Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
5✔
91
                    $tokens->getNextMeaningfulToken($nextAfterParenthesisEndIndex)
5✔
92
                );
5✔
93

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

97
                    $tokenAfterStatementEndIndex = $tokens->getNextMeaningfulToken($statementEndIndex);
5✔
98
                    if ($tokens[$tokenAfterStatementEndIndex]->equals(';')) {
5✔
99
                        $statementEndIndex = $tokenAfterStatementEndIndex;
5✔
100
                    }
101
                }
102
            }
103

104
            if (null === $statementEndIndex) {
14✔
105
                $statementEndIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
9✔
106
            }
107

108
            $tokensToInsertAfterStatement = [
14✔
109
                new Token([T_WHITESPACE, ' ']),
14✔
110
                new Token('}'),
14✔
111
            ];
14✔
112

113
            if (!$tokens[$statementEndIndex]->equalsAny([';', '}'])) {
14✔
114
                array_unshift($tokensToInsertAfterStatement, new Token(';'));
×
115
            }
116

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

119
            // insert opening brace
120
            $tokens->insertSlices([$parenthesisEndIndex + 1 => [
14✔
121
                new Token([T_WHITESPACE, ' ']),
14✔
122
                new Token('{'),
14✔
123
            ]]);
14✔
124
        }
125
    }
126

127
    private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
128
    {
129
        $nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex);
22✔
130
        $nextToken = $tokens[$nextIndex];
22✔
131

132
        if (!$nextToken->equals('(')) {
22✔
133
            return $structureTokenIndex;
4✔
134
        }
135

136
        return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
22✔
137
    }
138

139
    private function findStatementEnd(Tokens $tokens, int $parenthesisEndIndex): int
140
    {
141
        $nextIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
9✔
142
        $nextToken = $tokens[$nextIndex];
9✔
143

144
        if (!$nextToken) {
9✔
145
            return $parenthesisEndIndex;
×
146
        }
147

148
        if ($nextToken->equals('{')) {
9✔
149
            return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextIndex);
×
150
        }
151

152
        if ($nextToken->isGivenKind($this->getControlTokens())) {
9✔
153
            $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex);
×
154

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

157
            if ($nextToken->isGivenKind([T_IF, T_TRY, T_DO])) {
×
158
                $openingTokenKind = $nextToken->getId();
×
159

160
                while (true) {
×
161
                    $nextIndex = $tokens->getNextMeaningfulToken($endIndex);
×
162
                    $nextToken = isset($nextIndex) ? $tokens[$nextIndex] : null;
×
163
                    if ($nextToken && $nextToken->isGivenKind($this->getControlContinuationTokensForOpeningToken($openingTokenKind))) {
×
164
                        $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex);
×
165

166
                        $endIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
×
167

168
                        if ($nextToken->isGivenKind($this->getFinalControlContinuationTokensForOpeningToken($openingTokenKind))) {
×
169
                            return $endIndex;
×
170
                        }
171
                    } else {
172
                        break;
×
173
                    }
174
                }
175
            }
176

177
            return $endIndex;
×
178
        }
179

180
        $index = $parenthesisEndIndex;
9✔
181

182
        while (true) {
9✔
183
            $token = $tokens[++$index];
9✔
184

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

189
                continue;
×
190
            }
191

192
            if ($token->equals(';')) {
9✔
193
                return $index;
9✔
194
            }
195

196
            if ($token->isGivenKind(T_CLOSE_TAG)) {
9✔
197
                return $tokens->getPrevNonWhitespace($index);
×
198
            }
199
        }
200
    }
201

202
    /**
203
     * @return list<int>
204
     */
205
    private function getControlTokens(): array
206
    {
207
        static $tokens = [
22✔
208
            T_DECLARE,
22✔
209
            T_DO,
22✔
210
            T_ELSE,
22✔
211
            T_ELSEIF,
22✔
212
            T_FINALLY,
22✔
213
            T_FOR,
22✔
214
            T_FOREACH,
22✔
215
            T_IF,
22✔
216
            T_WHILE,
22✔
217
            T_TRY,
22✔
218
            T_CATCH,
22✔
219
            T_SWITCH,
22✔
220
        ];
22✔
221

222
        return $tokens;
22✔
223
    }
224

225
    /**
226
     * @return list<int>
227
     */
228
    private function getControlContinuationTokensForOpeningToken(int $openingTokenKind): array
229
    {
230
        if (T_IF === $openingTokenKind) {
×
231
            return [
×
232
                T_ELSE,
×
233
                T_ELSEIF,
×
234
            ];
×
235
        }
236

237
        if (T_DO === $openingTokenKind) {
×
238
            return [T_WHILE];
×
239
        }
240

241
        if (T_TRY === $openingTokenKind) {
×
242
            return [
×
243
                T_CATCH,
×
244
                T_FINALLY,
×
245
            ];
×
246
        }
247

248
        return [];
×
249
    }
250

251
    /**
252
     * @return list<int>
253
     */
254
    private function getFinalControlContinuationTokensForOpeningToken(int $openingTokenKind): array
255
    {
256
        if (T_IF === $openingTokenKind) {
×
257
            return [T_ELSE];
×
258
        }
259

260
        if (T_TRY === $openingTokenKind) {
×
261
            return [T_FINALLY];
×
262
        }
263

264
        return [];
×
265
    }
266
}
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