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

keradus / PHP-CS-Fixer / 17253322895

26 Aug 2025 11:52PM UTC coverage: 94.753% (+0.008%) from 94.745%
17253322895

push

github

keradus
add to git-blame-ignore-revs

28316 of 29884 relevant lines covered (94.75%)

45.64 hits per line

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

38.04
/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\FixerConfiguration\AllowedValueSubset;
40
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
41
use PhpCsFixer\FixerDefinition\CodeSample;
42
use PhpCsFixer\FixerDefinition\FixerDefinition;
43
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
44
use PhpCsFixer\Preg;
45
use PhpCsFixer\StdinFileInfo;
46
use PhpCsFixer\Tests\AbstractDoctrineAnnotationFixerTestCase;
47
use PhpCsFixer\Tests\AbstractFixerTest;
48
use PhpCsFixer\Tests\AbstractFunctionReferenceFixerTest;
49
use PhpCsFixer\Tests\AbstractProxyFixerTest;
50
use PhpCsFixer\Tests\Fixer\Whitespace\AbstractNullableTypeDeclarationFixerTestCase;
51
use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
52
use PhpCsFixer\Tokenizer\CT;
53
use PhpCsFixer\Tokenizer\FCT;
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
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
66
 */
67
final class ConfigurableFixerTemplateFixer extends AbstractFixer implements InternalFixerInterface
68
{
69
    private const MODIFIERS = [\T_PUBLIC, \T_PROTECTED, \T_PRIVATE, \T_FINAL, \T_ABSTRACT, \T_COMMENT, FCT::T_ATTRIBUTE, FCT::T_READONLY];
70

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

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

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

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

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

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

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

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

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

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

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

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

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

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

162
        $fixerName = array_pop($covers);
×
163

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

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

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

184
        $baseClassName = self::getShortClassName($baseClassName);
×
185

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

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

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

205
                break;
×
206
            }
207
        }
208

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

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

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

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

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

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

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

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

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

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

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

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

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

316
            sort($allowed);
1✔
317
            $allowed = implode('|', $allowed);
1✔
318

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

334
            $optionTypeInput[] = \sprintf('%s%s: %s', $optionName, $option->hasDefault() ? '?' : '', $allowed);
1✔
335
            $optionTypeComputed[] = \sprintf('%s: %s', $optionName, $allowedAfterNormalization ?? $allowed);
1✔
336
        }
337

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

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

344
        $docBlockIndex = $this->getDocBlockIndex($tokens, $classIndex);
1✔
345
        if (!$tokens[$docBlockIndex]->isGivenKind(\T_DOC_COMMENT)) {
1✔
346
            $docBlockIndex = $tokens->getNextMeaningfulToken($docBlockIndex);
×
347
            $tokens->insertAt($docBlockIndex, [
×
348
                new Token([\T_DOC_COMMENT, "/**\n */"]),
×
349
                new Token([\T_WHITESPACE, "\n"]),
×
350
            ]);
×
351
        }
352

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

358
        $templateTypeInputPresent = false;
1✔
359
        $templateTypeComputedPresent = false;
1✔
360
        $implementsWithTypesPresent = false;
1✔
361

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

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

376
                $templateTypeInputPresent = true;
×
377

378
                continue;
×
379
            }
380

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

390
                $templateTypeComputedPresent = true;
×
391

392
                continue;
×
393
            }
394
        }
395

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

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

410
                break;
×
411
            }
412
        }
413

414
        if (!$templateTypeInputPresent || !$templateTypeComputedPresent || !$implementsWithTypesPresent) {
1✔
415
            $lines = $doc->getLines();
1✔
416
            $lastLine = end($lines);
1✔
417
            \assert(false !== $lastLine);
1✔
418

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

428
        $tokens[$docBlockIndex] = new Token([\T_DOC_COMMENT, $doc->getContent()]);
1✔
429
    }
430

431
    private function getDocBlockIndex(Tokens $tokens, int $index): int
432
    {
433
        do {
434
            $index = $tokens->getPrevNonWhitespace($index);
1✔
435

436
            if ($tokens[$index]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
1✔
437
                $index = $tokens->getPrevTokenOfKind($index, [[\T_ATTRIBUTE]]);
×
438
            }
439
        } while ($tokens[$index]->isGivenKind(self::MODIFIERS));
1✔
440

441
        return $index;
1✔
442
    }
443

444
    private function getExampleFixerFile(): \SplFileInfo
445
    {
446
        $reflection = new \ReflectionClass(ConstantCaseFixer::class);
3✔
447
        $fileName = $reflection->getFileName();
3✔
448
        if (false === $fileName) {
3✔
449
            throw new \RuntimeException('Cannot read example fileName.');
×
450
        }
451

452
        return new \SplFileInfo($fileName);
3✔
453
    }
454

455
    private static function getShortClassName(string $longClassName): string
456
    {
457
        return \array_slice(explode('\\', $longClassName), -1)[0];
×
458
    }
459

460
    private function isTestForFixerFile(\SplFileInfo $file): bool
461
    {
462
        $basename = $file->getBasename('.php');
1✔
463

464
        return str_ends_with($basename, 'FixerTest')
1✔
465
            && !\in_array($basename, [
1✔
466
                self::getShortClassName(AbstractFunctionReferenceFixerTest::class),
1✔
467
                self::getShortClassName(AbstractFixerTest::class),
1✔
468
                self::getShortClassName(AbstractProxyFixerTest::class),
1✔
469
            ], true);
1✔
470
    }
471

472
    private function getFixerForSrcFile(\SplFileInfo $file): ?FixerInterface
473
    {
474
        $basename = $file->getBasename('.php');
1✔
475

476
        if (!str_ends_with($basename, 'Fixer')) {
1✔
477
            return null;
×
478
        }
479

480
        Preg::match('#.+src/(.+)\.php#', $file->getPathname(), $matches);
1✔
481
        if (!isset($matches[1])) {
1✔
482
            return null;
×
483
        }
484

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

487
        $implements = class_implements($className);
1✔
488
        if (false === $implements || !isset($implements[ConfigurableFixerInterface::class])) {
1✔
489
            return null;
×
490
        }
491

492
        if (AbstractPhpdocToTypeDeclarationFixer::class === $className) {
1✔
493
            return new class extends AbstractPhpdocToTypeDeclarationFixer {
×
494
                protected function isSkippedType(string $type): bool
495
                {
496
                    throw new \LogicException('Not implemented.');
×
497
                }
498

499
                protected function createTokensFromRawType(string $type): Tokens
500
                {
501
                    throw new \LogicException('Not implemented.');
×
502
                }
503

504
                public function getDefinition(): FixerDefinitionInterface
505
                {
506
                    throw new \LogicException('Not implemented.');
×
507
                }
508

509
                protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
510
                {
511
                    throw new \LogicException('Not implemented.');
×
512
                }
513

514
                public function isCandidate(Tokens $tokens): bool
515
                {
516
                    throw new \LogicException('Not implemented.');
×
517
                }
518
            };
×
519
        } elseif (AbstractDoctrineAnnotationFixer::class === $className) {
1✔
520
            return new class extends AbstractDoctrineAnnotationFixer {
×
521
                protected function isSkippedType(string $type): bool
522
                {
523
                    throw new \LogicException('Not implemented.');
×
524
                }
525

526
                protected function createTokensFromRawType(string $type): Tokens
527
                {
528
                    throw new \LogicException('Not implemented.');
×
529
                }
530

531
                public function getDefinition(): FixerDefinitionInterface
532
                {
533
                    throw new \LogicException('Not implemented.');
×
534
                }
535

536
                protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
537
                {
538
                    throw new \LogicException('Not implemented.');
×
539
                }
540

541
                public function isCandidate(Tokens $tokens): bool
542
                {
543
                    throw new \LogicException('Not implemented.');
×
544
                }
545

546
                public function configure(array $configuration): void
547
                {
548
                    // void
549
                }
×
550

551
                protected function fixAnnotations(DoctrineAnnotationTokens $doctrineAnnotationTokens): void
552
                {
553
                    throw new \LogicException('Not implemented.');
×
554
                }
555

556
                public function getConfigurationDefinition(): FixerConfigurationResolverInterface
557
                {
558
                    return $this->createConfigurationDefinition();
×
559
                }
560
            };
×
561
        }
562

563
        $fixer = new $className();
1✔
564

565
        \assert($fixer instanceof FixerInterface);
1✔
566

567
        return $fixer;
1✔
568
    }
569
}
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