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

keradus / PHP-CS-Fixer / 17678835382

12 Sep 2025 03:24PM UTC coverage: 94.69% (-0.06%) from 94.75%
17678835382

push

github

keradus
fix typo

1 of 1 new or added line in 1 file covered. (100.0%)

1042 existing lines in 177 files now uncovered.

28424 of 30018 relevant lines covered (94.69%)

45.5 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\Tokenizer\Token;
27
use PhpCsFixer\Tokenizer\Tokens;
28

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

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

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

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

79
    protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
80
    {
81
        return new FixerConfigurationResolver([
29✔
82
            (new FixerOptionBuilder('fix_non_monolithic_code', 'Whether to also fix code with inline HTML.'))
29✔
83
                ->setAllowedTypes(['bool'])
29✔
84
                ->setDefault(true) // @TODO change to "false" on next major 4.0
29✔
85
                ->getOption(),
29✔
86
        ]);
29✔
87
    }
88

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

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

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

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

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

129
            $items = [];
16✔
130

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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