Skip to content

Commit ea9ebaa

Browse files
authored
Core rector support - attempt #2 (#122)
* Remove BetterNodeFinder dependency from AddGenericReturnTypeToRelationsRector * Removed some usages of BetterNodeFinder and MultiInstanceofChecker * Removed usages of BetterNodeFinder and parent nodes from MigrateToSimplifiedAttributeRector * Removed usages of BetterNodeFinder and parent nodes from RedirectBackToBackHelperRector * Removed usages of BetterNodeFinder and parent nodes from RedirectRouteToToRouteHelperRector.php * Removed usages of BetterNodeFinder and parent nodes from ReplaceFakerInstanceWithHelperRector * Fix RequestStaticValidateToInjectRector * Refactor RequestStaticValidateToInjectRector * Support Rector 0.17.7
1 parent 57bd27f commit ea9ebaa

15 files changed

+303
-322
lines changed

src/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
use PhpParser\Node;
88
use PhpParser\Node\Expr\MethodCall;
99
use PhpParser\Node\Identifier;
10-
use PhpParser\Node\Stmt\Class_;
11-
use PhpParser\Node\Stmt\ClassLike;
1210
use PhpParser\Node\Stmt\ClassMethod;
1311
use PhpParser\Node\Stmt\Return_;
1412
use PHPStan\Analyser\Scope;
1513
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
1614
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
15+
use PHPStan\Reflection\ClassReflection;
1716
use PHPStan\Type\Constant\ConstantStringType;
1817
use PHPStan\Type\Generic\GenericClassStringType;
1918
use PHPStan\Type\Generic\GenericObjectType;
@@ -97,7 +96,7 @@ public function refactorWithScope(Node $node, Scope $scope): ?Node
9796
return null;
9897
}
9998

100-
if ($this->shouldSkipNode($node)) {
99+
if ($this->shouldSkipNode($node, $scope)) {
101100
return null;
102101
}
103102

@@ -301,20 +300,20 @@ private function areGenericTypesEqual(
301300
return $this->typeComparator->areTypesEqual($phpDocTypes[1], new ObjectType($classForChildGeneric));
302301
}
303302

304-
private function shouldSkipNode(ClassMethod $classMethod): bool
303+
private function shouldSkipNode(ClassMethod $classMethod, Scope $scope): bool
305304
{
306305
if ($classMethod->stmts === null) {
307306
return true;
308307
}
309308

310-
$classLike = $this->betterNodeFinder->findParentType($classMethod, ClassLike::class);
309+
$classReflection = $scope->getClassReflection();
311310

312-
if (! $classLike instanceof ClassLike) {
311+
if (! $classReflection instanceof ClassReflection || $classReflection->isAnonymous()) {
313312
return true;
314313
}
315314

316-
if ($classLike instanceof Class_) {
317-
return ! $this->isObjectType($classLike, new ObjectType('Illuminate\Database\Eloquent\Model'));
315+
if (! $classReflection->isTrait() && ! $classReflection->isSubclassOf('Illuminate\Database\Eloquent\Model')) {
316+
return true;
318317
}
319318

320319
return false;

src/Rector/ClassMethod/AddParentBootToModelClassMethodRector.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
namespace RectorLaravel\Rector\ClassMethod;
66

77
use PhpParser\Node;
8-
use PhpParser\Node\Stmt\ClassLike;
98
use PhpParser\Node\Stmt\ClassMethod;
109
use PhpParser\Node\Stmt\Expression;
11-
use PHPStan\Type\ObjectType;
10+
use PHPStan\Reflection\ClassReflection;
1211
use Rector\Core\Rector\AbstractRector;
12+
use Rector\Core\Reflection\ReflectionResolver;
1313
use RectorLaravel\NodeAnalyzer\StaticCallAnalyzer;
1414
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
1515
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@@ -27,7 +27,8 @@ final class AddParentBootToModelClassMethodRector extends AbstractRector
2727
private const BOOT = 'boot';
2828

2929
public function __construct(
30-
private readonly StaticCallAnalyzer $staticCallAnalyzer
30+
private readonly StaticCallAnalyzer $staticCallAnalyzer,
31+
private readonly ReflectionResolver $reflectionResolver,
3132
) {
3233
}
3334

@@ -78,12 +79,13 @@ public function getNodeTypes(): array
7879
*/
7980
public function refactor(Node $node): ?Node
8081
{
81-
$classLike = $this->betterNodeFinder->findParentType($node, ClassLike::class);
82-
if (! $classLike instanceof ClassLike) {
82+
$classReflection = $this->reflectionResolver->resolveClassReflection($node);
83+
84+
if (! $classReflection instanceof ClassReflection) {
8385
return null;
8486
}
8587

86-
if (! $this->isObjectType($classLike, new ObjectType('Illuminate\Database\Eloquent\Model'))) {
88+
if (! $classReflection->isSubclassOf('Illuminate\Database\Eloquent\Model')) {
8789
return null;
8890
}
8991

src/Rector/ClassMethod/AddParentRegisterToEventServiceProviderRector.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
namespace RectorLaravel\Rector\ClassMethod;
66

77
use PhpParser\Node;
8-
use PhpParser\Node\Stmt\ClassLike;
98
use PhpParser\Node\Stmt\ClassMethod;
109
use PhpParser\Node\Stmt\Expression;
11-
use PHPStan\Type\ObjectType;
10+
use PHPStan\Reflection\ClassReflection;
1211
use Rector\Core\Rector\AbstractRector;
12+
use Rector\Core\Reflection\ReflectionResolver;
1313
use RectorLaravel\NodeAnalyzer\StaticCallAnalyzer;
1414
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
1515
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@@ -27,7 +27,8 @@ final class AddParentRegisterToEventServiceProviderRector extends AbstractRector
2727
private const REGISTER = 'register';
2828

2929
public function __construct(
30-
private readonly StaticCallAnalyzer $staticCallAnalyzer
30+
private readonly StaticCallAnalyzer $staticCallAnalyzer,
31+
private readonly ReflectionResolver $reflectionResolver,
3132
) {
3233
}
3334

@@ -78,15 +79,13 @@ public function getNodeTypes(): array
7879
*/
7980
public function refactor(Node $node): ?Node
8081
{
81-
$classLike = $this->betterNodeFinder->findParentType($node, ClassLike::class);
82-
if (! $classLike instanceof ClassLike) {
82+
$classReflection = $this->reflectionResolver->resolveClassReflection($node);
83+
84+
if (! $classReflection instanceof ClassReflection) {
8385
return null;
8486
}
8587

86-
if (! $this->isObjectType(
87-
$classLike,
88-
new ObjectType('Illuminate\Foundation\Support\Providers\EventServiceProvider')
89-
)) {
88+
if (! $classReflection->isSubclassOf('Illuminate\Foundation\Support\Providers\EventServiceProvider')) {
9089
return null;
9190
}
9291

src/Rector/ClassMethod/MigrateToSimplifiedAttributeRector.php

Lines changed: 77 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use PhpParser\Node\Name\FullyQualified;
1919
use PhpParser\Node\Stmt;
2020
use PhpParser\Node\Stmt\Class_;
21-
use PhpParser\Node\Stmt\ClassLike;
2221
use PhpParser\Node\Stmt\ClassMethod;
2322
use PhpParser\Node\Stmt\Expression;
2423
use PhpParser\Node\Stmt\Return_;
@@ -36,72 +35,39 @@ final class MigrateToSimplifiedAttributeRector extends AbstractRector
3635
*/
3736
public function getNodeTypes(): array
3837
{
39-
return [ClassMethod::class];
38+
return [Class_::class];
4039
}
4140

4241
/**
43-
* @param ClassMethod $node
42+
* @param Class_ $node
4443
*/
4544
public function refactor(Node $node): Node|array|int|null
4645
{
47-
if ($this->shouldSkipNode($node)) {
46+
if (! $this->isObjectType($node, new ObjectType('Illuminate\Database\Eloquent\Model'))) {
4847
return null;
4948
}
5049

51-
$nodeName = $node->name->name;
50+
$classMethods = $node->getMethods();
5251

53-
if (! $this->isAccessor($nodeName) && ! $this->isMutator($nodeName)) {
54-
return null;
55-
}
56-
57-
$attributeName = $this->parseAttributeName($nodeName);
58-
59-
if ($attributeName === '') {
60-
return null;
61-
}
62-
63-
/** @var ClassLike $parentClass */
64-
$parentClass = $this->betterNodeFinder->findParentType($node, ClassLike::class);
65-
66-
// Skip if the new attribute name is already used
67-
foreach ($parentClass->getMethods() as $classMethod) {
68-
if ($this->isName($classMethod, $attributeName)) {
69-
return null;
52+
foreach ($node->stmts as $key => $stmt) {
53+
if (! $stmt instanceof ClassMethod) {
54+
continue;
7055
}
71-
}
7256

73-
if ($this->isAccessor($nodeName)) {
74-
$mutator = $this->findPossibleMutator($parentClass, $attributeName);
75-
$accessor = $node;
76-
} else {
77-
$accessor = $this->findPossibleAccessor($parentClass, $attributeName);
78-
$mutator = $node;
79-
}
57+
$newNode = $this->refactorClassMethod($stmt, $classMethods);
8058

81-
// This means we have both an accessor and a mutator
82-
// So we generate the new method where the accessor
83-
// is placed on the model and remove the mutator,
84-
// so we don't run the refactoring twice
85-
if ($accessor instanceof ClassMethod && $mutator instanceof ClassMethod && $this->isMutator($nodeName)) {
86-
return NodeTraverser::REMOVE_NODE;
87-
}
88-
89-
if ($accessor instanceof ClassMethod && $mutator instanceof ClassMethod) {
90-
$newNode = $this->createAccessorAndMutator($accessor, $mutator, $attributeName);
91-
} elseif ($accessor instanceof ClassMethod) {
92-
$newNode = $this->createAccessor($attributeName, $node);
93-
} else {
94-
$newNode = $this->createMutator($attributeName, $node);
95-
}
96-
97-
// Preserve docblock
98-
$docComment = $node->getDocComment();
59+
if ($newNode === null) {
60+
continue;
61+
}
9962

100-
if ($docComment instanceof Doc) {
101-
$newNode->setDocComment($docComment);
63+
if ($newNode instanceof ClassMethod) {
64+
$node->stmts[$key] = $newNode;
65+
} elseif ($newNode === NodeTraverser::REMOVE_NODE) {
66+
unset($node->stmts[$key]);
67+
}
10268
}
10369

104-
return $newNode;
70+
return $node;
10571
}
10672

10773
public function getRuleDefinition(): RuleDefinition
@@ -145,19 +111,62 @@ protected function firstName(): \Illuminate\Database\Eloquent\Casts\Attribute
145111
]);
146112
}
147113

148-
private function shouldSkipNode(ClassMethod $classMethod): bool
114+
/**
115+
* @param ClassMethod[] $allClassMethods
116+
*/
117+
private function refactorClassMethod(ClassMethod $classMethod, array $allClassMethods): ClassMethod|int|null
149118
{
150-
$classLike = $this->betterNodeFinder->findParentType($classMethod, ClassLike::class);
119+
$nodeName = $classMethod->name->name;
151120

152-
if (! $classLike instanceof ClassLike) {
153-
return true;
121+
if (! $this->isAccessor($nodeName) && ! $this->isMutator($nodeName)) {
122+
return null;
123+
}
124+
125+
$attributeName = $this->parseAttributeName($nodeName);
126+
127+
if ($attributeName === '') {
128+
return null;
129+
}
130+
131+
// Skip if the new attribute name is already used
132+
foreach ($allClassMethods as $method) {
133+
if ($this->isName($method, $attributeName)) {
134+
return null;
135+
}
136+
}
137+
138+
if ($this->isAccessor($nodeName)) {
139+
$mutator = $this->findPossibleMutator($allClassMethods, $attributeName);
140+
$accessor = $classMethod;
141+
} else {
142+
$accessor = $this->findPossibleAccessor($allClassMethods, $attributeName);
143+
$mutator = $classMethod;
144+
}
145+
146+
// This means we have both an accessor and a mutator
147+
// So we generate the new method where the accessor
148+
// is placed on the model and remove the mutator,
149+
// so we don't run the refactoring twice
150+
if ($accessor instanceof ClassMethod && $mutator instanceof ClassMethod && $this->isMutator($nodeName)) {
151+
return NodeTraverser::REMOVE_NODE;
152+
}
153+
154+
if ($accessor instanceof ClassMethod && $mutator instanceof ClassMethod) {
155+
$newNode = $this->createAccessorAndMutator($accessor, $mutator, $attributeName);
156+
} elseif ($accessor instanceof ClassMethod) {
157+
$newNode = $this->createAccessor($attributeName, $classMethod);
158+
} else {
159+
$newNode = $this->createMutator($attributeName, $classMethod);
154160
}
155161

156-
if ($classLike instanceof Class_) {
157-
return ! $this->isObjectType($classLike, new ObjectType('Illuminate\Database\Eloquent\Model'));
162+
// Preserve docblock
163+
$docComment = $classMethod->getDocComment();
164+
165+
if ($docComment instanceof Doc) {
166+
$newNode->setDocComment($docComment);
158167
}
159168

160-
return false;
169+
return $newNode;
161170
}
162171

163172
private function createAccessor(string $attributeName, ClassMethod $classMethod): ClassMethod
@@ -262,9 +271,12 @@ private function isMutator(string $nodeName): bool
262271
return str_starts_with($nodeName, 'set') && str_ends_with($nodeName, 'Attribute');
263272
}
264273

265-
private function findPossibleAccessor(ClassLike $classLike, string $attributeName): ?ClassMethod
274+
/**
275+
* @param ClassMethod[] $allClassMethods
276+
*/
277+
private function findPossibleAccessor(array $allClassMethods, string $attributeName): ?ClassMethod
266278
{
267-
foreach ($classLike->getMethods() as $classMethod) {
279+
foreach ($allClassMethods as $classMethod) {
268280
if ($classMethod->name->toString() === 'get' . ucfirst($attributeName) . 'Attribute') {
269281
return $classMethod;
270282
}
@@ -273,9 +285,12 @@ private function findPossibleAccessor(ClassLike $classLike, string $attributeNam
273285
return null;
274286
}
275287

276-
private function findPossibleMutator(ClassLike $classLike, string $attributeName): ?ClassMethod
288+
/**
289+
* @param ClassMethod[] $allClassMethods
290+
*/
291+
private function findPossibleMutator(array $allClassMethods, string $attributeName): ?ClassMethod
277292
{
278-
foreach ($classLike->getMethods() as $classMethod) {
293+
foreach ($allClassMethods as $classMethod) {
279294
if ($classMethod->name->toString() === 'set' . ucfirst($attributeName) . 'Attribute') {
280295
return $classMethod;
281296
}

0 commit comments

Comments
 (0)