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

keradus / PHP-CS-Fixer / 16999983712

15 Aug 2025 09:42PM UTC coverage: 94.75% (-0.09%) from 94.839%
16999983712

push

github

keradus
ci: more self-fixing checks on lowest/highest PHP

28263 of 29829 relevant lines covered (94.75%)

45.88 hits per line

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

98.68
/src/Fixer/PhpUnit/PhpUnitDataProviderNameFixer.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of PHP CS Fixer.
7
 *
8
 * (c) Fabien Potencier <fabien@symfony.com>
9
 *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14

15
namespace PhpCsFixer\Fixer\PhpUnit;
16

17
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
18
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
19
use PhpCsFixer\Fixer\ConfigurableFixerTrait;
20
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
21
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
22
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
23
use PhpCsFixer\FixerDefinition\CodeSample;
24
use PhpCsFixer\FixerDefinition\FixerDefinition;
25
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
26
use PhpCsFixer\Preg;
27
use PhpCsFixer\Tokenizer\Analyzer\DataProviderAnalyzer;
28
use PhpCsFixer\Tokenizer\FCT;
29
use PhpCsFixer\Tokenizer\Token;
30
use PhpCsFixer\Tokenizer\Tokens;
31

32
/**
33
 * @phpstan-type _AutogeneratedInputConfiguration array{
34
 *  prefix?: string,
35
 *  suffix?: string,
36
 * }
37
 * @phpstan-type _AutogeneratedComputedConfiguration array{
38
 *  prefix: string,
39
 *  suffix: string,
40
 * }
41
 *
42
 * @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
43
 *
44
 * @author Kuba Werłos <werlos@gmail.com>
45
 */
46
final class PhpUnitDataProviderNameFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface
47
{
48
    /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
49
    use ConfigurableFixerTrait;
50

51
    public function getDefinition(): FixerDefinitionInterface
52
    {
53
        return new FixerDefinition(
3✔
54
            'Data provider names must match the name of the test.',
3✔
55
            [
3✔
56
                new CodeSample(
3✔
57
                    '<?php
3✔
58
class FooTest extends TestCase {
59
    /**
60
     * @dataProvider dataProvider
61
     */
62
    public function testSomething($expected, $actual) {}
63
    public function dataProvider() {}
64
}
65
',
3✔
66
                ),
3✔
67
                new CodeSample(
3✔
68
                    '<?php
3✔
69
class FooTest extends TestCase {
70
    /**
71
     * @dataProvider dt_prvdr_ftr
72
     */
73
    public function test_feature($expected, $actual) {}
74
    public function dt_prvdr_ftr() {}
75
}
76
',
3✔
77
                    [
3✔
78
                        'prefix' => 'data_',
3✔
79
                        'suffix' => '',
3✔
80
                    ]
3✔
81
                ),
3✔
82
                new CodeSample(
3✔
83
                    '<?php
3✔
84
class FooTest extends TestCase {
85
    /**
86
     * @dataProvider dataProviderUsedInMultipleTests
87
     */
88
    public function testA($expected, $actual) {}
89
    /**
90
     * @dataProvider dataProviderUsedInMultipleTests
91
     */
92
    public function testB($expected, $actual) {}
93
    /**
94
     * @dataProvider dataProviderUsedInSingleTest
95
     */
96
    public function testC($expected, $actual) {}
97
    /**
98
     * @dataProvider dataProviderUsedAsFirstInTest
99
     * @dataProvider dataProviderUsedAsSecondInTest
100
     */
101
    public function testD($expected, $actual) {}
102

103
    public function dataProviderUsedInMultipleTests() {}
104
    public function dataProviderUsedInSingleTest() {}
105
    public function dataProviderUsedAsFirstInTest() {}
106
    public function dataProviderUsedAsSecondInTest() {}
107
}
108
',
3✔
109
                    [
3✔
110
                        'prefix' => 'provides',
3✔
111
                        'suffix' => 'Data',
3✔
112
                    ]
3✔
113
                ),
3✔
114
            ],
3✔
115
            null,
3✔
116
            'Fixer could be risky if one is calling data provider by name as function.'
3✔
117
        );
3✔
118
    }
119

120
    public function isRisky(): bool
121
    {
122
        return true;
1✔
123
    }
124

125
    protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
126
    {
127
        return new FixerConfigurationResolver([
46✔
128
            (new FixerOptionBuilder('prefix', 'Prefix that replaces "test".'))
46✔
129
                ->setAllowedTypes(['string'])
46✔
130
                ->setDefault('provide')
46✔
131
                ->getOption(),
46✔
132
            (new FixerOptionBuilder('suffix', 'Suffix to be present at the end.'))
46✔
133
                ->setAllowedTypes(['string'])
46✔
134
                ->setDefault('Cases')
46✔
135
                ->getOption(),
46✔
136
        ]);
46✔
137
    }
138

139
    protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
140
    {
141
        $dataProviders = (new DataProviderAnalyzer())->getDataProviders($tokens, $startIndex, $endIndex);
37✔
142

143
        $methodsProviders = [];
37✔
144
        $providersMethods = [];
37✔
145
        foreach ($dataProviders as $dataProviderAnalysis) {
37✔
146
            foreach ($dataProviderAnalysis->getUsageIndices() as [$usageIndex]) {
34✔
147
                $methodIndex = $tokens->getNextTokenOfKind($usageIndex, [[\T_FUNCTION]]);
34✔
148
                $methodsProviders[$methodIndex][$dataProviderAnalysis->getName()] = $usageIndex;
34✔
149
                $providersMethods[$dataProviderAnalysis->getName()][$methodIndex] = $usageIndex;
34✔
150
            }
151
        }
152

153
        foreach ($dataProviders as $dataProviderAnalysis) {
37✔
154
            // @phpstan-ignore offsetAccess.notFound
155
            if (\count($providersMethods[$dataProviderAnalysis->getName()]) > 1) {
34✔
156
                continue;
4✔
157
            }
158

159
            $methodIndex = $tokens->getNextTokenOfKind($dataProviderAnalysis->getUsageIndices()[0][0], [[\T_FUNCTION]]);
32✔
160
            // @phpstan-ignore offsetAccess.notFound
161
            if (\count($methodsProviders[$methodIndex]) > 1) {
32✔
162
                continue;
5✔
163
            }
164

165
            $dataProviderNewName = $this->getDataProviderNameForUsageIndex($tokens, $methodIndex);
29✔
166
            if (null !== $tokens->findSequence([[\T_FUNCTION], [\T_STRING, $dataProviderNewName]], $startIndex, $endIndex)) {
29✔
167
                continue;
28✔
168
            }
169

170
            foreach ($dataProviderAnalysis->getUsageIndices() as [$usageIndex]) {
28✔
171
                $tokens[$dataProviderAnalysis->getNameIndex()] = new Token([\T_STRING, $dataProviderNewName]);
28✔
172

173
                $newContent = $tokens[$usageIndex]->isGivenKind(\T_DOC_COMMENT)
28✔
174
                    ? Preg::replace(
27✔
175
                        \sprintf('/(@dataProvider\s+)%s/', $dataProviderAnalysis->getName()),
27✔
176
                        \sprintf('$1%s', $dataProviderNewName),
27✔
177
                        $tokens[$usageIndex]->getContent(),
27✔
178
                    )
27✔
179
                    : \sprintf('%1$s%2$s%1$s', $tokens[$usageIndex]->getContent()[0], $dataProviderNewName);
2✔
180

181
                $tokens[$usageIndex] = new Token([$tokens[$usageIndex]->getId(), $newContent]);
28✔
182
            }
183
        }
184
    }
185

186
    private function getDataProviderNameForUsageIndex(Tokens $tokens, int $index): string
187
    {
188
        do {
189
            if ($tokens[$index]->isGivenKind(FCT::T_ATTRIBUTE)) {
29✔
190
                $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $index);
×
191
            }
192
            $index = $tokens->getNextMeaningfulToken($index);
29✔
193
        } while (!$tokens[$index]->isGivenKind(\T_STRING));
29✔
194

195
        $name = $tokens[$index]->getContent();
29✔
196

197
        $name = Preg::replace('/^test_*/i', '', $name);
29✔
198

199
        if ('' === $this->configuration['prefix']) {
29✔
200
            $name = lcfirst($name);
2✔
201
        } elseif ('_' !== substr($this->configuration['prefix'], -1)) {
27✔
202
            $name = ucfirst($name);
21✔
203
        }
204

205
        return $this->configuration['prefix'].$name.$this->configuration['suffix'];
29✔
206
    }
207
}
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