5
5
namespace Rector \Symfony \CodeQuality \Rector \ClassMethod ;
6
6
7
7
use PhpParser \Node ;
8
+ use PhpParser \Node \Attribute ;
8
9
use PhpParser \Node \Expr ;
9
10
use PhpParser \Node \Expr \Closure ;
10
11
use PhpParser \Node \Expr \MethodCall ;
24
25
use Rector \BetterPhpDocParser \PhpDocManipulator \PhpDocTagRemover ;
25
26
use Rector \Comments \NodeDocBlock \DocBlockUpdater ;
26
27
use Rector \Contract \PhpParser \Node \StmtsAwareInterface ;
28
+ use Rector \Doctrine \NodeAnalyzer \AttributeFinder ;
27
29
use Rector \PhpParser \Node \BetterNodeFinder ;
28
30
use Rector \Rector \AbstractRector ;
29
31
use Rector \Symfony \Annotation \AnnotationAnalyzer ;
37
39
use Symplify \RuleDocGenerator \ValueObject \RuleDefinition ;
38
40
39
41
/**
40
- * @changelog https://github.com/symfony/symfony-docs/pull/12387#discussion_r329551967
41
- * @changelog https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view.html
42
+ * @see https://github.com/symfony/symfony-docs/pull/12387#discussion_r329551967
43
+ * @see https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view.html
42
44
*
43
45
* @see \Rector\Symfony\Tests\CodeQuality\Rector\ClassMethod\TemplateAnnotationToThisRenderRector\TemplateAnnotationToThisRenderRectorTest
44
46
*/
@@ -54,6 +56,7 @@ public function __construct(
54
56
private readonly DocBlockUpdater $ docBlockUpdater ,
55
57
private readonly BetterNodeFinder $ betterNodeFinder ,
56
58
private readonly PhpDocInfoFactory $ phpDocInfoFactory ,
59
+ private readonly AttributeFinder $ attributeFinder ,
57
60
) {
58
61
}
59
62
@@ -119,15 +122,14 @@ public function refactor(Node $node): ?Node
119
122
SymfonyAnnotation::TEMPLATE
120
123
);
121
124
122
<
8000
span class="diff-text-marker">- foreach ($ node ->getMethods () as $ classMethod ) {
123
- if (! $ classMethod ->isPublic ()) {
124
- continue ;
125
- }
125
+ $ classTemplateAttribute = $ this ->attributeFinder ->findAttributeByClass ($ node , SymfonyAnnotation::TEMPLATE );
126
126
127
+ foreach ($ node ->getMethods () as $ classMethod ) {
127
128
$ hasClassMethodChanged = $ this ->replaceTemplateAnnotation (
128
129
$ classMethod ,
129
- $ classDoctrineAnnotationTagValueNode
130
+ $ classDoctrineAnnotationTagValueNode ?: $ classTemplateAttribute ,
130
131
);
132
+
131
133
if ($ hasClassMethodChanged ) {
132
134
$ hasChanged = true ;
133
135
}
@@ -137,7 +139,7 @@ public function refactor(Node $node): ?Node
137
139
return null ;
138
140
}
139
141
140
- // cleanup Class_ @Template annotaion
142
+ // cleanup Class_ @Template annotation
141
143
if ($ classDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
142
144
$ this ->removeDoctrineAnnotationTagValueNode ($ node , $ classDoctrineAnnotationTagValueNode );
143
145
}
@@ -157,7 +159,7 @@ private function decorateAbstractControllerParentClass(Class_ $class): void
157
159
158
160
private function replaceTemplateAnnotation (
159
161
ClassMethod $ classMethod ,
160
- ? DoctrineAnnotationTagValueNode $ classDoctrineAnnotationTagValueNode
162
+ DoctrineAnnotationTagValueNode | Attribute | null $ classTagValueNodeOrAttribute
161
163
): bool {
162
164
if (! $ classMethod ->isPublic ()) {
163
165
return false ;
@@ -168,28 +170,30 @@ private function replaceTemplateAnnotation(
168
170
SymfonyAnnotation::TEMPLATE
169
171
);
170
172
171
- if ($ doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
172
- return $ this ->refactorClassMethod ($ classMethod , $ doctrineAnnotationTagValueNode );
173
+ $ templateAttribute = $ this ->attributeFinder ->findAttributeByClass ($ classMethod , SymfonyAnnotation::TEMPLATE );
174
+
175
+ if ($ doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode || $ templateAttribute instanceof Attribute) {
176
+ return $ this ->refactorClassMethod ($ classMethod , $ doctrineAnnotationTagValueNode ?: $ templateAttribute );
173
177
}
174
178
12AE
175
179
// global @Template access
176
- if ($ classDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
177
- return $ this ->refactorClassMethod ($ classMethod , $ classDoctrineAnnotationTagValueNode );
180
+ if ($ classTagValueNodeOrAttribute instanceof DoctrineAnnotationTagValueNode || $ classTagValueNodeOrAttribute instanceof Attribute ) {
181
+ return $ this ->refactorClassMethod ($ classMethod , $ classTagValueNodeOrAttribute );
178
182
}
179
183
180
184
return false ;
181
185
}
182
186
183
187
private function refactorClassMethod (
184
188
ClassMethod $ classMethod ,
185
- DoctrineAnnotationTagValueNode $ templateDoctrineAnnotationTagValueNode
189
+ DoctrineAnnotationTagValueNode | Attribute $ templateTagValueNodeOrAttribute ,
186
190
): bool {
187
191
$ hasThisRenderOrReturnsResponse = $ this ->hasLastReturnResponse ($ classMethod );
188
192
189
193
$ hasChanged = false ;
190
194
191
195
$ this ->traverseNodesWithCallable ($ classMethod , function (Node $ node ) use (
192
- $ templateDoctrineAnnotationTagValueNode ,
196
+ $ templateTagValueNodeOrAttribute ,
193
197
$ hasThisRenderOrReturnsResponse ,
194
198
$ classMethod ,
195
199
&$ hasChanged
@@ -206,7 +210,7 @@ private function refactorClassMethod(
206
210
207
211
$ hasChangedNode = $ this ->refactorStmtsAwareNode (
208
212
$ node ,
209
- $ templateDoctrineAnnotationTagValueNode ,
213
+ $ templateTagValueNodeOrAttribute ,
210
214
$ hasThisRenderOrReturnsResponse ,
211
215
$ classMethod
212
216
);
@@ -223,11 +227,11 @@ private function refactorClassMethod(
223
227
224
228
$ thisRenderMethodCall = $ this ->thisRenderFactory ->create (
225
229
null ,
226
- $ templateDoctrineAnnotationTagValueNode ,
230
+ $ templateTagValueNodeOrAttribute ,
227
231
$ classMethod
228
232
);
229
233
230
- $ this ->refactorNoReturn ($ classMethod , $ thisRenderMethodCall , $ templateDoctrineAnnotationTagValueNode );
234
+ $ this ->refactorNoReturn ($ classMethod , $ thisRenderMethodCall , $ templateTagValueNodeOrAttribute );
231
235
232
236
return true ;
233
237
}
@@ -254,7 +258,7 @@ private function hasLastReturnResponse(ClassMethod $classMethod): bool
254
258
255
259
private function refactorReturn (
256
260
Return_ $ return ,
257
- DoctrineAnnotationTagValueNode $ templateDoctrineAnnotationTagValueNode ,
261
+ DoctrineAnnotationTagValueNode | Attribute $ templateTagValueNodeOrAttribute ,
258
262
bool $ hasThisRenderOrReturnsResponse ,
259
263
ClassMethod $ classMethod
260
264
): bool {
@@ -266,7 +270,7 @@ private function refactorReturn(
266
270
// create "$this->render('template.file.twig.html', ['key' => 'value']);" method call
267
271
$ thisRenderMethodCall = $ this ->thisRenderFactory ->create (
268
272
$ return ,
269
- $ templateDoctrineAnnotationTagValueNode ,
273
+ $ templateTagValueNodeOrAttribute ,
270
274
$ classMethod
271
275
);
272
276
@@ -275,7 +279,7 @@ private function refactorReturn(
275
279
$ hasThisRenderOrReturnsResponse ,
276
280
$ thisRenderMethodCall ,
277
281
$ classMethod ,
278
- $ templateDoctrineAnnotationTagValueNode
282
+ $ templateTagValueNodeOrAttribute
279
283
);
280
284
}
281
285
@@ -295,7 +299,7 @@ private function refactorReturnWithValue(
295
299
bool $ hasThisRenderOrReturnsResponse ,
296
300
MethodCall $ thisRenderMethodCall ,
297
301
ClassMethod $ classMethod ,
298
- DoctrineAnnotationTagValueNode $ doctrineAnnotationTagValueNode
302
+ DoctrineAnnotationTagValueNode | Attribute $ doctrineTagValueNodeOrAttribute
299
303
): bool {
300
304
/** @var Expr $lastReturnExpr */
301
305
$ lastReturnExpr = $ return ->expr ;
@@ -324,24 +328,41 @@ private function refactorReturnWithValue(
324
328
}
325
329
326
330
// already response
327
- $ this ->removeDoctrineAnnotationTagValueNode ($ classMethod , $ doctrineAnnotationTagValueNode );
331
+ $ this ->removeDoctrineAnnotationTagValueNode ($ classMethod , $ doctrineTagValueNodeOrAttribute );
328
332
$ this ->returnTypeDeclarationUpdater ->updateClassMethod ($ classMethod , SymfonyClass::RESPONSE );
329
333
return true ;
330
334
}
331
335
332
336
private function removeDoctrineAnnotationTagValueNode (
333
337
Class_ |ClassMethod $ node ,
334
- DoctrineAnnotationTagValueNode $ doctrineAnnotationTagValueNode
338
+ DoctrineAnnotationTagValueNode | Attribute $ doctrineTagValueNodeOrAttribute
335
339
): void {
336
- $ phpDocInfo = $ this ->phpDocInfoFactory ->createFromNodeOrEmpty ($ node );
337
- $ this ->phpDocTagRemover ->removeTagValueFromNode ($ phpDocInfo , $ doctrineAnnotationTagValueNode );
340
+ if ($ doctrineTagValueNodeOrAttribute instanceof DoctrineAnnotationTagValueNode) {
341
+ $ phpDocInfo = $ this ->phpDocInfoFactory ->createFromNodeOrEmpty ($ node );
342
+ $ this ->phpDocTagRemover ->removeTagValueFromNode ($ phpDocInfo , $ doctrineTagValueNodeOrAttribute );
343
+
344
+ $ this ->docBlockUpdater ->updateRefactoredNodeWithPhpDocInfo ($ node );
345
+
346
+ return ;
347
+ }
338
348
339
- $ this ->docBlockUpdater ->updateRefactoredNodeWithPhpDocInfo ($ node );
349
+ foreach ($ node ->attrGroups as $ attrGroupKey => $ attrGroup ) {
350
+ foreach ($ attrGroup ->attrs as $ attributeKey => $ attribute ) {
351
+ if ($ attribute === $ doctrineTagValueNodeOrAttribute ) {
352
+ unset($ attrGroup ->attrs [$ attributeKey ]);
353
+ }
354
+ }
355
+
356
+ // no attributes left? remove the whole dgroup
357
+ if ($ attrGroup ->attrs === []) {
358
+ unset($ node ->attrGroups [$ attrGroupKey ]);
359
+ }
360
+ }
340
361
}
341
362
342
363
private function refactorStmtsAwareNode (
343
364
StmtsAwareInterface $ stmtsAware ,
344
- DoctrineAnnotationTagValueNode $ templateDoctrineAnnotationTagValueNode ,
365
+ DoctrineAnnotationTagValueNode | Attribute $ templateTagValueNodeOrAttribute ,
345
366
bool $ hasThisRenderOrReturnsResponse ,
346
367
ClassMethod $ classMethod
347
368
): bool {
@@ -362,7 +383,7 @@ private function refactorStmtsAwareNode(
362
383
363
384
$ hasChangedReturn = $ this ->refactorReturn (
364
385
$ stmt ,
365
- $ templateDoctrineAnnotationTagValueNode ,
386
+ $ templateTagValueNodeOrAttribute ,
366
387
$ hasThisRenderOrReturnsResponse ,
367
388
$ classMethod
368
389
);
0 commit comments