99use PhpParser \Node \Expr \Variable ;
1010use PhpParser \Node \Stmt \Class_ ;
1111use PhpParser \Node \Stmt \ClassMethod ;
12- use PHPStan \PhpDocParser \Ast \PhpDoc \GenericTagValueNode ;
13- use PHPStan \PhpDocParser \Ast \PhpDoc \PhpDocTagNode ;
1412use PHPStan \Reflection \ClassReflection ;
15- use Rector \BetterPhpDocParser \PhpDocInfo \PhpDocInfo ;
16- use Rector \BetterPhpDocParser \PhpDocInfo \PhpDocInfoFactory ;
1713use Rector \DeadCode \NodeAnalyzer \IsClassMethodUsedAnalyzer ;
18- use Rector \NodeTypeResolver \Node \AttributeKey ;
19- use Rector \Php80 \NodeAnalyzer \PhpAttributeAnalyzer ;
2014use Rector \PhpParser \Node \BetterNodeFinder ;
2115use Rector \PHPStan \ScopeFetcher ;
2216use Rector \Rector \AbstractRector ;
2317use Rector \Reflection \ReflectionResolver ;
18+ use Rector \TypeDeclarationDocblocks \NodeFinder \DataProviderMethodsFinder ;
2419use Rector \ValueObject \MethodName ;
2520use Symplify \RuleDocGenerator \ValueObject \CodeSample \CodeSample ;
2621use Symplify \RuleDocGenerator \ValueObject \RuleDefinition ;
@@ -34,8 +29,7 @@ public function __construct(
3429 private readonly IsClassMethodUsedAnalyzer $ isClassMethodUsedAnalyzer ,
3530 private readonly ReflectionResolver $ reflectionResolver ,
3631 private readonly BetterNodeFinder $ betterNodeFinder ,
37- private readonly PhpDocInfoFactory $ phpDocInfoFactory ,
38- private readonly PhpAttributeAnalyzer $ phpAttributeAnalyzer
32+ private readonly DataProviderMethodsFinder $ dataProviderMethodsFinder
3933 ) {
4034 }
4135
@@ -84,51 +78,47 @@ public function getNodeTypes(): array
8478 */
8579 public function refactor (Node $ node ): ?Node
8680 {
87- $ classMethods = $ node ->getMethods ();
88- if ($ classMethods === []) {
81+ if ($ node ->getMethods () === []) {
8982 return null ;
9083 }
9184
92- $ privateMethods = array_filter (
93- $ classMethods ,
94- fn (ClassMethod $ classMethod ): bool => $ classMethod ->isPrivate ()
95- );
96-
97- if ($ privateMethods === []) {
98- return null ;
99- }
100-
101- if ($ this ->hasDynamicMethodCallOnFetchThis ($ classMethods )) {
102- return null ;
103- }
85+ $ hasChanged = false ;
10486
10587 $ classReflection = $ this ->reflectionResolver ->resolveClassReflection ($ node );
10688 if (! $ classReflection instanceof ClassReflection) {
10789 return null ;
10890 }
10991
110- $ collectionTestMethodsUsesPrivateProvider = $ this ->collectTestMethodsUsesPrivateDataProvider (
111- $ classReflection ,
112- $ node ,
113- $ classMethods
114- );
92+ $ dataProviderMethodNames = $ this ->resolveDataProviderMethodNames ($ node );
11593
116- $ hasChanged = false ;
117- $ scope = ScopeFetcher::fetch ($ node );
118- foreach ($ privateMethods as $ privateMethod ) {
119- if ($ this ->shouldSkip ($ privateMethod , $ classReflection )) {
94+ foreach ($ node ->stmts as $ classStmtKey => $ classStmt ) {
95+ if (! $ classStmt instanceof ClassMethod) {
96+ continue ;
97+ }
98+
99+ if (! $ classStmt ->isPrivate ()) {
120100 continue ;
121101 }
122102
123- if ($ this ->isClassMethodUsedAnalyzer ->isClassMethodUsed ($ node , $ privateMethod , $ scope )) {
103+ $ classMethod = $ classStmt ;
104+ if ($ this ->hasDynamicMethodCallOnFetchThis ($ classStmt )) {
124105 continue ;
125106 }
126107
127- if (in_array ($ this ->getName ($ privateMethod ), $ collectionTestMethodsUsesPrivateProvider , true )) {
108+ $ scope = ScopeFetcher::fetch ($ node );
109+ if ($ this ->shouldSkip ($ classStmt , $ classReflection )) {
128110 continue ;
129111 }
130112
131- unset($ node ->stmts [$ privateMethod ->getAttribute (AttributeKey::STMT_KEY )]);
113+ if ($ this ->isClassMethodUsedAnalyzer ->isClassMethodUsed ($ node , $ classStmt , $ scope )) {
114+ continue ;
115+ }
116+
117+ if ($ this ->isNames ($ classMethod , $ dataProviderMethodNames )) {
118+ continue ;
119+ }
120+
121+ unset($ node ->stmts [$ classStmtKey ]);
132122 $ hasChanged = true ;
133123 }
134124
@@ -137,69 +127,11 @@ public function refactor(Node $node): ?Node
137127 }
138128
139129 return null ;
140- }
141130
142- /**
143- * @param ClassMethod[] $classMethods
144- * @return string[]
145- */
146- private function collectTestMethodsUsesPrivateDataProvider (
147- ClassReflection $ classReflection ,
148- Class_ $ class ,
149- array $ classMethods
150- ): array {
151- if (! $ classReflection ->is ('PHPUnit\Framework\TestCase ' )) {
152- return [];
153- }
154-
155- $ privateMethods = [];
156- foreach ($ classMethods as $ classMethod ) {
157- // test method only public, but may use private data provider
158- // so verify @dataProvider and #[\PHPUnit\Framework\Attributes\DataProvider] only on public methods
159- if (! $ classMethod ->isPublic ()) {
160- continue ;
161- }
162-
163- $ phpDocInfo = $ this ->phpDocInfoFactory ->createFromNode ($ classMethod );
164- if ($ phpDocInfo instanceof PhpDocInfo && $ phpDocInfo ->hasByName ('dataProvider ' )) {
165- $ dataProvider = $ phpDocInfo ->getByName ('dataProvider ' );
166- if ($ dataProvider instanceof PhpDocTagNode && $ dataProvider ->value instanceof GenericTagValueNode) {
167- $ dataProviderMethod = $ class ->getMethod ($ dataProvider ->value ->value );
168- if ($ dataProviderMethod instanceof ClassMethod && $ dataProviderMethod ->isPrivate ()) {
169- $ privateMethods [] = $ dataProvider ->value ->value ;
170- }
171- }
172- }
173-
174- if ($ this ->phpAttributeAnalyzer ->hasPhpAttribute (
175- $ classMethod ,
176- 'PHPUnit\Framework\Attributes\DataProvider '
177- )) {
178- foreach ($ classMethod ->attrGroups as $ attrGroup ) {
179- foreach ($ attrGroup ->attrs as $ attr ) {
180- if ($ attr ->name ->toString () === 'PHPUnit\Framework\Attributes\DataProvider ' ) {
181- $ argValue = $ attr ->args [0 ]->value ->value ?? '' ;
182- if (is_string ($ argValue )) {
183- $ dataProviderMethod = $ class ->getMethod ($ argValue );
184- if ($ dataProviderMethod instanceof ClassMethod && $ dataProviderMethod ->isPrivate ()) {
185- $ privateMethods [] = $ argValue ;
186- }
187- }
188- }
189- }
190- }
191- }
192- }
193-
194- return $ privateMethods ;
195131 }
196132
197- private function shouldSkip (ClassMethod $ classMethod , ? ClassReflection $ classReflection ): bool
133+ private function shouldSkip (ClassMethod $ classMethod , ClassReflection $ classReflection ): bool
198134 {
199- if (! $ classReflection instanceof ClassReflection) {
200- return true ;
201- }
202-
203135 // unreliable to detect trait, interface, anonymous class: doesn't make sense
204136 if ($ classReflection ->isTrait ()) {
205137 return true ;
@@ -221,36 +153,34 @@ private function shouldSkip(ClassMethod $classMethod, ?ClassReflection $classRef
221153 return $ classReflection ->hasMethod (MethodName::CALL );
222154 }
223155
224- /**
225- * @param ClassMethod[] $classMethods
226- */
227- private function hasDynamicMethodCallOnFetchThis (array $ classMethods ): bool
156+ private function hasDynamicMethodCallOnFetchThis (ClassMethod $ classMethod ): bool
228157 {
229- foreach ($ classMethods as $ classMethod ) {
230- $ isFound = (bool ) $ this ->betterNodeFinder ->findFirst (
231- (array ) $ classMethod ->getStmts (),
232- function (Node $ subNode ): bool {
233- if (! $ subNode instanceof MethodCall) {
234- return false ;
235- }
236-
237- if (! $ subNode ->var instanceof Variable) {
238- return false ;
239- }
158+ return (bool ) $ this ->betterNodeFinder ->findFirst (
159+ (array ) $ classMethod ->stmts ,
160+ function (Node $ subNode ): bool {
161+ if (! $ subNode instanceof MethodCall) {
162+ return false ;
163+ }
240164
241- if (! $ this -> isName ( $ subNode ->var , ' this ' ) ) {
242- return false ;
243- }
165+ if (! $ subNode ->var instanceof Variable ) {
166+ return false ;
167+ }
244168
245- return $ subNode ->name instanceof Variable;
169+ if (! $ this ->isName ($ subNode ->var , 'this ' )) {
170+ return false ;
246171 }
247- );
248172
249- if ($ isFound ) {
250- return true ;
173+ return $ subNode ->name instanceof Variable;
251174 }
252- }
175+ );
176+ }
253177
254- return false ;
178+ /**
179+ * @return string[]
180+ */
181+ private function resolveDataProviderMethodNames (Class_ $ class ): array
182+ {
183+ $ dataProviderClassMethods = $ this ->dataProviderMethodsFinder ->findDataProviderNodesInClass ($ class );
184+ return $ this ->nodeNameResolver ->getNames ($ dataProviderClassMethods );
255185 }
256186}
0 commit comments