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

keradus / PHP-CS-Fixer / 17377459942

01 Sep 2025 12:19PM UTC coverage: 94.684% (-0.009%) from 94.693%
17377459942

push

github

web-flow
chore: `Tokens::offsetSet` - explicit validation of input (#9004)

1 of 5 new or added lines in 1 file covered. (20.0%)

306 existing lines in 60 files now uncovered.

28390 of 29984 relevant lines covered (94.68%)

45.5 hits per line

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

97.82
/src/Fixer/Basic/BracesPositionFixer.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\Basic;
16

17
use PhpCsFixer\AbstractFixer;
18
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
19
use PhpCsFixer\Fixer\ConfigurableFixerTrait;
20
use PhpCsFixer\Fixer\IndentationTrait;
21
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
22
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
23
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
24
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
25
use PhpCsFixer\FixerDefinition\CodeSample;
26
use PhpCsFixer\FixerDefinition\FixerDefinition;
27
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
28
use PhpCsFixer\Preg;
29
use PhpCsFixer\Tokenizer\CT;
30
use PhpCsFixer\Tokenizer\FCT;
31
use PhpCsFixer\Tokenizer\Token;
32
use PhpCsFixer\Tokenizer\Tokens;
33
use PhpCsFixer\Tokenizer\TokensAnalyzer;
34

35
/**
36
 * @phpstan-type _AutogeneratedInputConfiguration array{
37
 *  allow_single_line_anonymous_functions?: bool,
38
 *  allow_single_line_empty_anonymous_classes?: bool,
39
 *  anonymous_classes_opening_brace?: 'next_line_unless_newline_at_signature_end'|'same_line',
40
 *  anonymous_functions_opening_brace?: 'next_line_unless_newline_at_signature_end'|'same_line',
41
 *  classes_opening_brace?: 'next_line_unless_newline_at_signature_end'|'same_line',
42
 *  control_structures_opening_brace?: 'next_line_unless_newline_at_signature_end'|'same_line',
43
 *  functions_opening_brace?: 'next_line_unless_newline_at_signature_end'|'same_line',
44
 * }
45
 * @phpstan-type _AutogeneratedComputedConfiguration array{
46
 *  allow_single_line_anonymous_functions: bool,
47
 *  allow_single_line_empty_anonymous_classes: bool,
48
 *  anonymous_classes_opening_brace: 'next_line_unless_newline_at_signature_end'|'same_line',
49
 *  anonymous_functions_opening_brace: 'next_line_unless_newline_at_signature_end'|'same_line',
50
 *  classes_opening_brace: 'next_line_unless_newline_at_signature_end'|'same_line',
51
 *  control_structures_opening_brace: 'next_line_unless_newline_at_signature_end'|'same_line',
52
 *  functions_opening_brace: 'next_line_unless_newline_at_signature_end'|'same_line',
53
 * }
54
 *
55
 * @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
56
 *
57
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
58
 */
59
final class BracesPositionFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
60
{
61
    /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
62
    use ConfigurableFixerTrait;
63

64
    use IndentationTrait;
65

66
    /**
67
     * @internal
68
     */
69
    public const NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END = 'next_line_unless_newline_at_signature_end';
70

71
    /**
72
     * @internal
73
     */
74
    public const SAME_LINE = 'same_line';
75

76
    private const CONTROL_STRUCTURE_TOKENS = [\T_DECLARE, \T_DO, \T_ELSE, \T_ELSEIF, \T_FINALLY, \T_FOR, \T_FOREACH, \T_IF, \T_WHILE, \T_TRY, \T_CATCH, \T_SWITCH, FCT::T_MATCH];
77

78
    public function getDefinition(): FixerDefinitionInterface
79
    {
80
        return new FixerDefinition(
3✔
81
            'Braces must be placed as configured.',
3✔
82
            [
3✔
83
                new CodeSample(
3✔
84
                    <<<'PHP'
3✔
85
                        <?php
86
                        class Foo {
87
                        }
88

89
                        function foo() {
90
                        }
91

92
                        $foo = function()
93
                        {
94
                        };
95

96
                        if (foo())
97
                        {
98
                            bar();
99
                        }
100

101
                        $foo = new class
102
                        {
103
                        };
104

105
                        PHP
3✔
106
                ),
3✔
107
                new CodeSample(
3✔
108
                    <<<'PHP'
3✔
109
                        <?php
110
                        if (foo()) {
111
                            bar();
112
                        }
113

114
                        PHP,
3✔
115
                    ['control_structures_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
3✔
116
                ),
3✔
117
                new CodeSample(
3✔
118
                    <<<'PHP'
3✔
119
                        <?php
120
                        function foo()
121
                        {
122
                        }
123

124
                        PHP,
3✔
125
                    ['functions_opening_brace' => self::SAME_LINE]
3✔
126
                ),
3✔
127
                new CodeSample(
3✔
128
                    <<<'PHP'
3✔
129
                        <?php
130
                        $foo = function () {
131
                        };
132

133
                        PHP,
3✔
134
                    ['anonymous_functions_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
3✔
135
                ),
3✔
136
                new CodeSample(
3✔
137
                    <<<'PHP'
3✔
138
                        <?php
139
                        class Foo
140
                        {
141
                        }
142

143
                        PHP,
3✔
144
                    ['classes_opening_brace' => self::SAME_LINE]
3✔
145
                ),
3✔
146
                new CodeSample(
3✔
147
                    <<<'PHP'
3✔
148
                        <?php
149
                        $foo = new class {
150
                        };
151

152
                        PHP,
3✔
153
                    ['anonymous_classes_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
3✔
154
                ),
3✔
155
                new CodeSample(
3✔
156
                    <<<'PHP'
3✔
157
                        <?php
158
                        $foo = new class { };
159
                        $bar = new class { private $baz; };
160

161
                        PHP,
3✔
162
                    ['allow_single_line_empty_anonymous_classes' => true]
3✔
163
                ),
3✔
164
                new CodeSample(
3✔
165
                    <<<'PHP'
3✔
166
                        <?php
167
                        $foo = function () { return true; };
168
                        $bar = function () { $result = true;
169
                            return $result; };
170

171
                        PHP,
3✔
172
                    ['allow_single_line_anonymous_functions' => true]
3✔
173
                ),
3✔
174
            ]
3✔
175
        );
3✔
176
    }
177

178
    public function isCandidate(Tokens $tokens): bool
179
    {
180
        return $tokens->isTokenKindFound('{');
60✔
181
    }
182

183
    /**
184
     * {@inheritdoc}
185
     *
186
     * Must run before SingleLineEmptyBodyFixer, StatementIndentationFixer.
187
     * Must run after ControlStructureBracesFixer, MultilinePromotedPropertiesFixer, NoMultipleStatementsPerLineFixer.
188
     */
189
    public function getPriority(): int
190
    {
191
        return -2;
1✔
192
    }
193

194
    /** @protected */
195
    public function createConfigurationDefinition(): FixerConfigurationResolverInterface
196
    {
197
        return new FixerConfigurationResolver([
69✔
198
            (new FixerOptionBuilder('control_structures_opening_brace', 'The position of the opening brace of control structures‘ body.'))
69✔
199
                ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
69✔
200
                ->setDefault(self::SAME_LINE)
69✔
201
                ->getOption(),
69✔
202
            (new FixerOptionBuilder('functions_opening_brace', 'The position of the opening brace of functions‘ body.'))
69✔
203
                ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
69✔
204
                ->setDefault(self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END)
69✔
205
                ->getOption(),
69✔
206
            (new FixerOptionBuilder('anonymous_functions_opening_brace', 'The position of the opening brace of anonymous functions‘ body.'))
69✔
207
                ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
69✔
208
                ->setDefault(self::SAME_LINE)
69✔
209
                ->getOption(),
69✔
210
            (new FixerOptionBuilder('classes_opening_brace', 'The position of the opening brace of classes‘ body.'))
69✔
211
                ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
69✔
212
                ->setDefault(self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END)
69✔
213
                ->getOption(),
69✔
214
            (new FixerOptionBuilder('anonymous_classes_opening_brace', 'The position of the opening brace of anonymous classes‘ body.'))
69✔
215
                ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
69✔
216
                ->setDefault(self::SAME_LINE)
69✔
217
                ->getOption(),
69✔
218
            (new FixerOptionBuilder('allow_single_line_empty_anonymous_classes', 'Allow anonymous classes to have opening and closing braces on the same line.'))
69✔
219
                ->setAllowedTypes(['bool'])
69✔
220
                ->setDefault(true)
69✔
221
                ->getOption(),
69✔
222
            (new FixerOptionBuilder('allow_single_line_anonymous_functions', 'Allow anonymous functions to have opening and closing braces on the same line.'))
69✔
223
                ->setAllowedTypes(['bool'])
69✔
224
                ->setDefault(true)
69✔
225
                ->getOption(),
69✔
226
        ]);
69✔
227
    }
228

229
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
230
    {
231
        $classyTokens = Token::getClassyTokenKinds();
60✔
232
        $tokensAnalyzer = new TokensAnalyzer($tokens);
60✔
233

234
        $allowSingleLineUntil = null;
60✔
235

236
        foreach ($tokens as $index => $token) {
60✔
237
            $allowSingleLine = false;
60✔
238
            $allowSingleLineIfEmpty = false;
60✔
239

240
            if ($token->isGivenKind($classyTokens)) {
60✔
241
                $openBraceIndex = $tokens->getNextTokenOfKind($index, ['{']);
15✔
242

243
                if ($tokensAnalyzer->isAnonymousClass($index)) {
15✔
244
                    $allowSingleLineIfEmpty = true === $this->configuration['allow_single_line_empty_anonymous_classes'];
4✔
245
                    $positionOption = 'anonymous_classes_opening_brace';
4✔
246
                } else {
247
                    $positionOption = 'classes_opening_brace';
12✔
248
                }
249
            } elseif ($token->isGivenKind(\T_FUNCTION)) {
60✔
250
                $openBraceIndex = $tokens->getNextTokenOfKind($index, ['{', ';', [CT::T_PROPERTY_HOOK_BRACE_OPEN]]);
25✔
251

252
                if (!$tokens[$openBraceIndex]->equals('{')) {
25✔
253
                    continue;
2✔
254
                }
255

256
                if ($tokensAnalyzer->isLambda($index)) {
23✔
257
                    $allowSingleLine = true === $this->configuration['allow_single_line_anonymous_functions'];
4✔
258
                    $positionOption = 'anonymous_functions_opening_brace';
4✔
259
                } else {
260
                    $positionOption = 'functions_opening_brace';
21✔
261
                }
262
            } elseif ($token->isGivenKind(self::CONTROL_STRUCTURE_TOKENS)) {
60✔
263
                $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
30✔
264
                $openBraceIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
30✔
265

266
                if (!$tokens[$openBraceIndex]->equals('{')) {
30✔
267
                    continue;
4✔
268
                }
269

270
                $positionOption = 'control_structures_opening_brace';
30✔
271
            } elseif ($token->isGivenKind(\T_VARIABLE)) {
60✔
272
                // handle default value - explicitly skip array as default value
273
                $nextMeaningfulIndex = $tokens->getNextMeaningfulToken($index);
47✔
274
                if ($tokens[$nextMeaningfulIndex]->equals('=')) {
47✔
275
                    $nextMeaningfulIndex = $tokens->getNextMeaningfulToken($nextMeaningfulIndex);
9✔
276

277
                    if ($tokens[$nextMeaningfulIndex]->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) {
9✔
278
                        $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $nextMeaningfulIndex);
1✔
279
                    } elseif ($tokens[$nextMeaningfulIndex]->isGivenKind(\T_ARRAY)) {
9✔
280
                        $nextMeaningfulIndex = $tokens->getNextMeaningfulToken($nextMeaningfulIndex);
1✔
281
                        if ($tokens[$nextMeaningfulIndex]->equals('(')) {
1✔
282
                            $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextMeaningfulIndex);
1✔
283
                        }
284
                    }
285
                }
286

287
                $openBraceIndex = $tokens->getNextTokenOfKind($index, ['{', ';', '.', [CT::T_CURLY_CLOSE], [CT::T_PROPERTY_HOOK_BRACE_OPEN], [\T_ENCAPSED_AND_WHITESPACE], [\T_CLOSE_TAG]]);
47✔
288

289
                if (!$tokens[$openBraceIndex]->isGivenKind(CT::T_PROPERTY_HOOK_BRACE_OPEN)) {
47✔
290
                    continue;
46✔
291
                }
292

293
                $closeBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PROPERTY_HOOK, $openBraceIndex);
5✔
294
                if (!$tokens->isPartialCodeMultiline($openBraceIndex, $closeBraceIndex)) {
5✔
295
                    continue;
1✔
296
                }
297

298
                $positionOption = 'control_structures_opening_brace';
4✔
299
            } elseif ($token->isGivenKind(\T_STRING)) {
60✔
300
                $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
54✔
301
                $openBraceIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
54✔
302
                if (!$tokens[$openBraceIndex]->equals('{')) {
54✔
303
                    continue;
46✔
304
                }
305

306
                $prevIndex = $tokens->getPrevMeaningfulToken($index);
27✔
307
                if (!$tokens[$prevIndex]->equalsAny(['}', ';', [CT::T_ATTRIBUTE_CLOSE], [CT::T_PROPERTY_HOOK_BRACE_OPEN]])) {
27✔
308
                    continue;
27✔
309
                }
310
                $allowSingleLine = true === $this->configuration['allow_single_line_anonymous_functions'];
4✔
311
                $positionOption = 'control_structures_opening_brace';
4✔
312
            } else {
313
                continue;
60✔
314
            }
315

316
            $closeBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openBraceIndex);
60✔
317

318
            $addNewlinesInsideBraces = true;
60✔
319
            if ($allowSingleLine || $allowSingleLineIfEmpty || $index < $allowSingleLineUntil) {
60✔
320
                $addNewlinesInsideBraces = false;
11✔
321

322
                for ($indexInsideBraces = $openBraceIndex + 1; $indexInsideBraces < $closeBraceIndex; ++$indexInsideBraces) {
11✔
323
                    $tokenInsideBraces = $tokens[$indexInsideBraces];
10✔
324

325
                    if (
326
                        ($allowSingleLineIfEmpty && !$tokenInsideBraces->isWhitespace() && !$tokenInsideBraces->isComment())
10✔
327
                        || ($tokenInsideBraces->isWhitespace() && Preg::match('/\R/', $tokenInsideBraces->getContent()))
10✔
328
                    ) {
329
                        $addNewlinesInsideBraces = true;
9✔
330

331
                        break;
9✔
332
                    }
333
                }
334

335
                if (!$addNewlinesInsideBraces && null === $allowSingleLineUntil) {
11✔
336
                    $allowSingleLineUntil = $closeBraceIndex;
3✔
337
                }
338
            }
339

340
            if (
341
                $addNewlinesInsideBraces
60✔
342
                && !$this->isFollowedByNewLine($tokens, $openBraceIndex)
60✔
343
                && !$this->hasCommentOnSameLine($tokens, $openBraceIndex)
60✔
344
                && !$tokens[$tokens->getNextMeaningfulToken($openBraceIndex)]->isGivenKind(\T_CLOSE_TAG)
60✔
345
            ) {
346
                $whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $openBraceIndex);
2✔
347
                if ($tokens->ensureWhitespaceAtIndex($openBraceIndex + 1, 0, $whitespace)) {
2✔
UNCOV
348
                    ++$closeBraceIndex;
×
349
                }
350
            }
351

352
            $whitespace = ' ';
60✔
353
            if (self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END === $this->configuration[$positionOption]) {
60✔
354
                $whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $index);
40✔
355

356
                $previousTokenIndex = $openBraceIndex;
40✔
357
                do {
358
                    $previousTokenIndex = $tokens->getPrevMeaningfulToken($previousTokenIndex);
40✔
359
                } while ($tokens[$previousTokenIndex]->isGivenKind([CT::T_TYPE_COLON, CT::T_NULLABLE_TYPE, \T_STRING, \T_NS_SEPARATOR, CT::T_ARRAY_TYPEHINT, \T_STATIC, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION, \T_CALLABLE, CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_OPEN, CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_CLOSE]));
40✔
360

361
                if ($tokens[$previousTokenIndex]->equals(')')) {
40✔
362
                    if ($tokens[--$previousTokenIndex]->isComment()) {
33✔
UNCOV
363
                        --$previousTokenIndex;
×
364
                    }
365
                    if (
366
                        $tokens[$previousTokenIndex]->isWhitespace()
33✔
367
                        && Preg::match('/\R/', $tokens[$previousTokenIndex]->getContent())
33✔
368
                    ) {
369
                        $whitespace = ' ';
13✔
370
                    }
371
                }
372
            }
373

374
            $moveBraceToIndex = null;
60✔
375

376
            if (' ' === $whitespace) {
60✔
377
                $previousMeaningfulIndex = $tokens->getPrevMeaningfulToken($openBraceIndex);
41✔
378
                for ($indexBeforeOpenBrace = $openBraceIndex - 1; $indexBeforeOpenBrace > $previousMeaningfulIndex; --$indexBeforeOpenBrace) {
41✔
379
                    if (!$tokens[$indexBeforeOpenBrace]->isComment()) {
41✔
380
                        continue;
41✔
381
                    }
382

383
                    $tokenBeforeOpenBrace = $tokens[--$indexBeforeOpenBrace];
4✔
384
                    if ($tokenBeforeOpenBrace->isWhitespace()) {
4✔
385
                        $moveBraceToIndex = $indexBeforeOpenBrace;
3✔
386
                    } elseif ($indexBeforeOpenBrace === $previousMeaningfulIndex) {
1✔
387
                        $moveBraceToIndex = $previousMeaningfulIndex + 1;
1✔
388
                    }
389
                }
390
            } elseif (!$tokens[$openBraceIndex - 1]->isWhitespace() || !Preg::match('/\R/', $tokens[$openBraceIndex - 1]->getContent())) {
28✔
391
                for ($indexAfterOpenBrace = $openBraceIndex + 1; $indexAfterOpenBrace < $closeBraceIndex; ++$indexAfterOpenBrace) {
22✔
392
                    if ($tokens[$indexAfterOpenBrace]->isWhitespace() && Preg::match('/\R/', $tokens[$indexAfterOpenBrace]->getContent())) {
22✔
393
                        break;
22✔
394
                    }
395

396
                    if ($tokens[$indexAfterOpenBrace]->isComment() && !str_starts_with($tokens[$indexAfterOpenBrace]->getContent(), '/*')) {
3✔
397
                        $moveBraceToIndex = $indexAfterOpenBrace + 1;
3✔
398
                    }
399
                }
400
            }
401

402
            if (null !== $moveBraceToIndex) {
60✔
403
                $movedToken = clone $tokens[$openBraceIndex];
7✔
404

405
                $delta = $openBraceIndex < $moveBraceToIndex ? 1 : -1;
7✔
406

407
                if ($tokens[$openBraceIndex + $delta]->isWhitespace()) {
7✔
408
                    if (-1 === $delta && Preg::match('/\R/', $tokens[$openBraceIndex - 1]->getContent())) {
6✔
409
                        $content = Preg::replace('/^(\h*?\R)?\h*/', '', $tokens[$openBraceIndex + 1]->getContent());
2✔
410
                        if ('' !== $content) {
2✔
411
                            $tokens[$openBraceIndex + 1] = new Token([\T_WHITESPACE, $content]);
1✔
412
                        } else {
413
                            $tokens->clearAt($openBraceIndex + 1);
1✔
414
                        }
415
                    } elseif ($tokens[$openBraceIndex - 1]->isWhitespace()) {
4✔
416
                        $tokens->clearAt($openBraceIndex - 1);
3✔
417
                    }
418
                }
419

420
                for ($i = $openBraceIndex; $i !== $moveBraceToIndex; $i += $delta) {
7✔
421
                    $siblingToken = $tokens[$i + $delta];
7✔
422
                    $tokens[$i] = $siblingToken;
7✔
423
                }
424

425
                $tokens[$i] = $movedToken;
7✔
426

427
                if ($tokens[$openBraceIndex]->isWhitespace() && $tokens[$openBraceIndex + 1]->isWhitespace()) {
7✔
428
                    $tokens[$openBraceIndex] = new Token([
4✔
429
                        \T_WHITESPACE,
4✔
430
                        $tokens[$openBraceIndex]->getContent().$tokens[$openBraceIndex + 1]->getContent(),
4✔
431
                    ]);
4✔
432
                    $tokens->clearAt($openBraceIndex + 1);
4✔
433
                }
434

435
                $openBraceIndex = $moveBraceToIndex;
7✔
436
            }
437

438
            if ($tokens->ensureWhitespaceAtIndex($openBraceIndex - 1, 1, $whitespace)) {
60✔
439
                ++$closeBraceIndex;
5✔
440
                if (null !== $allowSingleLineUntil) {
5✔
UNCOV
441
                    ++$allowSingleLineUntil;
×
442
                }
443
            }
444

445
            if (
446
                !$addNewlinesInsideBraces
60✔
447
                || $tokens[$tokens->getPrevMeaningfulToken($closeBraceIndex)]->isGivenKind(\T_OPEN_TAG)
60✔
448
            ) {
449
                continue;
3✔
450
            }
451

452
            $prevIndex = $closeBraceIndex - 1;
60✔
453
            while ($tokens->isEmptyAt($prevIndex)) {
60✔
UNCOV
454
                --$prevIndex;
×
455
            }
456

457
            $prevToken = $tokens[$prevIndex];
60✔
458

459
            if ($prevToken->isWhitespace() && Preg::match('/\R/', $prevToken->getContent())) {
60✔
460
                continue;
60✔
461
            }
462

463
            $whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $openBraceIndex);
5✔
464
            $tokens->ensureWhitespaceAtIndex($prevIndex, 1, $whitespace);
5✔
465
        }
466
    }
467

468
    private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
469
    {
470
        $nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex);
56✔
471
        $nextToken = $tokens[$nextIndex];
56✔
472

473
        // return if next token is not opening parenthesis
474
        if (!$nextToken->equals('(')) {
56✔
475
            return $structureTokenIndex;
32✔
476
        }
477

478
        return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
53✔
479
    }
480

481
    private function isFollowedByNewLine(Tokens $tokens, int $index): bool
482
    {
483
        for (++$index, $max = \count($tokens) - 1; $index < $max; ++$index) {
60✔
484
            $token = $tokens[$index];
60✔
485
            if (!$token->isComment()) {
60✔
486
                return $token->isWhitespace() && Preg::match('/\R/', $token->getContent());
60✔
487
            }
488
        }
489

UNCOV
490
        return false;
×
491
    }
492

493
    private function hasCommentOnSameLine(Tokens $tokens, int $index): bool
494
    {
495
        $token = $tokens[$index + 1];
7✔
496

497
        if ($token->isWhitespace() && !Preg::match('/\R/', $token->getContent())) {
7✔
498
            $token = $tokens[$index + 2];
7✔
499
        }
500

501
        return $token->isComment();
7✔
502
    }
503
}
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