88use PhpParser \Node \Arg ;
99use PhpParser \Node \Expr ;
1010use PhpParser \Node \Expr \Assign ;
11+ use PhpParser \Node \Expr \BinaryOp \Pipe ;
1112use PhpParser \Node \Expr \Cast \Array_ ;
1213use PhpParser \Node \Expr \Cast \String_ ;
1314use PhpParser \Node \Expr \ConstFetch ;
1920use PhpParser \Node \Stmt \Expression ;
2021use PhpParser \Node \VariadicPlaceholder ;
2122use Rector \Contract \PhpParser \Node \StmtsAwareInterface ;
23+ use Rector \NodeAnalyzer \ExprAnalyzer ;
2224use Rector \Rector \AbstractRector ;
2325use Rector \ValueObject \PhpVersionFeature ;
2426use Rector \VersionBonding \Contract \MinPhpVersionInterface ;
3133 */
3234final class NestedToPipeOperatorRector extends AbstractRector implements MinPhpVersionInterface
3335{
36+ public function __construct (
37+ private readonly ExprAnalyzer $ exprAnalyzer
38+ ) {
39+ }
40+
3441 public function getRuleDefinition (): RuleDefinition
3542 {
3643 return new RuleDefinition (
@@ -67,9 +74,12 @@ public function provideMinPhpVersion(): int
6774 return PhpVersionFeature::PIPE_OPERATOER ;
6875 }
6976
77+ /**
78+ * @param StmtsAwareInterface $node
79+ */
7080 public function refactor (Node $ node ): ?Node
7181 {
72- if (! $ node instanceof StmtsAwareInterface || $ node ->stmts === null ) {
82+ if ($ node ->stmts === null ) {
7383 return null ;
7484 }
7585
@@ -90,17 +100,18 @@ public function refactor(Node $node): ?Node
90100 return $ hasChanged ? $ node : null ;
91101 }
92102
93- private function transformSequentialAssignments (StmtsAwareInterface $ node ): bool
103+ private function transformSequentialAssignments (StmtsAwareInterface $ stmtsAware ): bool
94104 {
95105 $ hasChanged = false ;
96- $ statements = $ node ->stmts ;
106+
107+ $ statements = $ stmtsAware ->stmts ;
97108 $ totalStatements = count ($ statements ) - 1 ;
98109
99110 for ($ i = 0 ; $ i < $ totalStatements ; ++$ i ) {
100111 $ chain = $ this ->findAssignmentChain ($ statements , $ i );
101112
102113 if ($ chain && count ($ chain ) >= 2 ) {
103- $ this ->processAssignmentChain ($ node , $ chain , $ i );
114+ $ this ->processAssignmentChain ($ stmtsAware , $ chain , $ i );
104115 $ hasChanged = true ;
105116 // Skip processed statements
106117 $ i += count ($ chain ) - 1 ;
@@ -153,6 +164,7 @@ private function findAssignmentChain(array $statements, int $startIndex): ?array
153164 if (! $ arg ->value instanceof Variable && ! $ this ->isSimpleValue ($ arg ->value )) {
154165 return null ;
155166 }
167+
156168 $ chain [] = [
157169 'stmt ' => $ stmt ,
158170 'assign ' => $ expr ,
@@ -166,6 +178,7 @@ private function findAssignmentChain(array $statements, int $startIndex): ?array
166178 if (! $ arg ->value instanceof Variable || $ this ->getName ($ arg ->value ) !== $ previousVarName ) {
167179 break ;
168180 }
181+
169182 $ chain [] = [
170183 'stmt ' => $ stmt ,
171184 'assign ' => $ expr ,
@@ -181,20 +194,21 @@ private function findAssignmentChain(array $statements, int $startIndex): ?array
181194
182195 private function isSimpleValue (Expr $ expr ): bool
183196 {
184- return $ expr instanceof Variable
185- || $ expr instanceof ConstFetch
197+ if ($ expr instanceof Array_) {
198+ return ! $ this ->exprAnalyzer ->isDynamicExpr ($ expr );
199+ }
200+
201+ return $ expr instanceof ConstFetch
186202 || $ expr instanceof String_
187203 || $ expr instanceof Float_
188- || $ expr instanceof Int_
189- || $ expr instanceof Array_;
204+ || $ expr instanceof Int_;
190205 }
191206
192207 /**
193208 * @param array<int, array{stmt: Stmt, assign: Expr, funcCall: Expr\FuncCall}> $chain
194209 */
195- private function processAssignmentChain (StmtsAwareInterface $ node , array $ chain , int $ startIndex ): void
210+ private function processAssignmentChain (StmtsAwareInterface $ stmtsAware , array $ chain , int $ startIndex ): void
196211 {
197- $ firstAssignment = $ chain [0 ]['assign ' ];
198212 $ lastAssignment = $ chain [count ($ chain ) - 1 ]['assign ' ];
199213
200214 // Get the initial value from the first function call's argument
@@ -217,39 +231,40 @@ private function processAssignmentChain(StmtsAwareInterface $node, array $chain,
217231 foreach ($ chain as $ chainItem ) {
218232 $ funcCall = $ chainItem ['funcCall ' ];
219233 $ placeholderCall = $ this ->createPlaceholderCall ($ funcCall );
220- $ pipeExpression = new Node \ Expr \ BinaryOp \ Pipe ($ pipeExpression , $ placeholderCall );
234+ $ pipeExpression = new Pipe ($ pipeExpression , $ placeholderCall );
221235 }
222236
223237 if (! $ lastAssignment instanceof Assign) {
224238 return ;
225239 }
240+
226241 // Create the final assignment
227- $ finalAssignment = new Assign ($ lastAssignment ->var , $ pipeExpression );
228- $ finalExpression = new Expression ($ finalAssignment );
242+ $ assign = new Assign ($ lastAssignment ->var , $ pipeExpression );
243+ $ finalExpression = new Expression ($ assign );
229244
230245 // Replace the statements
231246 $ endIndex = $ startIndex + count ($ chain ) - 1 ;
232247
233248 // Remove all intermediate statements and replace with the final pipe expression
234249 for ($ i = $ startIndex ; $ i <= $ endIndex ; ++$ i ) {
235250 if ($ i === $ startIndex ) {
236- $ node ->stmts [$ i ] = $ finalExpression ;
251+ $ stmtsAware ->stmts [$ i ] = $ finalExpression ;
237252 } else {
238- unset($ node ->stmts [$ i ]);
253+ unset($ stmtsAware ->stmts [$ i ]);
239254 }
240255 }
241256
242- $ stmts = array_values ($ node ->stmts );
257+ $ stmts = array_values ($ stmtsAware ->stmts );
243258
244259 // Reindex the array
245- $ node ->stmts = $ stmts ;
260+ $ stmtsAware ->stmts = $ stmts ;
246261 }
247262
248- private function transformNestedCalls (StmtsAwareInterface $ node ): bool
263+ private function transformNestedCalls (StmtsAwareInterface $ stmtsAware ): bool
249264 {
250265 $ hasChanged = false ;
251266
252- foreach ($ node ->stmts as $ stmt ) {
267+ foreach ($ stmtsAware ->stmts as $ stmt ) {
253268 if (! $ stmt instanceof Expression) {
254269 continue ;
255270 }
@@ -260,14 +275,14 @@ private function transformNestedCalls(StmtsAwareInterface $node): bool
260275 $ assignedValue = $ expr ->expr ;
261276 $ processedValue = $ this ->processNestedCalls ($ assignedValue );
262277
263- if ($ processedValue !== null && $ processedValue !== $ assignedValue ) {
278+ if ($ processedValue instanceof Expr && $ processedValue !== $ assignedValue ) {
264279 $ expr ->expr = $ processedValue ;
265280 $ hasChanged = true ;
266281 }
267282 } elseif ($ expr instanceof FuncCall) {
268283 $ processedValue = $ this ->processNestedCalls ($ expr );
269284
270- if ($ processedValue !== null && $ processedValue !== $ expr ) {
285+ if ($ processedValue instanceof Expr && $ processedValue !== $ expr ) {
271286 $ stmt ->expr = $ processedValue ;
272287 $ hasChanged = true ;
273288 }
@@ -288,6 +303,7 @@ private function processNestedCalls(Node $node): ?Expr
288303 if (! $ arg instanceof Arg) {
289304 return null ;
290305 }
306+
291307 if ($ arg ->value instanceof FuncCall) {
292308 return $ this ->buildPipeExpression ($ node , $ arg ->value );
293309 }
@@ -296,20 +312,13 @@ private function processNestedCalls(Node $node): ?Expr
296312 return null ;
297313 }
298314
299- private function buildPipeExpression (FuncCall $ outerCall , FuncCall $ innerCall ): Node \ Expr \ BinaryOp \ Pipe
315+ private function buildPipeExpression (FuncCall $ outerCall , FuncCall $ innerCall ): Pipe
300316 {
301- $ pipe = new Node \Expr \BinaryOp \Pipe ($ innerCall , $ this ->createPlaceholderCall ($ outerCall ));
302-
303- return $ pipe ;
317+ return new Pipe ($ innerCall , $ this ->createPlaceholderCall ($ outerCall ));
304318 }
305319
306- private function createPlaceholderCall (FuncCall $ originalCall ): FuncCall
320+ private function createPlaceholderCall (FuncCall $ funcCall ): FuncCall
307321 {
308- $ newArgs = [];
309- foreach ($ originalCall ->args as $ arg ) {
310- $ newArgs [] = new VariadicPlaceholder ();
311- }
312-
313- return new FuncCall ($ originalCall ->name , $ newArgs );
322+ return new FuncCall ($ funcCall ->name , [new VariadicPlaceholder ()]);
314323 }
315324}
0 commit comments