55namespace Rector \Php85 \Rector \StmtsAwareInterface ;
66
77use PhpParser \Node ;
8+ use PhpParser \Node \Arg ;
9+ use PhpParser \Node \Expr ;
810use PhpParser \Node \Expr \Assign ;
11+ use PhpParser \Node \Expr \Cast \Array_ ;
12+ use PhpParser \Node \Expr \Cast \String_ ;
13+ use PhpParser \Node \Expr \ConstFetch ;
914use PhpParser \Node \Expr \FuncCall ;
1015use PhpParser \Node \Expr \Variable ;
16+ use PhpParser \Node \Scalar \DNumber ;
17+ use PhpParser \Node \Scalar \LNumber ;
1118use PhpParser \Node \Stmt ;
1219use PhpParser \Node \Stmt \Expression ;
1320use PhpParser \Node \VariadicPlaceholder ;
2532final class NestedToPipeOperatorRector extends AbstractRector implements MinPhpVersionInterface
2633{
2734 public function getRuleDefinition (): RuleDefinition
28- {
35+ {
2936 return new RuleDefinition (
3037 'Transform nested function calls and sequential assignments to pipe operator syntax ' ,
3138 [
@@ -105,7 +112,7 @@ private function transformSequentialAssignments(StmtsAwareInterface $node): bool
105112
106113 /**
107114 * @param array<int, Stmt> $statements
108- * @return array<int, Stmt>|null
115+ * @return array<int, array{stmt: Stmt, assign: Expr, funcCall: Expr\FuncCall} >|null
109116 */
110117 private function findAssignmentChain (array $ statements , int $ startIndex ): ?array
111118 {
@@ -122,22 +129,26 @@ private function findAssignmentChain(array $statements, int $startIndex): ?array
122129
123130 $ expr = $ stmt ->expr ;
124131 if (! $ expr instanceof Assign) {
125- break ;
132+ return null ;
126133 }
127134
128135 // Check if this is a simple function call with one argument
129136 if (! $ expr ->expr instanceof FuncCall) {
130- break ;
137+ return null ;
131138 }
132139
133140 $ funcCall = $ expr ->expr ;
134141 if (count ($ funcCall ->args ) !== 1 ) {
135- break ;
142+ return null ;
136143 }
137144
138145 $ arg = $ funcCall ->args [0 ];
146+ if (! $ arg instanceof Arg) {
147+ return null ;
148+ }
139149
140150 if ($ currentIndex === $ startIndex ) {
151+
141152 // First in chain - must be a variable or simple value
142153 if (! $ arg ->value instanceof Variable && ! $ this ->isSimpleValue ($ arg ->value )) {
143154 return null ;
@@ -152,9 +163,7 @@ private function findAssignmentChain(array $statements, int $startIndex): ?array
152163 $ previousAssign = $ chain [count ($ chain ) - 1 ]['assign ' ];
153164 $ previousVarName = $ this ->getName ($ previousAssign ->var );
154165
155- if (! $ arg ->value instanceof Variable || $ this ->getName (
156- $ arg ->value
157- ) !== $ previousVarName ) {
166+ if (! $ arg ->value instanceof Variable || $ this ->getName ($ arg ->value ) !== $ previousVarName ) {
158167 break ;
159168 }
160169 $ chain [] = [
@@ -164,29 +173,43 @@ private function findAssignmentChain(array $statements, int $startIndex): ?array
164173 ];
165174 }
166175
167- $ currentIndex += 1 ;
176+ ++ $ currentIndex ;
168177 }
169178
170179 return $ chain ;
171180 }
172181
173- private function isSimpleValue (Node $ node ): bool
182+ private function isSimpleValue (Expr $ expr ): bool
174183 {
175- return $ node instanceof Node \Scalar \String_
176- || $ node instanceof Node \Scalar \LNumber
177- || $ node instanceof Node \Scalar \DNumber
178- || $ node instanceof Node \Expr \ConstFetch
179- || $ node instanceof Node \Expr \Array_;
184+ return $ expr instanceof Variable
185+ || $ expr instanceof ConstFetch
186+ || $ expr instanceof String_
187+ || $ expr instanceof LNumber
188+ || $ expr instanceof DNumber
189+ || $ expr instanceof Array_;
180190 }
181191
192+ /**
193+ * @param array<int, array{stmt: Stmt, assign: Expr, funcCall: Expr\FuncCall}> $chain
194+ */
182195 private function processAssignmentChain (StmtsAwareInterface $ node , array $ chain , int $ startIndex ): void
183196 {
184197 $ firstAssignment = $ chain [0 ]['assign ' ];
185198 $ lastAssignment = $ chain [count ($ chain ) - 1 ]['assign ' ];
186199
187200 // Get the initial value from the first function call's argument
188201 $ firstFuncCall = $ chain [0 ]['funcCall ' ];
189- $ initialValue = $ firstFuncCall ->args [0 ]->value ;
202+
203+ if (! $ firstFuncCall instanceof FuncCall) {
204+ return ;
205+ }
206+
207+ $ firstArg = $ firstFuncCall ->args [0 ];
208+ if (! $ firstArg instanceof Arg) {
209+ return ;
210+ }
211+
212+ $ initialValue = $ firstArg ->value ;
190213
191214 // Build the pipe chain
192215 $ pipeExpression = $ initialValue ;
@@ -197,6 +220,9 @@ private function processAssignmentChain(StmtsAwareInterface $node, array $chain,
197220 $ pipeExpression = new Node \Expr \BinaryOp \Pipe ($ pipeExpression , $ placeholderCall );
198221 }
199222
223+ if (! $ lastAssignment instanceof Assign) {
224+ return ;
225+ }
200226 // Create the final assignment
201227 $ finalAssignment = new Assign ($ lastAssignment ->var , $ pipeExpression );
202228 $ finalExpression = new Expression ($ finalAssignment );
@@ -205,7 +231,7 @@ private function processAssignmentChain(StmtsAwareInterface $node, array $chain,
205231 $ endIndex = $ startIndex + count ($ chain ) - 1 ;
206232
207233 // Remove all intermediate statements and replace with the final pipe expression
208- for ($ i = $ startIndex ; $ i <= $ endIndex ; $ i ++ ) {
234+ for ($ i = $ startIndex ; $ i <= $ endIndex ; ++ $ i ) {
209235 if ($ i === $ startIndex ) {
210236 $ node ->stmts [$ i ] = $ finalExpression ;
211237 } else {
@@ -240,6 +266,7 @@ private function transformNestedCalls(StmtsAwareInterface $node): bool
240266 }
241267 } elseif ($ expr instanceof FuncCall) {
242268 $ processedValue = $ this ->processNestedCalls ($ expr );
269+
243270 if ($ processedValue !== null && $ processedValue !== $ expr ) {
244271 $ stmt ->expr = $ processedValue ;
245272 $ hasChanged = true ;
@@ -250,14 +277,17 @@ private function transformNestedCalls(StmtsAwareInterface $node): bool
250277 return $ hasChanged ;
251278 }
252279
253- private function processNestedCalls (Node $ node ): ?Node
280+ private function processNestedCalls (Node $ node ): ?Expr
254281 {
255282 if (! $ node instanceof FuncCall) {
256283 return null ;
257284 }
258285
259286 // Check if any argument is a function call
260287 foreach ($ node ->args as $ arg ) {
288+ if (! $ arg instanceof Arg) {
289+ return null ;
290+ }
261291 if ($ arg ->value instanceof FuncCall) {
262292 return $ this ->buildPipeExpression ($ node , $ arg ->value );
263293 }
0 commit comments