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

keradus / PHP-CS-Fixer / 22221749797

20 Feb 2026 11:06AM UTC coverage: 92.928% (-0.03%) from 92.957%
22221749797

push

github

web-flow
fix: `NoUnusedImportsFixer` - do not remove constant types (#9442)

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

139 existing lines in 4 files now uncovered.

29305 of 31535 relevant lines covered (92.93%)

44.03 hits per line

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

38.21
/src/Fixer/Internal/ConfigurableFixerTemplateFixer.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\Internal;
16

17
use PhpCsFixer\AbstractDoctrineAnnotationFixer;
18
use PhpCsFixer\AbstractFixer;
19
use PhpCsFixer\AbstractPhpdocToTypeDeclarationFixer;
20
use PhpCsFixer\Console\Command\HelpCommand;
21
use PhpCsFixer\DocBlock\DocBlock;
22
use PhpCsFixer\Doctrine\Annotation\Tokens as DoctrineAnnotationTokens;
23
use PhpCsFixer\Fixer\AttributeNotation\OrderedAttributesFixer;
24
use PhpCsFixer\Fixer\Casing\ConstantCaseFixer;
25
use PhpCsFixer\Fixer\ClassNotation\FinalInternalClassFixer;
26
use PhpCsFixer\Fixer\Comment\HeaderCommentFixer;
27
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
28
use PhpCsFixer\Fixer\ControlStructure\NoBreakCommentFixer;
29
use PhpCsFixer\Fixer\ControlStructure\TrailingCommaInMultilineFixer;
30
use PhpCsFixer\Fixer\FixerInterface;
31
use PhpCsFixer\Fixer\Import\OrderedImportsFixer;
32
use PhpCsFixer\Fixer\InternalFixerInterface;
33
use PhpCsFixer\Fixer\NamespaceNotation\BlankLinesBeforeNamespaceFixer;
34
use PhpCsFixer\Fixer\Phpdoc\GeneralPhpdocAnnotationRemoveFixer;
35
use PhpCsFixer\Fixer\Phpdoc\GeneralPhpdocTagRenameFixer;
36
use PhpCsFixer\Fixer\Phpdoc\PhpdocOrderByValueFixer;
37
use PhpCsFixer\Fixer\Phpdoc\PhpdocReturnSelfReferenceFixer;
38
use PhpCsFixer\Fixer\Phpdoc\PhpdocTagTypeFixer;
39
use PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer;
40
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
41
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
42
use PhpCsFixer\FixerDefinition\CodeSample;
43
use PhpCsFixer\FixerDefinition\FixerDefinition;
44
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
45
use PhpCsFixer\Preg;
46
use PhpCsFixer\StdinFileInfo;
47
use PhpCsFixer\Tests\AbstractDoctrineAnnotationFixerTestCase;
48
use PhpCsFixer\Tests\AbstractFixerTest;
49
use PhpCsFixer\Tests\AbstractFunctionReferenceFixerTest;
50
use PhpCsFixer\Tests\AbstractProxyFixerTest;
51
use PhpCsFixer\Tests\Fixer\Whitespace\AbstractNullableTypeDeclarationFixerTestCase;
52
use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
53
use PhpCsFixer\Tokenizer\CT;
54
use PhpCsFixer\Tokenizer\FCT;
55
use PhpCsFixer\Tokenizer\Token;
56
use PhpCsFixer\Tokenizer\Tokens;
57
use PhpCsFixer\Utils;
58

59
/**
60
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
61
 *
62
 * @internal
63
 *
64
 * @warning Does not support PHPUnit attributes
65
 *
66
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
67
 */
68
final class ConfigurableFixerTemplateFixer extends AbstractFixer implements InternalFixerInterface
69
{
70
    private const MODIFIERS = [\T_PUBLIC, \T_PROTECTED, \T_PRIVATE, \T_FINAL, \T_ABSTRACT, \T_COMMENT, FCT::T_ATTRIBUTE, FCT::T_READONLY];
71

72
    public function getName(): string
73
    {
74
        return 'PhpCsFixerInternal/'.parent::getName();
3✔
75
    }
76

77
    public function getDefinition(): FixerDefinitionInterface
78
    {
79
        $fileInfo = $this->getExampleFixerFile();
3✔
80
        $file = $fileInfo->openFile('r');
3✔
81
        $content = $file->fread($file->getSize());
3✔
82
        if (false === $content) {
3✔
UNCOV
83
            throw new \RuntimeException('Cannot read example file.');
×
84
        }
85
        $tokens = Tokens::fromCode($content);
3✔
86

87
        $generalPhpdocAnnotationRemoveFixer = new GeneralPhpdocAnnotationRemoveFixer();
3✔
88
        $generalPhpdocAnnotationRemoveFixer->configure([
3✔
89
            'annotations' => [
3✔
90
                'implements',
3✔
91
                'phpstan-type',
3✔
92
            ],
3✔
93
        ]);
3✔
94
        $generalPhpdocAnnotationRemoveFixer->applyFix($fileInfo, $tokens);
3✔
95

96
        return new FixerDefinition(
3✔
97
            'Configurable Fixers must declare Template type.',
3✔
98
            [
3✔
99
                new CodeSample(
3✔
100
                    $tokens->generateCode(),
3✔
101
                ),
3✔
102
            ],
3✔
103
            null,
3✔
104
            'This rule auto-adjust @implements and @phpstan-type, which heavily change information for SCA.',
3✔
105
        );
3✔
106
    }
107

108
    public function isCandidate(Tokens $tokens): bool
109
    {
110
        return $tokens->isTokenKindFound(\T_CLASS);
1✔
111
    }
112

113
    public function isRisky(): bool
114
    {
115
        return true;
1✔
116
    }
117

118
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
119
    {
120
        $this->applyFixForSrc($file, $tokens);
1✔
121
        $this->applyFixForTest($file, $tokens);
1✔
122
    }
123

124
    private function applyFixForTest(\SplFileInfo $file, Tokens $tokens): void
125
    {
126
        if (!$this->isTestForFixerFile($file)) {
1✔
127
            return;
1✔
128
        }
129

UNCOV
130
        $classIndex = $tokens->getNextTokenOfKind(0, [[\T_CLASS]]);
×
131

132
        $docBlockIndex = $this->getDocBlockIndex($tokens, $classIndex);
×
133
        if (!$tokens[$docBlockIndex]->isGivenKind(\T_DOC_COMMENT)) {
×
134
            $docBlockIndex = $tokens->getNextMeaningfulToken($docBlockIndex);
×
135
            $tokens->insertAt($docBlockIndex, [
×
136
                new Token([\T_DOC_COMMENT, "/**\n */"]),
×
137
                new Token([\T_WHITESPACE, "\n"]),
×
UNCOV
138
            ]);
×
139
        }
140

141
        $doc = new DocBlock($tokens[$docBlockIndex]->getContent());
×
142
        if (!$doc->isMultiLine()) {
×
UNCOV
143
            throw new \RuntimeException('Non-multiline docblock not expected, please convert it manually!');
×
144
        }
145

146
        $covers = array_map(
×
147
            static function ($annotation): string {
×
UNCOV
148
                $parts = explode(' ', $annotation->getContent());
×
149

150
                return trim(array_pop($parts));
×
151
            },
×
152
            $doc->getAnnotationsOfType(['covers']),
×
153
        );
×
154
        $covers = array_filter(
×
155
            $covers,
×
156
            static fn (string $className): bool => !str_contains($className, '\Abstract') && str_ends_with($className, 'Fixer'),
×
UNCOV
157
        );
×
158

159
        if (1 !== \count($covers)) {
×
UNCOV
160
            throw new \RuntimeException('Non-single covers annotation, please handle manually!');
×
161
        }
162

UNCOV
163
        $fixerName = array_pop($covers);
×
164

165
        $allowedBaseClasses = [
×
166
            AbstractDoctrineAnnotationFixerTestCase::class,
×
167
            AbstractNullableTypeDeclarationFixerTestCase::class,
×
168
            AbstractFixerTestCase::class,
×
UNCOV
169
        ];
×
170

171
        $currentClassName = str_replace('\PhpCsFixer', '\PhpCsFixer\Tests', $fixerName).'Test';
×
172
        $baseClassName = false;
×
173
        while (true) {
×
174
            $baseClassName = get_parent_class($currentClassName);
×
175
            if (false === $baseClassName || \in_array($baseClassName, $allowedBaseClasses, true)) {
×
UNCOV
176
                break;
×
177
            }
UNCOV
178
            $currentClassName = $baseClassName;
×
179
        }
180

181
        if (false === $baseClassName) {
×
UNCOV
182
            throw new \RuntimeException('Cannot find valid parent class!');
×
183
        }
184

UNCOV
185
        $baseClassName = self::getShortClassName($baseClassName);
×
186

187
        $expectedAnnotation = \sprintf('extends %s<%s>', $baseClassName, $fixerName);
×
188
        $expectedAnnotationPresent = false;
×
189
        $expectedTypeImport = \sprintf('phpstan-import-type _AutogeneratedInputConfiguration from %s', $fixerName);
×
UNCOV
190
        $expectedTypeImportPresent = false;
×
191

192
        foreach ($doc->getAnnotationsOfType(['extends']) as $annotation) {
×
193
            $annotationContent = $annotation->getContent();
×
UNCOV
194
            Preg::match('#^.*?(?P<annotation>@extends\s+?(?P<class>\w+)\<[^>]+?\>\S*)\s*?$#s', $annotationContent, $matches);
×
195

196
            if (
UNCOV
197
                ($matches['class'] ?? '') === $baseClassName
×
198
            ) {
199
                if (($matches['annotation'] ?? '') !== '@'.$expectedAnnotation) {
×
200
                    $annotationStart = $annotation->getStart();
×
201
                    $annotation->remove();
×
UNCOV
202
                    $doc->getLine($annotationStart)->setContent(' * @'.$expectedAnnotation."\n");
×
203
                }
UNCOV
204
                $expectedAnnotationPresent = true;
×
205

UNCOV
206
                break;
×
207
            }
208
        }
209

210
        $implements = class_implements($fixerName);
×
211
        if (isset($implements[ConfigurableFixerInterface::class])) {
×
212
            foreach ($doc->getAnnotationsOfType(['phpstan-import-type']) as $annotation) {
×
UNCOV
213
                $annotationContent = $annotation->getContent();
×
214

UNCOV
215
                Preg::match('#^.*?(@'.preg_quote($expectedTypeImport, '\\').')\s*?$#s', $annotationContent, $matches);
×
216

217
                if ([] !== $matches) {
×
UNCOV
218
                    $expectedTypeImportPresent = true;
×
219
                }
220
            }
221
        } else {
UNCOV
222
            $expectedTypeImportPresent = true;
×
223
        }
224

225
        if (!$expectedAnnotationPresent || !$expectedTypeImportPresent) {
×
226
            $lines = $doc->getLines();
×
227
            $lastLine = end($lines);
×
UNCOV
228
            \assert(false !== $lastLine);
×
229

230
            $lastLine->setContent(
×
231
                ''
×
232
                .(!$expectedAnnotationPresent ? ' * @'.$expectedAnnotation."\n" : '')
×
233
                .(!$expectedTypeImportPresent ? ' * @'.$expectedTypeImport."\n" : '')
×
234
                .$lastLine->getContent(),
×
UNCOV
235
            );
×
236
        }
237

UNCOV
238
        $tokens[$docBlockIndex] = new Token([\T_DOC_COMMENT, $doc->getContent()]);
×
239
    }
240

241
    private function applyFixForSrc(\SplFileInfo $file, Tokens $tokens): void
242
    {
243
        if ($file instanceof StdinFileInfo) {
1✔
244
            $file = $this->getExampleFixerFile();
1✔
245
        }
246

247
        $fixer = $this->getFixerForSrcFile($file);
1✔
248

249
        if (null === $fixer || !$fixer instanceof ConfigurableFixerInterface) {
1✔
UNCOV
250
            return;
×
251
        }
252

253
        $optionTypeInput = [];
1✔
254
        $optionTypeComputed = [];
1✔
255

256
        $configurationDefinition = $fixer->getConfigurationDefinition();
1✔
257
        foreach ($configurationDefinition->getOptions() as $option) {
1✔
258
            $optionName = $option->getName();
1✔
259
            $optionExistsAfterNormalization = true;
1✔
260
            $allowed = HelpCommand::getDisplayableAllowedValues($option);
1✔
261
            $allowedAfterNormalization = null;
1✔
262

263
            // manual handling of normalization
264
            if (null !== $option->getNormalizer()) {
1✔
265
                if ($fixer instanceof PhpdocOrderByValueFixer && 'annotations' === $optionName) {
×
266
                    $allowedAfterNormalization = 'array{'
×
267
                        .implode(
×
268
                            ', ',
×
269
                            array_map(
×
270
                                static fn ($value): string => \sprintf("'%s'?: '%s'", $value, strtolower($value)),
×
271
                                $allowed[0]->getAllowedValues(),
×
272
                            ),
×
273
                        )
×
UNCOV
274
                        .'}';
×
275
                } elseif ($fixer instanceof HeaderCommentFixer && \in_array($optionName, ['header', 'validator'], true)) {
×
276
                    // nothing to do
277
                } elseif ($fixer instanceof BlankLinesBeforeNamespaceFixer && \in_array($optionName, ['min_line_breaks', 'max_line_breaks'], true)) {
×
278
                    // nothing to do
279
                } elseif ($fixer instanceof PhpdocReturnSelfReferenceFixer && 'replacements' === $optionName) {
×
280
                    // nothing to do
281
                } elseif ($fixer instanceof GeneralPhpdocTagRenameFixer && 'replacements' === $optionName) {
×
282
                    // nothing to do
283
                } elseif ($fixer instanceof NoBreakCommentFixer && 'comment_text' === $optionName) {
×
284
                    // nothing to do
285
                } elseif ($fixer instanceof TrailingCommaInMultilineFixer && 'elements' === $optionName) {
×
286
                    // nothing to do
287
                } elseif ($fixer instanceof OrderedAttributesFixer && 'sort_algorithm' === $optionName) {
×
288
                    // nothing to do
289
                } elseif ($fixer instanceof OrderedAttributesFixer && 'order' === $optionName) {
×
290
                    $allowedAfterNormalization = 'array<string, int>';
×
291
                } elseif ($fixer instanceof FinalInternalClassFixer && \in_array($optionName, ['annotation_include', 'annotation_exclude', 'include', 'exclude'], true)) {
×
UNCOV
292
                    $allowedAfterNormalization = 'array<string, string>';
×
293
                } elseif ($fixer instanceof PhpdocTagTypeFixer && 'tags' === $optionName) {
×
294
                    // nothing to do
UNCOV
295
                } elseif ($fixer instanceof OrderedImportsFixer && 'sort_algorithm' === $optionName) {
×
296
                    // nothing to do
UNCOV
297
                } elseif ($fixer instanceof DeclareStrictTypesFixer && 'preserve_existing_declaration' === $optionName) {
×
UNCOV
298
                    $optionExistsAfterNormalization = false;
×
299
                } else {
UNCOV
300
                    throw new \LogicException(\sprintf('How to handle normalized types of "%s.%s" [`%s`]? Explicit instructions needed!', $fixer->getName(), $optionName, \get_class($fixer)));
×
301
                }
302
            }
303

304
            if (\is_array($allowed)) {
1✔
305
                // $allowed are allowed values
306
                $allowed = array_map(
1✔
307
                    static fn ($value): string => $value instanceof AllowedValueSubset
1✔
UNCOV
308
                        ? \sprintf('list<%s>', implode('|', array_map(static fn ($val) => "'".$val."'", $value->getAllowedValues())))
×
309
                        : Utils::toString($value),
1✔
310
                    $allowed,
1✔
311
                );
1✔
312
            } else {
313
                // $allowed will be allowed types
UNCOV
314
                $allowed = array_map(
×
UNCOV
315
                    static fn ($value): string => Utils::convertArrayTypeToList($value),
×
UNCOV
316
                    $option->getAllowedTypes(),
×
UNCOV
317
                );
×
318
            }
319

320
            sort($allowed);
1✔
321
            $allowed = implode('|', $allowed);
1✔
322

323
            if ('array' === $allowed) {
1✔
324
                $default = $option->getDefault();
×
325
                $getTypes = static fn ($values): array => array_unique(array_map(
×
326
                    static fn ($val) => \gettype($val),
×
327
                    $values,
×
328
                ));
×
329
                $defaultKeyTypes = $getTypes(array_keys($default));
×
330
                $defaultValueTypes = $getTypes(array_values($default));
×
331
                $allowed = \sprintf(
×
UNCOV
332
                    'array<%s, %s>',
×
UNCOV
333
                    [] !== $defaultKeyTypes ? implode('|', $defaultKeyTypes) : 'array-key',
×
UNCOV
334
                    [] !== $defaultValueTypes ? implode('|', $defaultValueTypes) : 'mixed',
×
UNCOV
335
                );
×
336
            }
337

338
            $optionTypeInput[] = \sprintf('%s%s: %s', $optionName, $option->hasDefault() ? '?' : '', $allowed);
1✔
339
            if (true === $optionExistsAfterNormalization) {
1✔
340
                $optionTypeComputed[] = \sprintf('%s: %s', $optionName, $allowedAfterNormalization ?? $allowed);
1✔
341
            }
342
        }
343

344
        $expectedTemplateTypeInputAnnotation = \sprintf("phpstan-type _AutogeneratedInputConfiguration array{\n *  %s,\n * }", implode(",\n *  ", $optionTypeInput));
1✔
345
        $expectedTemplateTypeComputedAnnotation = \sprintf("phpstan-type _AutogeneratedComputedConfiguration array{\n *  %s,\n * }", implode(",\n *  ", $optionTypeComputed));
1✔
346
        $expectedImplementsWithTypesAnnotation = 'implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>';
1✔
347

348
        $classIndex = $tokens->getNextTokenOfKind(0, [[\T_CLASS]]);
1✔
349

350
        $docBlockIndex = $this->getDocBlockIndex($tokens, $classIndex);
1✔
351
        if (!$tokens[$docBlockIndex]->isGivenKind(\T_DOC_COMMENT)) {
1✔
UNCOV
352
            $docBlockIndex = $tokens->getNextMeaningfulToken($docBlockIndex);
×
UNCOV
353
            $tokens->insertAt($docBlockIndex, [
×
UNCOV
354
                new Token([\T_DOC_COMMENT, "/**\n */"]),
×
355
                new Token([\T_WHITESPACE, "\n"]),
×
UNCOV
356
            ]);
×
357
        }
358

359
        $doc = new DocBlock($tokens[$docBlockIndex]->getContent());
1✔
360
        if (!$doc->isMultiLine()) {
1✔
UNCOV
361
            throw new \RuntimeException('Non-multiline docblock not expected, please convert it manually!');
×
362
        }
363

364
        $templateTypeInputPresent = false;
1✔
365
        $templateTypeComputedPresent = false;
1✔
366
        $implementsWithTypesPresent = false;
1✔
367

368
        foreach ($doc->getAnnotationsOfType(['phpstan-type']) as $annotation) {
1✔
UNCOV
369
            $annotationContent = $annotation->getContent();
×
370
            $matches = [];
×
371
            Preg::match('#^.*?(?P<annotation>@phpstan-type\s+?(?P<typeName>.+?)\s+?(?P<typeContent>.+?))\s*?$#s', $annotationContent, $matches);
×
372

373
            if (
UNCOV
374
                ($matches['typeName'] ?? '') === '_AutogeneratedInputConfiguration'
×
375
            ) {
376
                if (($matches['annotation'] ?? '') !== '@'.$expectedTemplateTypeInputAnnotation) {
×
UNCOV
377
                    $annotationStart = $annotation->getStart();
×
378
                    $annotation->remove();
×
UNCOV
379
                    $doc->getLine($annotationStart)->setContent(' * @'.$expectedTemplateTypeInputAnnotation."\n");
×
380
                }
381

382
                $templateTypeInputPresent = true;
×
383

384
                continue;
×
385
            }
386

387
            if (
UNCOV
388
                ($matches['typeName'] ?? '') === '_AutogeneratedComputedConfiguration'
×
389
            ) {
390
                if (($matches['annotation'] ?? '') !== '@'.$expectedTemplateTypeComputedAnnotation) {
×
UNCOV
391
                    $annotationStart = $annotation->getStart();
×
392
                    $annotation->remove();
×
UNCOV
393
                    $doc->getLine($annotationStart)->setContent(' * @'.$expectedTemplateTypeComputedAnnotation."\n");
×
394
                }
395

UNCOV
396
                $templateTypeComputedPresent = true;
×
397

398
                continue;
×
399
            }
400
        }
401

402
        foreach ($doc->getAnnotationsOfType(['implements']) as $annotation) {
1✔
403
            $annotationContent = $annotation->getContent();
×
404
            Preg::match('#^.*?(?P<annotation>@implements\s+?(?P<class>\w+)\<[^>]+?\>\S*)\s*?$#s', $annotationContent, $matches);
×
405

406
            if (
UNCOV
407
                ($matches['class'] ?? '') === 'ConfigurableFixerInterface'
×
408
            ) {
UNCOV
409
                if (($matches['annotation'] ?? '') !== '@'.$expectedImplementsWithTypesAnnotation) {
×
410
                    $annotationStart = $annotation->getStart();
×
UNCOV
411
                    $annotation->remove();
×
UNCOV
412
                    $doc->getLine($annotationStart)->setContent(' * @'.$expectedImplementsWithTypesAnnotation."\n");
×
413
                }
UNCOV
414
                $implementsWithTypesPresent = true;
×
415

UNCOV
416
                break;
×
417
            }
418
        }
419

420
        if (!$templateTypeInputPresent || !$templateTypeComputedPresent || !$implementsWithTypesPresent) {
1✔
421
            $lines = $doc->getLines();
1✔
422
            $lastLine = end($lines);
1✔
423
            \assert(false !== $lastLine);
1✔
424

425
            $lastLine->setContent(
1✔
426
                ''
1✔
427
                .(!$templateTypeInputPresent ? ' * @'.$expectedTemplateTypeInputAnnotation."\n" : '')
1✔
428
                .(!$templateTypeComputedPresent ? ' * @'.$expectedTemplateTypeComputedAnnotation."\n" : '')
1✔
429
                .(!$implementsWithTypesPresent ? ' * @'.$expectedImplementsWithTypesAnnotation."\n" : '')
1✔
430
                .$lastLine->getContent(),
1✔
431
            );
1✔
432
        }
433

434
        $tokens[$docBlockIndex] = new Token([\T_DOC_COMMENT, $doc->getContent()]);
1✔
435
    }
436

437
    private function getDocBlockIndex(Tokens $tokens, int $index): int
438
    {
439
        do {
440
            $index = $tokens->getPrevNonWhitespace($index);
1✔
441

442
            if ($tokens[$index]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
1✔
UNCOV
443
                $index = $tokens->getPrevTokenOfKind($index, [[\T_ATTRIBUTE]]);
×
444
            }
445
        } while ($tokens[$index]->isGivenKind(self::MODIFIERS));
1✔
446

447
        return $index;
1✔
448
    }
449

450
    private function getExampleFixerFile(): \SplFileInfo
451
    {
452
        $reflection = new \ReflectionClass(ConstantCaseFixer::class);
3✔
453
        $fileName = $reflection->getFileName();
3✔
454
        if (false === $fileName) {
3✔
UNCOV
455
            throw new \RuntimeException('Cannot read example fileName.');
×
456
        }
457

458
        return new \SplFileInfo($fileName);
3✔
459
    }
460

461
    private static function getShortClassName(string $longClassName): string
462
    {
UNCOV
463
        return \array_slice(explode('\\', $longClassName), -1)[0];
×
464
    }
465

466
    private function isTestForFixerFile(\SplFileInfo $file): bool
467
    {
468
        $basename = $file->getBasename('.php');
1✔
469

470
        return str_ends_with($basename, 'FixerTest')
1✔
471
            && !\in_array($basename, [
1✔
472
                self::getShortClassName(AbstractFunctionReferenceFixerTest::class),
1✔
473
                self::getShortClassName(AbstractFixerTest::class),
1✔
474
                self::getShortClassName(AbstractProxyFixerTest::class),
1✔
475
            ], true);
1✔
476
    }
477

478
    private function getFixerForSrcFile(\SplFileInfo $file): ?FixerInterface
479
    {
480
        $basename = $file->getBasename('.php');
1✔
481

482
        if (!str_ends_with($basename, 'Fixer')) {
1✔
UNCOV
483
            return null;
×
484
        }
485

486
        Preg::match('#.+src/(.+)\.php#', $file->getPathname(), $matches);
1✔
487
        if (!isset($matches[1])) {
1✔
UNCOV
488
            return null;
×
489
        }
490

491
        $className = 'PhpCsFixer\\'.str_replace('/', '\\', $matches[1]);
1✔
492

493
        $implements = class_implements($className);
1✔
494
        if (false === $implements || !isset($implements[ConfigurableFixerInterface::class])) {
1✔
UNCOV
495
            return null;
×
496
        }
497

498
        if (AbstractPhpdocToTypeDeclarationFixer::class === $className) {
1✔
UNCOV
499
            return new class extends AbstractPhpdocToTypeDeclarationFixer {
×
500
                protected function isSkippedType(string $type): bool
501
                {
UNCOV
502
                    throw new \LogicException('Not implemented.');
×
503
                }
504

505
                protected function createTokensFromRawType(string $type): Tokens
506
                {
UNCOV
507
                    throw new \LogicException('Not implemented.');
×
508
                }
509

510
                public function getDefinition(): FixerDefinitionInterface
511
                {
UNCOV
512
                    throw new \LogicException('Not implemented.');
×
513
                }
514

515
                protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
516
                {
UNCOV
517
                    throw new \LogicException('Not implemented.');
×
518
                }
519

520
                public function isCandidate(Tokens $tokens): bool
521
                {
UNCOV
522
                    throw new \LogicException('Not implemented.');
×
523
                }
UNCOV
524
            };
×
525
        } elseif (AbstractDoctrineAnnotationFixer::class === $className) {
1✔
UNCOV
526
            return new class extends AbstractDoctrineAnnotationFixer {
×
527
                protected function isSkippedType(string $type): bool
528
                {
UNCOV
529
                    throw new \LogicException('Not implemented.');
×
530
                }
531

532
                protected function createTokensFromRawType(string $type): Tokens
533
                {
UNCOV
534
                    throw new \LogicException('Not implemented.');
×
535
                }
536

537
                public function getDefinition(): FixerDefinitionInterface
538
                {
UNCOV
539
                    throw new \LogicException('Not implemented.');
×
540
                }
541

542
                protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
543
                {
UNCOV
544
                    throw new \LogicException('Not implemented.');
×
545
                }
546

547
                public function isCandidate(Tokens $tokens): bool
548
                {
549
                    throw new \LogicException('Not implemented.');
×
550
                }
551

552
                public function configure(array $configuration): void
553
                {
554
                    // void
UNCOV
555
                }
×
556

557
                protected function fixAnnotations(DoctrineAnnotationTokens $doctrineAnnotationTokens): void
558
                {
UNCOV
559
                    throw new \LogicException('Not implemented.');
×
560
                }
561

562
                public function getConfigurationDefinition(): FixerConfigurationResolverInterface
563
                {
UNCOV
564
                    return $this->createConfigurationDefinition();
×
565
                }
UNCOV
566
            };
×
567
        }
568

569
        $fixer = new $className();
1✔
570

571
        \assert($fixer instanceof FixerInterface);
1✔
572

573
        return $fixer;
1✔
574
    }
575
}
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