55use PHPStan \Analyser \Scope ;
66use PHPStan \PhpDoc \ResolvedPhpDocBlock ;
77use PHPStan \PhpDocParser \Ast \PhpDoc \PhpDocTagNode ;
8+ use PHPStan \Reflection \ClassReflection ;
89use PHPStan \Reflection \MissingMethodFromReflectionException ;
10+ use PHPStan \Reflection \ReflectionProvider ;
911use PHPStan \Rules \RuleError ;
1012use PHPStan \Rules \RuleErrorBuilder ;
1113use function array_merge ;
14+ use function count ;
15+ use function explode ;
1216use function preg_match ;
1317use function sprintf ;
1418
1519class 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}
0 commit comments