Skip to content

Commit 3cbe357

Browse files
committed
[Symfony73] Handle first class callable on new \Twig\TwigFilter(some_filter, $this->someFilter(...)) on GetFiltersToAsTwigFilterAttributeRector
1 parent 5a6e171 commit 3cbe357

File tree

2 files changed

+89
-35
lines changed

2 files changed

+89
-35
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\GetFiltersToAsTwigFilterAttributeRector\Fixture;
4+
5+
use Twig\Extension\AbstractExtension;
6+
7+
final class WithFirstClassCallableFilter extends AbstractExtension
8+
{
9+
public function getFilters()
10+
{
11+
return [
12+
new \Twig\TwigFilter('some_filter', $this->someFilter(...)),
13+
];
14+
}
15+
16+
public function someFilter($value)
17+
{
18+
return $value;
19+
}
20+
}
21+
22+
?>
23+
-----
24+
<?php
25+
26+
namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\GetFiltersToAsTwigFilterAttributeRector\Fixture;
27+
28+
use Twig\Extension\AbstractExtension;
29+
30+
final class WithFirstClassCallableFilter extends AbstractExtension
31+
{
32+
#[\Twig\Attribute\AsTwigFilter]
33+
public function someFilter($value)
34+
{
35+
return $value;
36+
}
37+
}
38+
39+
?>

rules/Symfony73/Rector/Class_/GetFiltersToAsTwigFilterAttributeRector.php

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Rector\Symfony\Symfony73\Rector\Class_;
66

77
use PhpParser\Node;
8+
use PhpParser\Node\Arg;
89
use PhpParser\Node\Attribute;
910
use PhpParser\Node\AttributeGroup;
1011
use PhpParser\Node\Expr\Array_;
@@ -18,7 +19,6 @@
1819
use PhpParser\Node\Stmt\Return_;
1920
use PHPStan\Reflection\ReflectionProvider;
2021
use PHPStan\Type\ObjectType;
21-
use Rector\Exception\ShouldNotHappenException;
2222
use Rector\Rector\AbstractRector;
2323
use Rector\Symfony\Enum\TwigClass;
2424
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@@ -96,7 +96,8 @@ public function refactor(Node $node): ?Class_
9696
return null;
9797
}
9898

99-
if (! $this->isObjectType($node, new ObjectType(TwigClass::TWIG_EXTENSION))) {
99+
$twigExtensionObjectType = new ObjectType(TwigClass::TWIG_EXTENSION);
100+
if (! $this->isObjectType($node, $twigExtensionObjectType)) {
100101
return null;
101102
}
102103

@@ -133,33 +134,21 @@ public function refactor(Node $node): ?Class_
133134
}
134135

135136
$secondArg = $new->getArgs()[1];
136-
if ($secondArg->value instanceof MethodCall && $secondArg->value->isFirstClassCallable()) {
137-
throw new ShouldNotHappenException('Not supported yet');
138-
}
139-
140-
if ($secondArg->value instanceof Array_ && count($secondArg->value->items) === 2) {
141-
$localMethodName = $this->matchLocalMethodName($secondArg->value);
142-
if (! is_string($localMethodName)) {
143-
continue;
137+
if ($secondArg->value instanceof MethodCall && $secondArg->value->isFirstClassCallable() && $this->isObjectType(
138+
$secondArg->value->var,
139+
$twigExtensionObjectType
140+
)) {
141+
if ($this->processSetAttribute($secondArg, $node, $returnArray, $key)) {
142+
$hasChanged = true;
144143
}
145-
146-
$localMethod = $node->getMethod($localMethodName);
147-
if (! $localMethod instanceof ClassMethod) {
148-
continue;
144+
} elseif ($secondArg->value instanceof Array_ && count($secondArg->value->items) === 2) {
145+
if ($this->processSetAttribute($secondArg, $node, $returnArray, $key)) {
146+
$hasChanged = true;
149147
}
150-
151-
$localMethod->attrGroups[] = new AttributeGroup([
152-
new Attribute(new FullyQualified(TwigClass::AS_TWIG_FILTER_ATTRIBUTE)),
153-
]);
154-
155-
// remove old new fuction instance
156-
unset($returnArray->items[$key]);
157-
158-
$hasChanged = true;
159148
}
160149
}
161150

162-
$this->removeGetFilterMethodIfEmpty($returnArray, $node, $stmt);
151+
$this->removeGetFilterMethodIfEmpty($returnArray, $node);
163152
}
164153

165154
if ($hasChanged) {
@@ -169,26 +158,52 @@ public function refactor(Node $node): ?Class_
169158
return null;
170159
}
171160

172-
private function matchLocalMethodName(Array_ $callableArray): ?string
161+
private function processSetAttribute(Arg $secondArg, Class_ $class, Array_ $returnArray, int $key): bool
173162
{
174-
$firstItem = $callableArray->items[0];
175-
if (! $firstItem->value instanceof Variable) {
176-
return null;
163+
$localMethodName = $this->matchLocalMethodName($secondArg->value);
164+
if (! is_string($localMethodName)) {
165+
return false;
177166
}
178167

179-
if (! $this->isName($firstItem->value, 'this')) {
180-
return null;
168+
$localMethod = $class->getMethod($localMethodName);
169+
if (! $localMethod instanceof ClassMethod) {
170+
return false;
181171
}
182172

183-
$secondItem = $callableArray->items[1];
184-
if (! $secondItem->value instanceof String_) {
185-
return null;
173+
$localMethod->attrGroups[] = new AttributeGroup([
174+
new Attribute(new FullyQualified(TwigClass::AS_TWIG_FILTER_ATTRIBUTE)),
175+
]);
176+
177+
// remove old new fuction instance
178+
unset($returnArray->items[$key]);
179+
180+
return true;
181+
}
182+
183+
private function matchLocalMethodName(Array_|MethodCall $callable): ?string
184+
{
185+
if ($callable instanceof Array_) {
186+
$firstItem = $callable->items[0];
187+
if (! $firstItem->value instanceof Variable) {
188+
return null;
189+
}
190+
191+
if (! $this->isName($firstItem->value, 'this')) {
192+
return null;
193+
}
194+
195+
$secondItem = $callable->items[1];
196+
if (! $secondItem->value instanceof String_) {
197+
return null;
198+
}
199+
200+
return $secondItem->value->value;
186201
}
187202

188-
return $secondItem->value->value;
203+
return $this->getName($callable->name);
189204
}
190205

191-
private function removeGetFilterMethodIfEmpty(Array_ $getFilterReturnArray, Class_ $class, Return_ $return): void
206+
private function removeGetFilterMethodIfEmpty(Array_ $getFilterReturnArray, Class_ $class): void
192207
{
193208
if (count($getFilterReturnArray->items) !== 0) {
194209
return;

0 commit comments

Comments
 (0)