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

keradus / PHP-CS-Fixer / 18051010410

26 Sep 2025 10:40PM UTC coverage: 94.308% (-0.02%) from 94.331%
18051010410

push

github

web-flow
chore: use accidentally missing `@auto:risky` (#9102)

28583 of 30308 relevant lines covered (94.31%)

45.24 hits per line

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

97.73
/src/Fixer/ControlStructure/NoAlternativeSyntaxFixer.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\Fixer\ConfigurableFixerInterface;
19
use PhpCsFixer\Fixer\ConfigurableFixerTrait;
20
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
21
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
22
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
23
use PhpCsFixer\FixerDefinition\CodeSample;
24
use PhpCsFixer\FixerDefinition\FixerDefinition;
25
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
26
use PhpCsFixer\Future;
27
use PhpCsFixer\Tokenizer\Token;
28
use PhpCsFixer\Tokenizer\Tokens;
29

30
/**
31
 * @phpstan-type _AutogeneratedInputConfiguration array{
32
 *  fix_non_monolithic_code?: bool,
33
 * }
34
 * @phpstan-type _AutogeneratedComputedConfiguration array{
35
 *  fix_non_monolithic_code: bool,
36
 * }
37
 *
38
 * @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
39
 *
40
 * @author Eddilbert Macharia <edd.cowan@gmail.com>
41
 *
42
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
43
 */
44
final class NoAlternativeSyntaxFixer extends AbstractFixer implements ConfigurableFixerInterface
45
{
46
    /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
47
    use ConfigurableFixerTrait;
48

49
    public function getDefinition(): FixerDefinitionInterface
50
    {
51
        return new FixerDefinition(
3✔
52
            'Replace control structure alternative syntax to use braces.',
3✔
53
            [
3✔
54
                new CodeSample(
3✔
55
                    "<?php\nif(true):echo 't';else:echo 'f';endif;\n"
3✔
56
                ),
3✔
57
                new CodeSample(
3✔
58
                    "<?php if (\$condition): ?>\nLorem ipsum.\n<?php endif; ?>\n",
3✔
59
                    ['fix_non_monolithic_code' => true]
3✔
60
                ),
3✔
61
            ]
3✔
62
        );
3✔
63
    }
64

65
    public function isCandidate(Tokens $tokens): bool
66
    {
67
        return $tokens->hasAlternativeSyntax() && (true === $this->configuration['fix_non_monolithic_code'] || $tokens->isMonolithicPhp());
20✔
68
    }
69

70
    /**
71
     * {@inheritdoc}
72
     *
73
     * Must run before BracesFixer, ElseifFixer, NoSuperfluousElseifFixer, NoUnneededControlParenthesesFixer, NoUselessElseFixer, SwitchContinueToBreakFixer.
74
     */
75
    public function getPriority(): int
76
    {
77
        return 42;
1✔
78
    }
79

80
    protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
81
    {
82
        return new FixerConfigurationResolver([
29✔
83
            (new FixerOptionBuilder('fix_non_monolithic_code', 'Whether to also fix code with inline HTML.'))
29✔
84
                ->setAllowedTypes(['bool'])
29✔
85
                ->setDefault(Future::getV4OrV3(false, true))
29✔
86
                ->getOption(),
29✔
87
        ]);
29✔
88
    }
89

90
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
91
    {
92
        for ($index = \count($tokens) - 1; 0 <= $index; --$index) {
16✔
93
            $token = $tokens[$index];
16✔
94
            $this->fixElseif($index, $token, $tokens);
16✔
95
            $this->fixElse($index, $token, $tokens);
16✔
96
            $this->fixOpenCloseControls($index, $token, $tokens);
16✔
97
        }
98
    }
99

100
    private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
101
    {
102
        $nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex);
1✔
103
        $nextToken = $tokens[$nextIndex];
1✔
104

105
        return $nextToken->equals('(')
1✔
106
            ? $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex)
1✔
107
            : $structureTokenIndex; // return if next token is not opening parenthesis
1✔
108
    }
109

110
    /**
111
     * Handle both extremes of the control structures.
112
     * e.g. if(): or endif;.
113
     *
114
     * @param int    $index  the index of the token being processed
115
     * @param Token  $token  the token being processed
116
     * @param Tokens $tokens the collection of tokens
117
     */
118
    private function fixOpenCloseControls(int $index, Token $token, Tokens $tokens): void
119
    {
120
        if ($token->isGivenKind([\T_IF, \T_FOREACH, \T_WHILE, \T_FOR, \T_SWITCH, \T_DECLARE])) {
16✔
121
            $openIndex = $tokens->getNextTokenOfKind($index, ['(']);
16✔
122
            $closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
16✔
123
            $afterParenthesisIndex = $tokens->getNextMeaningfulToken($closeIndex);
16✔
124
            $afterParenthesis = $tokens[$afterParenthesisIndex];
16✔
125

126
            if (!$afterParenthesis->equals(':')) {
16✔
127
                return;
2✔
128
            }
129

130
            $items = [];
16✔
131

132
            if (!$tokens[$afterParenthesisIndex - 1]->isWhitespace()) {
16✔
133
                $items[] = new Token([\T_WHITESPACE, ' ']);
10✔
134
            }
135

136
            $items[] = new Token('{');
16✔
137

138
            if (!$tokens[$afterParenthesisIndex + 1]->isWhitespace()) {
16✔
139
                $items[] = new Token([\T_WHITESPACE, ' ']);
5✔
140
            }
141

142
            $tokens->clearAt($afterParenthesisIndex);
16✔
143
            $tokens->insertAt($afterParenthesisIndex, $items);
16✔
144
        }
145

146
        if (!$token->isGivenKind([\T_ENDIF, \T_ENDFOREACH, \T_ENDWHILE, \T_ENDFOR, \T_ENDSWITCH, \T_ENDDECLARE])) {
16✔
147
            return;
16✔
148
        }
149

150
        $nextTokenIndex = $tokens->getNextMeaningfulToken($index);
16✔
151
        $nextToken = $tokens[$nextTokenIndex];
16✔
152
        $tokens[$index] = new Token('}');
16✔
153

154
        if ($nextToken->equals(';')) {
16✔
155
            $tokens->clearAt($nextTokenIndex);
16✔
156
        }
157
    }
158

159
    /**
160
     * Handle the else: cases.
161
     *
162
     * @param int    $index  the index of the token being processed
163
     * @param Token  $token  the token being processed
164
     * @param Tokens $tokens the collection of tokens
165
     */
166
    private function fixElse(int $index, Token $token, Tokens $tokens): void
167
    {
168
        if (!$token->isGivenKind(\T_ELSE)) {
16✔
169
            return;
16✔
170
        }
171

172
        $tokenAfterElseIndex = $tokens->getNextMeaningfulToken($index);
4✔
173
        $tokenAfterElse = $tokens[$tokenAfterElseIndex];
4✔
174

175
        if (!$tokenAfterElse->equals(':')) {
4✔
176
            return;
×
177
        }
178

179
        $this->addBraces($tokens, new Token([\T_ELSE, 'else']), $index, $tokenAfterElseIndex);
4✔
180
    }
181

182
    /**
183
     * Handle the elsif(): cases.
184
     *
185
     * @param int    $index  the index of the token being processed
186
     * @param Token  $token  the token being processed
187
     * @param Tokens $tokens the collection of tokens
188
     */
189
    private function fixElseif(int $index, Token $token, Tokens $tokens): void
190
    {
191
        if (!$token->isGivenKind(\T_ELSEIF)) {
16✔
192
            return;
16✔
193
        }
194

195
        $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
1✔
196
        $tokenAfterParenthesisIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
1✔
197
        $tokenAfterParenthesis = $tokens[$tokenAfterParenthesisIndex];
1✔
198

199
        if (!$tokenAfterParenthesis->equals(':')) {
1✔
200
            return;
×
201
        }
202

203
        $this->addBraces($tokens, new Token([\T_ELSEIF, 'elseif']), $index, $tokenAfterParenthesisIndex);
1✔
204
    }
205

206
    /**
207
     * Add opening and closing braces to the else: and elseif: cases.
208
     *
209
     * @param Tokens $tokens     the tokens collection
210
     * @param Token  $token      the current token
211
     * @param int    $index      the current token index
212
     * @param int    $colonIndex the index of the colon
213
     */
214
    private function addBraces(Tokens $tokens, Token $token, int $index, int $colonIndex): void
215
    {
216
        $items = [
5✔
217
            new Token('}'),
5✔
218
            new Token([\T_WHITESPACE, ' ']),
5✔
219
            $token,
5✔
220
        ];
5✔
221

222
        if (!$tokens[$index + 1]->isWhitespace()) {
5✔
223
            $items[] = new Token([\T_WHITESPACE, ' ']);
1✔
224
        }
225

226
        $tokens->clearAt($index);
5✔
227
        $tokens->insertAt(
5✔
228
            $index,
5✔
229
            $items
5✔
230
        );
5✔
231

232
        // increment the position of the colon by number of items inserted
233
        $colonIndex += \count($items);
5✔
234

235
        $items = [new Token('{')];
5✔
236

237
        if (!$tokens[$colonIndex + 1]->isWhitespace()) {
5✔
238
            $items[] = new Token([\T_WHITESPACE, ' ']);
3✔
239
        }
240

241
        $tokens->clearAt($colonIndex);
5✔
242
        $tokens->insertAt(
5✔
243
            $colonIndex,
5✔
244
            $items
5✔
245
        );
5✔
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