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

keradus / PHP-CS-Fixer / 17319949156

29 Aug 2025 09:20AM UTC coverage: 94.696% (-0.05%) from 94.744%
17319949156

push

github

keradus
CS

28333 of 29920 relevant lines covered (94.7%)

45.63 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
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
47
 */
48
final class PhpUnitDataProviderNameFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface
49
{
50
    /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
51
    use ConfigurableFixerTrait;
52

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

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

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

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

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

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

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

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

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

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

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

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

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

197
        $name = $tokens[$index]->getContent();
29✔
198

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

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

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