33declare (strict_types=1 );
44namespace Rector \NodeManipulator ;
55
6- use PhpParser \Node \Stmt ;
76use PhpParser \Node \Stmt \Class_ ;
87use PhpParser \Node \Stmt \ClassConst ;
98use PhpParser \Node \Stmt \ClassMethod ;
@@ -24,19 +23,47 @@ public function __construct(NodeFactory $nodeFactory)
2423 }
2524 /**
2625 * @api
27- * @param \PhpParser\Node\Stmt\Property|\PhpParser\Node\Stmt\ClassConst|\PhpParser\Node\Stmt\ClassMethod $stmt
26+ * @param \PhpParser\Node\Stmt\Property|\PhpParser\Node\Stmt\ClassConst|\PhpParser\Node\Stmt\ClassMethod $addedStmt
2827 */
29- public function addAsFirstMethod (Class_ $ class , $ stmt ) : void
28+ public function addAsFirstMethod (Class_ $ class , $ addedStmt ) : void
3029 {
3130 $ scope = $ class ->getAttribute (AttributeKey::SCOPE );
32- $ stmt ->setAttribute (AttributeKey::SCOPE , $ scope );
33- if ($ this ->isSuccessToInsertBeforeFirstMethod ($ class , $ stmt )) {
31+ $ addedStmt ->setAttribute (AttributeKey::SCOPE , $ scope );
32+ // no stmts? add this one
33+ if ($ class ->stmts === []) {
34+ $ class ->stmts [] = $ addedStmt ;
3435 return ;
3536 }
36- if ($ this ->isSuccessToInsertAfterLastProperty ($ class , $ stmt )) {
37+ $ newClassStmts = [];
38+ $ isAdded = \false;
39+ foreach ($ class ->stmts as $ key => $ classStmt ) {
40+ $ nextStmt = $ class ->stmts [$ key + 1 ] ?? null ;
41+ if ($ isAdded === \false) {
42+ // first class method
43+ if ($ classStmt instanceof ClassMethod) {
44+ $ newClassStmts [] = $ addedStmt ;
45+ $ newClassStmts [] = $ classStmt ;
46+ $ isAdded = \true;
47+ continue ;
48+ }
49+ // after last property
50+ if ($ classStmt instanceof Property && !$ nextStmt instanceof Property) {
51+ $ newClassStmts [] = $ classStmt ;
52+ $ newClassStmts [] = $ addedStmt ;
53+ $ isAdded = \true;
54+ continue ;
55+ }
56+ }
57+ $ newClassStmts [] = $ classStmt ;
58+ }
59+ // still not added? try after last trait
60+ // @todo
61+ if ($ isAdded ) {
62+ $ class ->stmts = $ newClassStmts ;
3763 return ;
3864 }
39- $ class ->stmts [] = $ stmt ;
65+ // keep added at least as first stmt
66+ $ class ->stmts = \array_merge ([$ addedStmt ], $ class ->stmts );
4067 }
4168 /**
4269 * @internal Use PropertyAdder service instead
@@ -50,42 +77,4 @@ public function addPropertyToClass(Class_ $class, string $name, ?Type $type) : v
5077 $ property = $ this ->nodeFactory ->createPrivatePropertyFromNameAndType ($ name , $ type );
5178 $ this ->addAsFirstMethod ($ class , $ property );
5279 }
53- /**
54- * @param Stmt[] $stmts
55- * @return Stmt[]
56- */
57- private function insertBefore (array $ stmts , Stmt $ stmt , int $ key ) : array
58- {
59- \array_splice ($ stmts , $ key , 0 , [$ stmt ]);
60- return $ stmts ;
61- }
62- /**
63- * @param \PhpParser\Node\Stmt\ClassConst|\PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Property $stmt
64- */
65- private function isSuccessToInsertBeforeFirstMethod (Class_ $ class , $ stmt ) : bool
66- {
67- foreach ($ class ->stmts as $ key => $ classStmt ) {
68- if (!$ classStmt instanceof ClassMethod) {
69- continue ;
70- }
71- $ class ->stmts = $ this ->insertBefore ($ class ->stmts , $ stmt , $ key );
72- return \true;
73- }
74- return \false;
75- }
76- /**
77- * @param \PhpParser\Node\Stmt\ClassConst|\PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Property $stmt
78- */
79- private function isSuccessToInsertAfterLastProperty (Class_ $ class , $ stmt ) : bool
80- {
81- $ previousElement = null ;
82- foreach ($ class ->stmts as $ key => $ classStmt ) {
83- if ($ previousElement instanceof Property && !$ classStmt instanceof Property) {
84- $ class ->stmts = $ this ->insertBefore ($ class ->stmts , $ stmt , $ key );
85- return \true;
86- }
87- $ previousElement = $ classStmt ;
88- }
89- return \false;
90- }
9180}
0 commit comments