• 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

82.35
/src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.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\PhpUnit;
16

17
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
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\Analyzer\FunctionsAnalyzer;
27
use PhpCsFixer\Tokenizer\CT;
28
use PhpCsFixer\Tokenizer\Token;
29
use PhpCsFixer\Tokenizer\Tokens;
30
use PhpCsFixer\Tokenizer\TokensAnalyzer;
31
use PhpCsFixer\Utils;
32
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
33

34
/**
35
 * @phpstan-type _AutogeneratedInputConfiguration array{
36
 *  call_type?: 'self'|'static'|'this',
37
 *  methods?: array<string, string>,
38
 * }
39
 * @phpstan-type _AutogeneratedComputedConfiguration array{
40
 *  call_type: 'self'|'static'|'this',
41
 *  methods: array<string, string>,
42
 * }
43
 *
44
 * @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
45
 *
46
 * @phpstan-import-type _PhpTokenArray from Token
47
 *
48
 * @author Filippo Tessarotto <zoeslam@gmail.com>
49
 *
50
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
51
 */
52
final class PhpUnitTestCaseStaticMethodCallsFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface
53
{
54
    /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
55
    use ConfigurableFixerTrait;
56

57
    /**
58
     * @internal
59
     */
60
    public const CALL_TYPE_THIS = 'this';
61

62
    /**
63
     * @internal
64
     */
65
    public const CALL_TYPE_SELF = 'self';
66

67
    /**
68
     * @internal
69
     */
70
    public const CALL_TYPE_STATIC = 'static';
71

72
    /**
73
     * @var array<string, true>
74
     */
75
    private const STATIC_METHODS = [
76
        // Assert methods
77
        'anything' => true,
78
        'arrayHasKey' => true,
79
        'assertArrayHasKey' => true,
80
        'assertArrayIsEqualToArrayIgnoringListOfKeys' => true,
81
        'assertArrayIsEqualToArrayOnlyConsideringListOfKeys' => true,
82
        'assertArrayIsIdenticalToArrayIgnoringListOfKeys' => true,
83
        'assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys' => true,
84
        'assertArrayNotHasKey' => true,
85
        'assertArraySubset' => true,
86
        'assertAttributeContains' => true,
87
        'assertAttributeContainsOnly' => true,
88
        'assertAttributeCount' => true,
89
        'assertAttributeEmpty' => true,
90
        'assertAttributeEquals' => true,
91
        'assertAttributeGreaterThan' => true,
92
        'assertAttributeGreaterThanOrEqual' => true,
93
        'assertAttributeInstanceOf' => true,
94
        'assertAttributeInternalType' => true,
95
        'assertAttributeLessThan' => true,
96
        'assertAttributeLessThanOrEqual' => true,
97
        'assertAttributeNotContains' => true,
98
        'assertAttributeNotContainsOnly' => true,
99
        'assertAttributeNotCount' => true,
100
        'assertAttributeNotEmpty' => true,
101
        'assertAttributeNotEquals' => true,
102
        'assertAttributeNotInstanceOf' => true,
103
        'assertAttributeNotInternalType' => true,
104
        'assertAttributeNotSame' => true,
105
        'assertAttributeSame' => true,
106
        'assertClassHasAttribute' => true,
107
        'assertClassHasStaticAttribute' => true,
108
        'assertClassNotHasAttribute' => true,
109
        'assertClassNotHasStaticAttribute' => true,
110
        'assertContains' => true,
111
        'assertContainsEquals' => true,
112
        'assertContainsNotOnlyArray' => true,
113
        'assertContainsNotOnlyBool' => true,
114
        'assertContainsNotOnlyCallable' => true,
115
        'assertContainsNotOnlyClosedResource' => true,
116
        'assertContainsNotOnlyFloat' => true,
117
        'assertContainsNotOnlyInstancesOf' => true,
118
        'assertContainsNotOnlyInt' => true,
119
        'assertContainsNotOnlyIterable' => true,
120
        'assertContainsNotOnlyNull' => true,
121
        'assertContainsNotOnlyNumeric' => true,
122
        'assertContainsNotOnlyObject' => true,
123
        'assertContainsNotOnlyResource' => true,
124
        'assertContainsNotOnlyScalar' => true,
125
        'assertContainsNotOnlyString' => true,
126
        'assertContainsOnly' => true,
127
        'assertContainsOnlyArray' => true,
128
        'assertContainsOnlyBool' => true,
129
        'assertContainsOnlyCallable' => true,
130
        'assertContainsOnlyClosedResource' => true,
131
        'assertContainsOnlyFloat' => true,
132
        'assertContainsOnlyInstancesOf' => true,
133
        'assertContainsOnlyInt' => true,
134
        'assertContainsOnlyIterable' => true,
135
        'assertContainsOnlyNull' => true,
136
        'assertContainsOnlyNumeric' => true,
137
        'assertContainsOnlyObject' => true,
138
        'assertContainsOnlyResource' => true,
139
        'assertContainsOnlyScalar' => true,
140
        'assertContainsOnlyString' => true,
141
        'assertCount' => true,
142
        'assertDirectoryDoesNotExist' => true,
143
        'assertDirectoryExists' => true,
144
        'assertDirectoryIsNotReadable' => true,
145
        'assertDirectoryIsNotWritable' => true,
146
        'assertDirectoryIsReadable' => true,
147
        'assertDirectoryIsWritable' => true,
148
        'assertDirectoryNotExists' => true,
149
        'assertDirectoryNotIsReadable' => true,
150
        'assertDirectoryNotIsWritable' => true,
151
        'assertDoesNotMatchRegularExpression' => true,
152
        'assertEmpty' => true,
153
        'assertEquals' => true,
154
        'assertEqualsCanonicalizing' => true,
155
        'assertEqualsIgnoringCase' => true,
156
        'assertEqualsWithDelta' => true,
157
        'assertEqualXMLStructure' => true,
158
        'assertFalse' => true,
159
        'assertFileDoesNotExist' => true,
160
        'assertFileEquals' => true,
161
        'assertFileEqualsCanonicalizing' => true,
162
        'assertFileEqualsIgnoringCase' => true,
163
        'assertFileExists' => true,
164
        'assertFileIsNotReadable' => true,
165
        'assertFileIsNotWritable' => true,
166
        'assertFileIsReadable' => true,
167
        'assertFileIsWritable' => true,
168
        'assertFileMatchesFormat' => true,
169
        'assertFileMatchesFormatFile' => true,
170
        'assertFileNotEquals' => true,
171
        'assertFileNotEqualsCanonicalizing' => true,
172
        'assertFileNotEqualsIgnoringCase' => true,
173
        'assertFileNotExists' => true,
174
        'assertFileNotIsReadable' => true,
175
        'assertFileNotIsWritable' => true,
176
        'assertFinite' => true,
177
        'assertGreaterThan' => true,
178
        'assertGreaterThanOrEqual' => true,
179
        'assertInfinite' => true,
180
        'assertInstanceOf' => true,
181
        'assertInternalType' => true,
182
        'assertIsArray' => true,
183
        'assertIsBool' => true,
184
        'assertIsCallable' => true,
185
        'assertIsClosedResource' => true,
186
        'assertIsFloat' => true,
187
        'assertIsInt' => true,
188
        'assertIsIterable' => true,
189
        'assertIsList' => true,
190
        'assertIsNotArray' => true,
191
        'assertIsNotBool' => true,
192
        'assertIsNotCallable' => true,
193
        'assertIsNotClosedResource' => true,
194
        'assertIsNotFloat' => true,
195
        'assertIsNotInt' => true,
196
        'assertIsNotIterable' => true,
197
        'assertIsNotNumeric' => true,
198
        'assertIsNotObject' => true,
199
        'assertIsNotReadable' => true,
200
        'assertIsNotResource' => true,
201
        'assertIsNotScalar' => true,
202
        'assertIsNotString' => true,
203
        'assertIsNotWritable' => true,
204
        'assertIsNumeric' => true,
205
        'assertIsObject' => true,
206
        'assertIsReadable' => true,
207
        'assertIsResource' => true,
208
        'assertIsScalar' => true,
209
        'assertIsString' => true,
210
        'assertIsWritable' => true,
211
        'assertJson' => true,
212
        'assertJsonFileEqualsJsonFile' => true,
213
        'assertJsonFileNotEqualsJsonFile' => true,
214
        'assertJsonStringEqualsJsonFile' => true,
215
        'assertJsonStringEqualsJsonString' => true,
216
        'assertJsonStringNotEqualsJsonFile' => true,
217
        'assertJsonStringNotEqualsJsonString' => true,
218
        'assertLessThan' => true,
219
        'assertLessThanOrEqual' => true,
220
        'assertMatchesRegularExpression' => true,
221
        'assertNan' => true,
222
        'assertNotContains' => true,
223
        'assertNotContainsEquals' => true,
224
        'assertNotContainsOnly' => true,
225
        'assertNotCount' => true,
226
        'assertNotEmpty' => true,
227
        'assertNotEquals' => true,
228
        'assertNotEqualsCanonicalizing' => true,
229
        'assertNotEqualsIgnoringCase' => true,
230
        'assertNotEqualsWithDelta' => true,
231
        'assertNotFalse' => true,
232
        'assertNotInstanceOf' => true,
233
        'assertNotInternalType' => true,
234
        'assertNotIsReadable' => true,
235
        'assertNotIsWritable' => true,
236
        'assertNotNull' => true,
237
        'assertNotRegExp' => true,
238
        'assertNotSame' => true,
239
        'assertNotSameSize' => true,
240
        'assertNotTrue' => true,
241
        'assertNull' => true,
242
        'assertObjectEquals' => true,
243
        'assertObjectHasAttribute' => true,
244
        'assertObjectHasProperty' => true,
245
        'assertObjectNotEquals' => true,
246
        'assertObjectNotHasAttribute' => true,
247
        'assertObjectNotHasProperty' => true,
248
        'assertRegExp' => true,
249
        'assertSame' => true,
250
        'assertSameSize' => true,
251
        'assertStringContainsString' => true,
252
        'assertStringContainsStringIgnoringCase' => true,
253
        'assertStringContainsStringIgnoringLineEndings' => true,
254
        'assertStringEndsNotWith' => true,
255
        'assertStringEndsWith' => true,
256
        'assertStringEqualsFile' => true,
257
        'assertStringEqualsFileCanonicalizing' => true,
258
        'assertStringEqualsFileIgnoringCase' => true,
259
        'assertStringEqualsStringIgnoringLineEndings' => true,
260
        'assertStringMatchesFormat' => true,
261
        'assertStringMatchesFormatFile' => true,
262
        'assertStringNotContainsString' => true,
263
        'assertStringNotContainsStringIgnoringCase' => true,
264
        'assertStringNotEqualsFile' => true,
265
        'assertStringNotEqualsFileCanonicalizing' => true,
266
        'assertStringNotEqualsFileIgnoringCase' => true,
267
        'assertStringNotMatchesFormat' => true,
268
        'assertStringNotMatchesFormatFile' => true,
269
        'assertStringStartsNotWith' => true,
270
        'assertStringStartsWith' => true,
271
        'assertThat' => true,
272
        'assertTrue' => true,
273
        'assertXmlFileEqualsXmlFile' => true,
274
        'assertXmlFileNotEqualsXmlFile' => true,
275
        'assertXmlStringEqualsXmlFile' => true,
276
        'assertXmlStringEqualsXmlString' => true,
277
        'assertXmlStringNotEqualsXmlFile' => true,
278
        'assertXmlStringNotEqualsXmlString' => true,
279
        'attribute' => true,
280
        'attributeEqualTo' => true,
281
        'callback' => true,
282
        'classHasAttribute' => true,
283
        'classHasStaticAttribute' => true,
284
        'contains' => true,
285
        'containsEqual' => true,
286
        'containsIdentical' => true,
287
        'containsOnly' => true,
288
        'containsOnlyArray' => true,
289
        'containsOnlyBool' => true,
290
        'containsOnlyCallable' => true,
291
        'containsOnlyClosedResource' => true,
292
        'containsOnlyFloat' => true,
293
        'containsOnlyInstancesOf' => true,
294
        'containsOnlyInt' => true,
295
        'containsOnlyIterable' => true,
296
        'containsOnlyNull' => true,
297
        'containsOnlyNumeric' => true,
298
        'containsOnlyObject' => true,
299
        'containsOnlyResource' => true,
300
        'containsOnlyScalar' => true,
301
        'containsOnlyString' => true,
302
        'countOf' => true,
303
        'directoryExists' => true,
304
        'equalTo' => true,
305
        'equalToCanonicalizing' => true,
306
        'equalToIgnoringCase' => true,
307
        'equalToWithDelta' => true,
308
        'fail' => true,
309
        'fileExists' => true,
310
        'getCount' => true,
311
        'getObjectAttribute' => true,
312
        'getStaticAttribute' => true,
313
        'greaterThan' => true,
314
        'greaterThanOrEqual' => true,
315
        'identicalTo' => true,
316
        'isArray' => true,
317
        'isBool' => true,
318
        'isCallable' => true,
319
        'isClosedResource' => true,
320
        'isEmpty' => true,
321
        'isFalse' => true,
322
        'isFinite' => true,
323
        'isFloat' => true,
324
        'isInfinite' => true,
325
        'isInstanceOf' => true,
326
        'isInt' => true,
327
        'isIterable' => true,
328
        'isJson' => true,
329
        'isList' => true,
330
        'isNan' => true,
331
        'isNull' => true,
332
        'isNumeric' => true,
333
        'isObject' => true,
334
        'isReadable' => true,
335
        'isResource' => true,
336
        'isScalar' => true,
337
        'isString' => true,
338
        'isTrue' => true,
339
        'isType' => true,
340
        'isWritable' => true,
341
        'lessThan' => true,
342
        'lessThanOrEqual' => true,
343
        'logicalAnd' => true,
344
        'logicalNot' => true,
345
        'logicalOr' => true,
346
        'logicalXor' => true,
347
        'markTestIncomplete' => true,
348
        'markTestSkipped' => true,
349
        'matches' => true,
350
        'matchesRegularExpression' => true,
351
        'objectEquals' => true,
352
        'objectHasAttribute' => true,
353
        'readAttribute' => true,
354
        'resetCount' => true,
355
        'stringContains' => true,
356
        'stringEndsWith' => true,
357
        'stringEqualsStringIgnoringLineEndings' => true,
358
        'stringStartsWith' => true,
359

360
        // TestCase methods
361
        'any' => true,
362
        'at' => true,
363
        'atLeast' => true,
364
        'atLeastOnce' => true,
365
        'atMost' => true,
366
        'createStub' => true,
367
        'createConfiguredStub' => true,
368
        'createStubForIntersectionOfInterfaces' => true,
369
        'exactly' => true,
370
        'never' => true,
371
        'once' => true,
372
        'onConsecutiveCalls' => true,
373
        'returnArgument' => true,
374
        'returnCallback' => true,
375
        'returnSelf' => true,
376
        'returnValue' => true,
377
        'returnValueMap' => true,
378
        'setUpBeforeClass' => true,
379
        'tearDownAfterClass' => true,
380
        'throwException' => true,
381
    ];
382

383
    /**
384
     * @var array<string, bool>
385
     */
386
    private const ALLOWED_VALUES = [
387
        self::CALL_TYPE_THIS => true,
388
        self::CALL_TYPE_SELF => true,
389
        self::CALL_TYPE_STATIC => true,
390
    ];
391

392
    /**
393
     * @var non-empty-array<string, non-empty-list<_PhpTokenArray>>
394
     */
395
    private array $conversionMap = [
396
        self::CALL_TYPE_THIS => [[\T_OBJECT_OPERATOR, '->'], [\T_VARIABLE, '$this']],
397
        self::CALL_TYPE_SELF => [[\T_DOUBLE_COLON, '::'], [\T_STRING, 'self']],
398
        self::CALL_TYPE_STATIC => [[\T_DOUBLE_COLON, '::'], [\T_STATIC, 'static']],
399
    ];
400

401
    public function getDefinition(): FixerDefinitionInterface
402
    {
403
        $codeSample = '<?php
3✔
404
final class MyTest extends \PHPUnit_Framework_TestCase
405
{
406
    public function testMe()
407
    {
408
        $this->assertSame(1, 2);
409
        self::assertSame(1, 2);
410
        static::assertSame(1, 2);
411
        static::assertTrue(false);
412
    }
413
}
414
';
3✔
415

416
        return new FixerDefinition(
3✔
417
            'Calls to `PHPUnit\Framework\TestCase` static methods must all be of the same type, either `$this->`, `self::` or `static::`.',
3✔
418
            [
3✔
419
                new CodeSample($codeSample),
3✔
420
                new CodeSample($codeSample, ['call_type' => self::CALL_TYPE_THIS]),
3✔
421
                new CodeSample($codeSample, ['methods' => ['assertTrue' => self::CALL_TYPE_THIS]]),
3✔
422
            ],
3✔
423
            null,
3✔
424
            'Risky when PHPUnit methods are overridden or not accessible, or when project has PHPUnit incompatibilities.'
3✔
425
        );
3✔
426
    }
427

428
    /**
429
     * {@inheritdoc}
430
     *
431
     * Must run before SelfStaticAccessorFixer.
432
     */
433
    public function getPriority(): int
434
    {
435
        return 0;
1✔
436
    }
437

438
    public function isRisky(): bool
439
    {
440
        return true;
1✔
441
    }
442

443
    protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
444
    {
445
        return new FixerConfigurationResolver([
28✔
446
            (new FixerOptionBuilder('call_type', 'The call type to use for referring to PHPUnit methods.'))
28✔
447
                ->setAllowedTypes(['string'])
28✔
448
                ->setAllowedValues(array_keys(self::ALLOWED_VALUES))
28✔
449
                ->setDefault(self::CALL_TYPE_STATIC)
28✔
450
                ->getOption(),
28✔
451
            (new FixerOptionBuilder('methods', 'Dictionary of `method` => `call_type` values that differ from the default strategy.'))
28✔
452
                ->setAllowedTypes(['array<string, string>'])
28✔
453
                ->setAllowedValues([static function (array $option): bool {
28✔
454
                    foreach ($option as $method => $value) {
28✔
455
                        if (!isset(self::STATIC_METHODS[$method])) {
2✔
456
                            throw new InvalidOptionsException(
×
457
                                \sprintf(
×
458
                                    'Unexpected "methods" key, expected any of %s, got "%s".',
×
459
                                    Utils::naturalLanguageJoin(array_keys(self::STATIC_METHODS)),
×
460
                                    \gettype($method).'#'.$method
×
461
                                )
×
462
                            );
×
463
                        }
464

465
                        if (!isset(self::ALLOWED_VALUES[$value])) {
2✔
466
                            throw new InvalidOptionsException(
×
467
                                \sprintf(
×
468
                                    'Unexpected value for method "%s", expected any of %s, got "%s".',
×
469
                                    $method,
×
470
                                    Utils::naturalLanguageJoin(array_keys(self::ALLOWED_VALUES)),
×
471
                                    \is_object($value) ? \get_class($value) : (null === $value ? 'null' : \gettype($value).'#'.$value)
×
472
                                )
×
473
                            );
×
474
                        }
475
                    }
476

477
                    return true;
28✔
478
                }])
28✔
479
                ->setDefault([])
28✔
480
                ->getOption(),
28✔
481
        ]);
28✔
482
    }
483

484
    protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
485
    {
486
        $analyzer = new TokensAnalyzer($tokens);
16✔
487

488
        for ($index = $startIndex; $index < $endIndex; ++$index) {
16✔
489
            // skip anonymous classes
490
            if ($tokens[$index]->isGivenKind(\T_CLASS)) {
16✔
491
                $index = $this->findEndOfNextBlock($tokens, $index);
1✔
492

493
                continue;
1✔
494
            }
495

496
            $callType = $this->configuration['call_type'];
16✔
497

498
            if ($tokens[$index]->isGivenKind(\T_FUNCTION)) {
16✔
499
                // skip lambda
500
                if ($analyzer->isLambda($index)) {
16✔
501
                    $index = $this->findEndOfNextBlock($tokens, $index);
3✔
502

503
                    continue;
3✔
504
                }
505

506
                // do not change `self` to `this` in static methods
507
                if (self::CALL_TYPE_THIS === $callType) {
16✔
508
                    $attributes = $analyzer->getMethodAttributes($index);
6✔
509

510
                    if (false !== $attributes['static']) {
6✔
511
                        $index = $this->findEndOfNextBlock($tokens, $index);
2✔
512

513
                        continue;
2✔
514
                    }
515
                }
516
            }
517

518
            if (!$tokens[$index]->isGivenKind(\T_STRING) || !isset(self::STATIC_METHODS[$tokens[$index]->getContent()])) {
16✔
519
                continue;
16✔
520
            }
521

522
            $nextIndex = $tokens->getNextMeaningfulToken($index);
14✔
523

524
            if (!$tokens[$nextIndex]->equals('(')) {
14✔
525
                $index = $nextIndex;
1✔
526

527
                continue;
1✔
528
            }
529

530
            if ($tokens[$tokens->getNextMeaningfulToken($nextIndex)]->isGivenKind(CT::T_FIRST_CLASS_CALLABLE)) {
14✔
531
                continue;
1✔
532
            }
533

534
            $methodName = $tokens[$index]->getContent();
13✔
535

536
            if (isset($this->configuration['methods'][$methodName])) {
13✔
537
                $callType = $this->configuration['methods'][$methodName];
2✔
538
            }
539

540
            $operatorIndex = $tokens->getPrevMeaningfulToken($index);
13✔
541
            $referenceIndex = $tokens->getPrevMeaningfulToken($operatorIndex);
13✔
542

543
            if (!$this->needsConversion($tokens, $index, $referenceIndex, $callType)) {
13✔
544
                continue;
13✔
545
            }
546

547
            $tokens[$operatorIndex] = new Token($this->conversionMap[$callType][0]);
11✔
548
            $tokens[$referenceIndex] = new Token($this->conversionMap[$callType][1]);
11✔
549
        }
550
    }
551

552
    private function needsConversion(Tokens $tokens, int $index, int $referenceIndex, string $callType): bool
553
    {
554
        $functionsAnalyzer = new FunctionsAnalyzer();
13✔
555

556
        return $functionsAnalyzer->isTheSameClassCall($tokens, $index)
13✔
557
            && !$tokens[$referenceIndex]->equals($this->conversionMap[$callType][1], false);
13✔
558
    }
559

560
    private function findEndOfNextBlock(Tokens $tokens, int $index): int
561
    {
562
        $nextIndex = $tokens->getNextTokenOfKind($index, [';', '{']);
5✔
563

564
        return $tokens[$nextIndex]->equals('{')
5✔
565
            ? $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextIndex)
4✔
566
            : $nextIndex;
5✔
567
    }
568
}
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