Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b9827cf

Browse files
authored
Discover data providers from other classes
1 parent 008f5da commit b9827cf

File tree

3 files changed

+70
-18
lines changed

3 files changed

+70
-18
lines changed

‎src/Rules/PHPUnit/DataProviderHelper.php

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,32 @@
55
use PHPStan\Analyser\Scope;
66
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
77
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
8+
use PHPStan\Reflection\ClassReflection;
89
use PHPStan\Reflection\MissingMethodFromReflectionException;
10+
use PHPStan\Reflection\ReflectionProvider;
911
use PHPStan\Rules\RuleError;
1012
use PHPStan\Rules\RuleErrorBuilder;
1113
use function array_merge;
14+
use function count;
15+
use function explode;
1216
use function preg_match;
1317
use function sprintf;
1418

1519
class DataProviderHelper
1620
{
1721

22+
/**
23+
* Reflection provider.
24+
*
25+
* @var ReflectionProvider
26+
*/
27+
private $reflectionProvider;
28+
29+
public function __construct(ReflectionProvider $reflectionProvider)
30+
{
31+
$this->reflectionProvider = $reflectionProvider;
32+
}
33+
1834
/**
1935
* @return array<PhpDocTagNode>
2036
*/
@@ -48,57 +64,61 @@ public function processDataProvider(
4864
bool $deprecationRulesInstalled
4965
): array
5066
{
51-
$dataProviderName = $this->getDataProviderName($phpDocTag);
52-
if ($dataProviderName === null) {
53-
// Missing name is already handled in NoMissingSpaceInMethodAnnotationRule
67+
$dataProviderValue = $this->getDataProviderValue($phpDocTag);
68+
if ($dataProviderValue === null) {
69+
// Missing value is already handled in NoMissingSpaceInMethodAnnotationRule
5470
return [];
5571
}
5672

57-
$classReflection= $scope->getClassReflection();
73+
[$classReflection, $method] = $this->parseDataProviderValue($scope, $dataProviderValue);
5874
if ($classReflection === null) {
59-
// Should not happen
60-
return [];
75+
$error = RuleErrorBuilder::message(sprintf(
76+
'@dataProvider %s related class not found.',
77+
$dataProviderValue
78+
))->build();
79+
80+
return [$error];
6181
}
6282

6383
try {
64-
$dataProviderMethodReflection = $classReflection->getNativeMethod($dataProviderName);
84+
$dataProviderMethodReflection = $classReflection->getNativeMethod($method);
6585
} catch (MissingMethodFromReflectionException $missingMethodFromReflectionException) {
6686
$error = RuleErrorBuilder::message(sprintf(
6787
'@dataProvider %s related method not found.',
68-
$dataProviderName
88+
$dataProviderValue
6989
))->build();
7090

7191
return [$error];
7292
}
7393

7494
$errors = [];
7595

76-
if ($checkFunctionNameCase && $dataProviderName !== $dataProviderMethodReflection->getName()) {
96+
if ($checkFunctionNameCase && $method !== $dataProviderMethodReflection->getName()) {
7797
$errors[] = RuleErrorBuilder::message(sprintf(
7898
'@dataProvider %s related method is used with incorrect case: %s.',
79-
$dataProviderName,
99+
$dataProviderValue,
80100
$dataProviderMethodReflection->getName()
81101
))->build();
82102
}
83103

84104
if (!$dataProviderMethodReflection->isPublic()) {
85105
$errors[] = RuleErrorBuilder::message(sprintf(
86106
'@dataProvider %s related method must be public.',
87-
$dataProviderName
107+
$dataProviderValue
88108
))->build();
89109
}
90110

91111
if ($deprecationRulesInstalled && !$dataProviderMethodReflection->isStatic()) {
92112
$errors[] = RuleErrorBuilder::message(sprintf(
93113
'@dataProvider %s related method must be static.',
94-
$dataProviderName
114+
$dataProviderValue
95115
))->build();
96116
}
97117

98118
return $errors;
99119
}
100120

101-
private function getDataProviderName(PhpDocTagNode $phpDocTag): ?string
121+
private function getDataProviderValue(PhpDocTagNode $phpDocTag): ?string
102122
{
103123
if (preg_match('/^[^ \t]+/', (string) $phpDocTag->value, $matches) !== 1) {
104124
return null;
@@ -107,4 +127,21 @@ private function getDataProviderName(PhpDocTagNode $phpDocTag): ?string
107127
return $matches[0];
108128
}
109129

130+
/**
131+
* @return array{ClassReflection|null, string}
132+
*/
133+
private function parseDataProviderValue(Scope $scope, string $dataProviderValue): array
134+
{
135+
$parts = explode('::', $dataProviderValue, 2);
136+
if (count($parts) <= 1) {
137+
return [$scope->getClassReflection(), $dataProviderValue];
138+
}
139+
140+
if ($this->reflectionProvider->hasClass($parts[0])) {
141+
return [$this->reflectionProvider->getClass($parts[0]), $parts[1]];
142+
}
143+
144+
return [null, $dataProviderValue];
145+
}
146+
110147
}

‎tests/Rules/PHPUnit/DataProviderDeclarationRuleTest.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ class DataProviderDeclarationRuleTest extends RuleTestCase
1414

1515
protected function getRule(): Rule
1616
{
17+
$reflection = $this->createReflectionProvider();
18+
1719
return new DataProviderDeclarationRule(
18-
new DataProviderHelper(),
20+
new DataProviderHelper($reflection),
1921
self::getContainer()->getByType(FileTypeMapper::class),
2022
true,
2123
true
@@ -27,19 +29,23 @@ public function testRule(): void
2729
$this->analyse([__DIR__ . '/data/data-provider-declaration.php'], [
2830
[
2931
'@dataProvider providebaz related method is used with incorrect case: provideBaz.',
30-
13,
32+
14,
3133
],
3234
[
3335
'@dataProvider provideQux related method must be static.',
34-
13,
36+
14,
3537
],
3638
[
3739
'@dataProvider provideQuux related method must be public.',
38-
13,
40+
14,
3941
],
4042
[
4143
'@dataProvider provideNonExisting related method not found.',
42-
66,
44+
68,
45+
],
46+
[
47+
'@dataProvider NonExisting::provideNonExisting related class not found.',
48+
68,
4349
],
4450
]);
4551
}

‎tests/Rules/PHPUnit/data/data-provider-declaration.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class FooTestCase extends \PHPUnit\Framework\TestCase
99
* @dataProvider providebaz
1010
* @dataProvider provideQux
1111
* @dataProvider provideQuux
12+
* @dataProvider \ExampleTestCase\BarTestCase::provideToOtherClass
1213
*/
1314
public function testIsNotFoo(string $subject): void
1415
{
@@ -61,10 +62,18 @@ class BarTestCase extends \PHPUnit\Framework\TestCase
6162

6263
/**
6364
* @dataProvider provideNonExisting
65+
* @dataProvider NonExisting::provideNonExisting
6466
* @dataProvider provideCorge
6567
*/
6668
public function testIsNotBar(string $subject): void
6769
{
6870
self::assertNotSame('bar', $subject);
6971
}
72+
73+
public static function provideToOtherClass(): iterable
74+
{
75+
return [
76+
['toOtherClass'],
77+
];
78+
}
7079
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /