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

keradus / PHP-CS-Fixer / 16018263876

02 Jul 2025 06:58AM UTC coverage: 94.846% (-0.002%) from 94.848%
16018263876

push

github

keradus
debug2

28193 of 29725 relevant lines covered (94.85%)

45.34 hits per line

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

38.27
/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
final class ConfigurableFixerTemplateFixer extends AbstractFixer implements InternalFixerInterface
66
{
67
    private const MODIFIERS = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL, T_ABSTRACT, T_COMMENT, FCT::T_ATTRIBUTE, FCT::T_READONLY];
68

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

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

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

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

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

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

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

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

127
        $classIndex = $tokens->getNextTokenOfKind(0, [[T_CLASS]]);
×
128

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

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

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

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

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

160
        $fixerName = array_pop($covers);
×
161

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

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

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

182
        $baseClassName = self::getShortClassName($baseClassName);
×
183

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

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

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

203
                break;
×
204
            }
205
        }
206

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

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

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

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

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

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

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

244
        $fixer = $this->getFixerForSrcFile($file);
1✔
245

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

250
        $optionTypeInput = [];
1✔
251
        $optionTypeComputed = [];
1✔
252

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

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

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

314
            sort($allowed);
1✔
315
            $allowed = implode('|', $allowed);
1✔
316

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

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

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

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

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

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

356
        $templateTypeInputPresent = false;
1✔
357
        $templateTypeComputedPresent = false;
1✔
358
        $implementsWithTypesPresent = false;
1✔
359

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

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

374
                $templateTypeInputPresent = true;
×
375

376
                continue;
×
377
            }
378

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

388
                $templateTypeComputedPresent = true;
×
389

390
                continue;
×
391
            }
392
        }
393

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

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

408
                break;
×
409
            }
410
        }
411

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

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

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

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

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

439
        return $index;
1✔
440
    }
441

442
    private function isPHPDoc(Tokens $tokens, int $index): bool
443
    {
444
        return $tokens[$index]->isGivenKind(T_DOC_COMMENT);
1✔
445
    }
446

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

455
        return new \SplFileInfo($fileName);
3✔
456
    }
457

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

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

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

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

479
        if (!str_ends_with($basename, 'Fixer')) {
1✔
480
            return null;
×
481
        }
482

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

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

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

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

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

507
                public function getDefinition(): FixerDefinitionInterface
508
                {
509
                    throw new \LogicException('Not implemented.');
×
510
                }
511

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

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

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

534
                public function getDefinition(): FixerDefinitionInterface
535
                {
536
                    throw new \LogicException('Not implemented.');
×
537
                }
538

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

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

549
                public function configure(array $configuration): void
550
                {
551
                    // void
552
                }
×
553

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

559
                public function getConfigurationDefinition(): FixerConfigurationResolverInterface
560
                {
561
                    return $this->createConfigurationDefinition();
×
562
                }
563
            };
×
564
        }
565

566
        $fixer = new $className();
1✔
567

568
        \assert($fixer instanceof FixerInterface);
1✔
569

570
        return $fixer;
1✔
571
    }
572
}
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