1818use PhpParser \Node \Name \FullyQualified ;
1919use PhpParser \Node \Stmt ;
2020use PhpParser \Node \Stmt \Class_ ;
21- use PhpParser \Node \Stmt \ClassLike ;
2221use PhpParser \Node \Stmt \ClassMethod ;
2322use PhpParser \Node \Stmt \Expression ;
2423use 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