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

keradus / PHP-CS-Fixer / 13747606772

08 Mar 2025 10:38PM UTC coverage: 94.847% (-0.08%) from 94.929%
13747606772

push

github

web-flow
fix: `MbStrFunctionsFixer` - fix imports (#8474)

23 of 24 new or added lines in 1 file covered. (95.83%)

181 existing lines in 6 files now uncovered.

28127 of 29655 relevant lines covered (94.85%)

43.08 hits per line

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

39.36
/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\WhitespacesAwareFixerInterface;
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\Token;
55
use PhpCsFixer\Tokenizer\Tokens;
56
use PhpCsFixer\Utils;
57

58
/**
59
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
60
 *
61
 * @internal
62
 *
63
 * @warning Does not support PHPUnit attributes
64
 */
65
final class ConfigurableFixerTemplateFixer extends AbstractFixer implements InternalFixerInterface, WhitespacesAwareFixerInterface
66
{
67
    public function getName(): string
68
    {
69
        return 'PhpCsFixerInternal/'.parent::getName();
3✔
70
    }
71

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

82
        $generalPhpdocAnnotationRemoveFixer = new GeneralPhpdocAnnotationRemoveFixer();
3✔
83
        $generalPhpdocAnnotationRemoveFixer->configure([
3✔
84
            'annotations' => [
3✔
85
                'implements',
3✔
86
                'phpstan-type',
3✔
87
            ],
3✔
88
        ]);
3✔
89
        $generalPhpdocAnnotationRemoveFixer->applyFix($fileInfo, $tokens);
3✔
90

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

103
    public function isCandidate(Tokens $tokens): bool
104
    {
105
        return $tokens->isTokenKindFound(T_CLASS);
1✔
106
    }
107

108
    public function isRisky(): bool
109
    {
110
        return true;
1✔
111
    }
112

113
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
114
    {
115
        $this->applyFixForSrc($file, $tokens);
1✔
116
        $this->applyFixForTest($file, $tokens);
1✔
117
    }
118

119
    private function applyFixForTest(\SplFileInfo $file, Tokens $tokens): void
120
    {
121
        if (!$this->isTestForFixerFile($file)) {
1✔
122
            return;
1✔
123
        }
124

UNCOV
125
        $classIndex = $tokens->getNextTokenOfKind(0, [[T_CLASS]]);
×
126

127
        $docBlockIndex = $this->getDocBlockIndex($tokens, $classIndex);
×
128
        if (!$this->isPHPDoc($tokens, $docBlockIndex)) {
×
129
            $docBlockIndex = $tokens->getNextMeaningfulToken($docBlockIndex);
×
130
            $tokens->insertAt($docBlockIndex, [
×
131
                new Token([T_DOC_COMMENT, "/**\n */"]),
×
132
                new Token([T_WHITESPACE, "\n"]),
×
UNCOV
133
            ]);
×
134
        }
135

136
        $doc = new DocBlock($tokens[$docBlockIndex]->getContent());
×
137
        if (!$doc->isMultiLine()) {
×
UNCOV
138
            throw new \RuntimeException('Non-multiline docblock not expected, please convert it manually!');
×
139
        }
140

141
        $covers = array_map(
×
142
            static function ($annotation): string {
×
UNCOV
143
                $parts = explode(' ', $annotation->getContent());
×
144

145
                return trim(array_pop($parts));
×
146
            },
×
147
            $doc->getAnnotationsOfType(['covers'])
×
148
        );
×
149
        $covers = array_filter(
×
150
            $covers,
×
151
            static fn ($className): bool => !str_contains($className, '\Abstract') && str_ends_with($className, 'Fixer')
×
UNCOV
152
        );
×
153

154
        if (1 !== \count($covers)) {
×
UNCOV
155
            throw new \RuntimeException('Non-single covers annotation, please handle manually!');
×
156
        }
157

UNCOV
158
        $fixerName = array_pop($covers);
×
159

160
        $allowedBaseClasses = [
×
161
            AbstractDoctrineAnnotationFixerTestCase::class,
×
162
            AbstractNullableTypeDeclarationFixerTestCase::class,
×
163
            AbstractFixerTestCase::class,
×
UNCOV
164
        ];
×
165

166
        $currentClassName = str_replace('\PhpCsFixer', '\PhpCsFixer\Tests', $fixerName).'Test';
×
167
        $baseClassName = false;
×
168
        while (true) {
×
169
            $baseClassName = get_parent_class($currentClassName);
×
170
            if (false === $baseClassName || \in_array($baseClassName, $allowedBaseClasses, true)) {
×
UNCOV
171
                break;
×
172
            }
UNCOV
173
            $currentClassName = $baseClassName;
×
174
        }
175

176
        if (false === $baseClassName) {
×
UNCOV
177
            throw new \RuntimeException('Cannot find valid parent class!');
×
178
        }
179

UNCOV
180
        $baseClassName = self::getShortClassName($baseClassName);
×
181

182
        $expectedAnnotation = \sprintf('extends %s<%s>', $baseClassName, $fixerName);
×
183
        $expectedAnnotationPresent = false;
×
184
        $expectedTypeImport = \sprintf('phpstan-import-type _AutogeneratedInputConfiguration from %s', $fixerName);
×
UNCOV
185
        $expectedTypeImportPresent = false;
×
186

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

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

UNCOV
201
                break;
×
202
            }
203
        }
204

205
        $implements = class_implements($fixerName);
×
206
        if (isset($implements[ConfigurableFixerInterface::class])) {
×
207
            foreach ($doc->getAnnotationsOfType(['phpstan-import-type']) as $annotation) {
×
UNCOV
208
                $annotationContent = $annotation->getContent();
×
209

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

212
                if ([] !== $matches) {
×
UNCOV
213
                    $expectedTypeImportPresent = true;
×
214
                }
215
            }
216
        } else {
UNCOV
217
            $expectedTypeImportPresent = true;
×
218
        }
219

220
        if (!$expectedAnnotationPresent || !$expectedTypeImportPresent) {
×
221
            $lines = $doc->getLines();
×
222
            $lastLine = end($lines);
×
UNCOV
223
            \assert(false !== $lastLine);
×
224

225
            $lastLine->setContent(
×
226
                ''
×
227
                .(!$expectedAnnotationPresent ? ' * @'.$expectedAnnotation."\n" : '')
×
228
                .(!$expectedTypeImportPresent ? ' * @'.$expectedTypeImport."\n" : '')
×
229
                .$lastLine->getContent()
×
UNCOV
230
            );
×
231
        }
232

UNCOV
233
        $tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $doc->getContent()]);
×
234
    }
235

236
    private function applyFixForSrc(\SplFileInfo $file, Tokens $tokens): void
237
    {
238
        if ($file instanceof StdinFileInfo) {
1✔
239
            $file = $this->getExampleFixerFile();
1✔
240
        }
241

242
        $fixer = $this->getFixerForSrcFile($file);
1✔
243

244
        if (null === $fixer || !$fixer instanceof ConfigurableFixerInterface) {
1✔
UNCOV
245
            return;
×
246
        }
247

248
        $optionTypeInput = [];
1✔
249
        $optionTypeComputed = [];
1✔
250

251
        $configurationDefinition = $fixer->getConfigurationDefinition();
1✔
252
        foreach ($configurationDefinition->getOptions() as $option) {
1✔
253
            $optionName = $option->getName();
1✔
254
            $allowed = HelpCommand::getDisplayableAllowedValues($option);
1✔
255
            $allowedAfterNormalization = null;
1✔
256

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

296
            if (\is_array($allowed)) {
1✔
297
                // $allowed are allowed values
298
                $allowed = array_map(
1✔
299
                    static fn ($value): string => $value instanceof AllowedValueSubset
1✔
UNCOV
300
                        ? \sprintf('list<%s>', implode('|', array_map(static fn ($val) => "'".$val."'", $value->getAllowedValues())))
×
301
                        : Utils::toString($value),
1✔
302
                    $allowed
1✔
303
                );
1✔
304
            } else {
305
                // $allowed will be allowed types
306
                $allowed = array_map(
×
UNCOV
307
                    static fn ($value): string => Utils::convertArrayTypeToList($value),
×
UNCOV
308
                    $option->getAllowedTypes()
×
UNCOV
309
                );
×
310
            }
311

312
            sort($allowed);
1✔
313
            $allowed = implode('|', $allowed);
1✔
314

315
            if ('array' === $allowed) {
1✔
316
                $default = $option->getDefault();
×
317
                $getTypes = static fn ($values): array => array_unique(array_map(
×
318
                    static fn ($val) => \gettype($val),
×
319
                    $values
×
320
                ));
×
321
                $defaultKeyTypes = $getTypes(array_keys($default));
×
322
                $defaultValueTypes = $getTypes(array_values($default));
×
323
                $allowed = \sprintf(
×
324
                    'array<%s, %s>',
×
UNCOV
325
                    [] !== $defaultKeyTypes ? implode('|', $defaultKeyTypes) : 'array-key',
×
UNCOV
326
                    [] !== $defaultValueTypes ? implode('|', $defaultValueTypes) : 'mixed'
×
UNCOV
327
                );
×
328
            }
329

330
            $optionTypeInput[] = \sprintf('%s%s: %s', $optionName, $option->hasDefault() ? '?' : '', $allowed);
1✔
331
            $optionTypeComputed[] = \sprintf('%s: %s', $optionName, $allowedAfterNormalization ?? $allowed);
1✔
332
        }
333

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

338
        $classIndex = $tokens->getNextTokenOfKind(0, [[T_CLASS]]);
1✔
339

340
        $docBlockIndex = $this->getDocBlockIndex($tokens, $classIndex);
1✔
341
        if (!$this->isPHPDoc($tokens, $docBlockIndex)) {
1✔
342
            $docBlockIndex = $tokens->getNextMeaningfulToken($docBlockIndex);
×
343
            $tokens->insertAt($docBlockIndex, [
×
UNCOV
344
                new Token([T_DOC_COMMENT, "/**\n */"]),
×
UNCOV
345
                new Token([T_WHITESPACE, "\n"]),
×
UNCOV
346
            ]);
×
347
        }
348

349
        $doc = new DocBlock($tokens[$docBlockIndex]->getContent());
1✔
350
        if (!$doc->isMultiLine()) {
1✔
UNCOV
351
            throw new \RuntimeException('Non-multiline docblock not expected, please convert it manually!');
×
352
        }
353

354
        $templateTypeInputPresent = false;
1✔
355
        $templateTypeComputedPresent = false;
1✔
356
        $implementsWithTypesPresent = false;
1✔
357

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

363
            if (
364
                ($matches['typeName'] ?? '') === '_AutogeneratedInputConfiguration'
×
365
            ) {
366
                if (($matches['annotation'] ?? '') !== '@'.$expectedTemplateTypeInputAnnotation) {
×
UNCOV
367
                    $annotationStart = $annotation->getStart();
×
UNCOV
368
                    $annotation->remove();
×
369
                    $doc->getLine($annotationStart)->setContent(' * @'.$expectedTemplateTypeInputAnnotation."\n");
×
370
                }
371

UNCOV
372
                $templateTypeInputPresent = true;
×
373

UNCOV
374
                continue;
×
375
            }
376

377
            if (
378
                ($matches['typeName'] ?? '') === '_AutogeneratedComputedConfiguration'
×
379
            ) {
380
                if (($matches['annotation'] ?? '') !== '@'.$expectedTemplateTypeComputedAnnotation) {
×
UNCOV
381
                    $annotationStart = $annotation->getStart();
×
UNCOV
382
                    $annotation->remove();
×
383
                    $doc->getLine($annotationStart)->setContent(' * @'.$expectedTemplateTypeComputedAnnotation."\n");
×
384
                }
385

UNCOV
386
                $templateTypeComputedPresent = true;
×
387

UNCOV
388
                continue;
×
389
            }
390
        }
391

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

396
            if (
397
                ($matches['class'] ?? '') === 'ConfigurableFixerInterface'
×
398
            ) {
399
                if (($matches['annotation'] ?? '') !== '@'.$expectedImplementsWithTypesAnnotation) {
×
UNCOV
400
                    $annotationStart = $annotation->getStart();
×
401
                    $annotation->remove();
×
UNCOV
402
                    $doc->getLine($annotationStart)->setContent(' * @'.$expectedImplementsWithTypesAnnotation."\n");
×
403
                }
UNCOV
404
                $implementsWithTypesPresent = true;
×
405

UNCOV
406
                break;
×
407
            }
408
        }
409

410
        if (!$templateTypeInputPresent || !$templateTypeComputedPresent || !$implementsWithTypesPresent) {
1✔
411
            $lines = $doc->getLines();
1✔
412
            $lastLine = end($lines);
1✔
413
            \assert(false !== $lastLine);
1✔
414

415
            $lastLine->setContent(
1✔
416
                ''
1✔
417
                .(!$templateTypeInputPresent ? ' * @'.$expectedTemplateTypeInputAnnotation."\n" : '')
1✔
418
                .(!$templateTypeComputedPresent ? ' * @'.$expectedTemplateTypeComputedAnnotation."\n" : '')
1✔
419
                .(!$implementsWithTypesPresent ? ' * @'.$expectedImplementsWithTypesAnnotation."\n" : '')
1✔
420
                .$lastLine->getContent()
1✔
421
            );
1✔
422
        }
423

424
        $tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $doc->getContent()]);
1✔
425
    }
426

427
    private function getDocBlockIndex(Tokens $tokens, int $index): int
428
    {
429
        $modifiers = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL, T_ABSTRACT, T_COMMENT];
1✔
430

431
        if (\defined('T_ATTRIBUTE')) { // @TODO: drop condition when PHP 8.0+ is required
1✔
432
            $modifiers[] = T_ATTRIBUTE;
1✔
433
        }
434

435
        if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.2+ is required
1✔
436
            $modifiers[] = T_READONLY;
1✔
437
        }
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($modifiers));
1✔
446

447
        return $index;
1✔
448
    }
449

450
    private function isPHPDoc(Tokens $tokens, int $index): bool
451
    {
452
        return $tokens[$index]->isGivenKind(T_DOC_COMMENT);
1✔
453
    }
454

455
    private function getExampleFixerFile(): \SplFileInfo
456
    {
457
        $reflection = new \ReflectionClass(ConstantCaseFixer::class);
3✔
458
        $fileName = $reflection->getFileName();
3✔
459
        if (false === $fileName) {
3✔
UNCOV
460
            throw new \RuntimeException('Cannot read example fileName.');
×
461
        }
462

463
        return new \SplFileInfo($fileName);
3✔
464
    }
465

466
    private static function getShortClassName(string $longClassName): string
467
    {
UNCOV
468
        return \array_slice(explode('\\', $longClassName), -1)[0];
×
469
    }
470

471
    private function isTestForFixerFile(\SplFileInfo $file): bool
472
    {
473
        $basename = $file->getBasename('.php');
1✔
474

475
        return str_ends_with($basename, 'FixerTest')
1✔
476
            && !\in_array($basename, [
1✔
477
                self::getShortClassName(AbstractFunctionReferenceFixerTest::class),
1✔
478
                self::getShortClassName(AbstractFixerTest::class),
1✔
479
                self::getShortClassName(AbstractProxyFixerTest::class),
1✔
480
            ], true);
1✔
481
    }
482

483
    private function getFixerForSrcFile(\SplFileInfo $file): ?FixerInterface
484
    {
485
        $basename = $file->getBasename('.php');
1✔
486

487
        if (!str_ends_with($basename, 'Fixer')) {
1✔
UNCOV
488
            return null;
×
489
        }
490

491
        Preg::match('#.+src/(.+)\.php#', $file->getPathname(), $matches);
1✔
492
        if (!isset($matches[1])) {
1✔
UNCOV
493
            return null;
×
494
        }
495

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

498
        $implements = class_implements($className);
1✔
499
        if (false === $implements || !isset($implements[ConfigurableFixerInterface::class])) {
1✔
UNCOV
500
            return null;
×
501
        }
502

503
        if (AbstractPhpdocToTypeDeclarationFixer::class === $className) {
1✔
504
            return new class extends AbstractPhpdocToTypeDeclarationFixer {
×
505
                protected function isSkippedType(string $type): bool
506
                {
UNCOV
507
                    throw new \LogicException('Not implemented.');
×
508
                }
509

510
                protected function createTokensFromRawType(string $type): Tokens
511
                {
UNCOV
512
                    throw new \LogicException('Not implemented.');
×
513
                }
514

515
                public function getDefinition(): FixerDefinitionInterface
516
                {
UNCOV
517
                    throw new \LogicException('Not implemented.');
×
518
                }
519

520
                protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
521
                {
UNCOV
522
                    throw new \LogicException('Not implemented.');
×
523
                }
524

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

537
                protected function createTokensFromRawType(string $type): Tokens
538
                {
UNCOV
539
                    throw new \LogicException('Not implemented.');
×
540
                }
541

542
                public function getDefinition(): FixerDefinitionInterface
543
                {
UNCOV
544
                    throw new \LogicException('Not implemented.');
×
545
                }
546

547
                protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
548
                {
UNCOV
549
                    throw new \LogicException('Not implemented.');
×
550
                }
551

552
                public function isCandidate(Tokens $tokens): bool
553
                {
UNCOV
554
                    throw new \LogicException('Not implemented.');
×
555
                }
556

557
                public function configure(array $configuration): void
558
                {
559
                    // void
UNCOV
560
                }
×
561

562
                protected function fixAnnotations(DoctrineAnnotationTokens $doctrineAnnotationTokens): void
563
                {
UNCOV
564
                    throw new \LogicException('Not implemented.');
×
565
                }
566

567
                public function getConfigurationDefinition(): FixerConfigurationResolverInterface
568
                {
UNCOV
569
                    return $this->createConfigurationDefinition();
×
570
                }
UNCOV
571
            };
×
572
        }
573

574
        $fixer = new $className();
1✔
575

576
        \assert($fixer instanceof FixerInterface);
1✔
577

578
        return $fixer;
1✔
579
    }
580
}
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