From fd80f7465b5fdfbf93c1b9f7bcdc644dc3770166 Mon Sep 17 00:00:00 2001 From: Arshid Date: Sat, 4 Oct 2025 15:33:42 +0530 Subject: [PATCH 01/12] downgrade pipe --- .../DowngradePipeOperatorRectorTest.php | 28 ++++++ .../Fixture/fixture.php.inc | 26 ++++++ .../config/configured_rule.php | 10 +++ .../DowngradePipeOperatorRector.php | 85 +++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/DowngradePipeOperatorRectorTest.php create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/fixture.php.inc create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/config/configured_rule.php create mode 100644 rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/DowngradePipeOperatorRectorTest.php b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/DowngradePipeOperatorRectorTest.php new file mode 100644 index 00000000..f5caa60d --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/DowngradePipeOperatorRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/fixture.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/fixture.php.inc new file mode 100644 index 00000000..7b9f1d30 --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/fixture.php.inc @@ -0,0 +1,26 @@ + function3(...) + |> function2(...) + |> function1(...); + +?> +----- + diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/config/configured_rule.php b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/config/configured_rule.php new file mode 100644 index 00000000..2f425293 --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(DowngradePipeOperatorRector::class); +}; diff --git a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php new file mode 100644 index 00000000..13c943c1 --- /dev/null +++ b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php @@ -0,0 +1,85 @@ + to PHP < 8.1 compatible code', + [ + new CodeSample( + <<<'CODE_SAMPLE' +$value = "hello world"; +$result = $value + |> function3(...) + |> function2(...) + |> function1(...); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +$value = "hello world"; +$result1 = function3($value); +$result2 = function2($result1); +$result = function1($result2); +CODE_SAMPLE + ), + new CodeSample( + <<<'CODE_SAMPLE' +$result = strtoupper("Hello World") |> trim(...); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +$result = trim(strtoupper("Hello World")); +CODE_SAMPLE + ), + ] + ); + } + + public function getNodeTypes(): array + { + return [Pipe::class]; + } + + public function refactor(Node $node): ?Node + { + if (!$node instanceof Pipe) { + return null; + } + + return $this->processPipeOperation($node); + } + + private function processPipeOperation(Pipe $pipe): void + { + + } + + +} \ No newline at end of file From 94a365987d3c7bcd936a68411ac4181164a58929 Mon Sep 17 00:00:00 2001 From: Arshid Date: Sun, 12 Oct 2025 09:47:14 +0530 Subject: [PATCH 02/12] Downgrade php85 pipe --- .../DowngradePipeOperatorRectorTest.php | 2 + .../Fixture/complex_pipe_chain.php.inc | 28 +++ ...fixture.php.inc => multiple_pipes.php.inc} | 52 ++--- .../Fixture/pipe_in_assignment.php.inc | 22 ++ .../Fixture/pipe_with_closure.php.inc | 20 ++ .../Fixture/pipe_with_method_call.php.inc | 33 +++ .../pipe_with_variable_function.php.inc | 21 ++ .../Fixture/simple_pipe.php.inc | 19 ++ .../Fixture/skip_invalid_pipe.php.inc | 10 + .../Fixture/skip_no_pipe.php.inc | 9 + .../DowngradePipeOperatorRector.php | 219 ++++++++++++++++-- 11 files changed, 390 insertions(+), 45 deletions(-) create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc rename rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/{fixture.php.inc => multiple_pipes.php.inc} (94%) create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_in_assignment.php.inc create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_closure.php.inc create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_method_call.php.inc create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_variable_function.php.inc create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/simple_pipe.php.inc create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/skip_invalid_pipe.php.inc create mode 100644 rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/skip_no_pipe.php.inc diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/DowngradePipeOperatorRectorTest.php b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/DowngradePipeOperatorRectorTest.php index f5caa60d..448e8575 100644 --- a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/DowngradePipeOperatorRectorTest.php +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/DowngradePipeOperatorRectorTest.php @@ -6,11 +6,13 @@ use Iterator; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\RequiresPhp; use Rector\Testing\PHPUnit\AbstractRectorTestCase; final class DowngradePipeOperatorRectorTest extends AbstractRectorTestCase { #[DataProvider('provideData')] + #[RequiresPhp('>= 8.5')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc new file mode 100644 index 00000000..3a0bdfaf --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc @@ -0,0 +1,28 @@ + trim(...) + |> strtoupper(...) + |> (fn($str) => str_replace('WORLD', 'PHP', $str)) + |> addslashes(...); + +?> +----- + str_replace('WORLD', 'PHP', $str))($result2); +$processed = addslashes($result3); + +?> \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/fixture.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/multiple_pipes.php.inc similarity index 94% rename from rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/fixture.php.inc rename to rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/multiple_pipes.php.inc index 7b9f1d30..420d8f2c 100644 --- a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/fixture.php.inc +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/multiple_pipes.php.inc @@ -1,26 +1,26 @@ - function3(...) - |> function2(...) - |> function1(...); - -?> ------ - + function3(...) + |> function2(...) + |> function1(...); + +?> +----- + \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_in_assignment.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_in_assignment.php.inc new file mode 100644 index 00000000..12ae6d00 --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_in_assignment.php.inc @@ -0,0 +1,22 @@ + trim(...) |> strtoupper(...); + +?> +----- + \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_closure.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_closure.php.inc new file mode 100644 index 00000000..fddf8b6a --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_closure.php.inc @@ -0,0 +1,20 @@ + (fn($x) => strtoupper($x)) |> (fn($y) => $y . '!'); + +?> +----- + strtoupper($x))("hello"); +$result = (fn($y) => $y . '!')($result1); + +?> \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_method_call.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_method_call.php.inc new file mode 100644 index 00000000..bce7b4fb --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_method_call.php.inc @@ -0,0 +1,33 @@ + $processor->process(...); + +?> +----- +process(" hello "); + +?> \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_variable_function.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_variable_function.php.inc new file mode 100644 index 00000000..dd77e646 --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_variable_function.php.inc @@ -0,0 +1,21 @@ + $func(...); + +?> +----- + \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/simple_pipe.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/simple_pipe.php.inc new file mode 100644 index 00000000..18c7c0fd --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/simple_pipe.php.inc @@ -0,0 +1,19 @@ + trim(...); + +?> +----- + \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/skip_invalid_pipe.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/skip_invalid_pipe.php.inc new file mode 100644 index 00000000..25df4960 --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/skip_invalid_pipe.php.inc @@ -0,0 +1,10 @@ + "not_callable"; + +?> \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/skip_no_pipe.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/skip_no_pipe.php.inc new file mode 100644 index 00000000..a55a3550 --- /dev/null +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/skip_no_pipe.php.inc @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php index 13c943c1..98079b54 100644 --- a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php +++ b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php @@ -5,17 +5,19 @@ namespace Rector\DowngradePhp85\Rector\StmtsAwareInterface; use PhpParser\Node; +use PhpParser\Node\Expr\ArrowFunction; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp\Pipe; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Expr\Closure; -use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Stmt\Expression; -use PhpParser\Node\Stmt\Return_; -use PhpParser\NodeTraverser; +use Rector\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\Rector\AbstractRector; -use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see https://wiki.php.net/rfc/pipe-operator-v3 @@ -23,15 +25,10 @@ */ final class DowngradePipeOperatorRector extends AbstractRector { - /** - * @var int - */ - private $tempVariableCounter = 0; - public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Downgrade pipe operator |> to PHP < 8.1 compatible code', + 'Downgrade pipe operator |> to PHP < 8.5 compatible code', [ new CodeSample( <<<'CODE_SAMPLE' @@ -64,22 +61,206 @@ public function getRuleDefinition(): RuleDefinition public function getNodeTypes(): array { - return [Pipe::class]; + return [StmtsAwareInterface::class]; } - + + /** + * @param StmtsAwareInterface $node + */ public function refactor(Node $node): ?Node { - if (!$node instanceof Pipe) { + if ($node->stmts === null) { return null; } - return $this->processPipeOperation($node); + $hasChanged = false; + + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Expression) { + continue; + } + + $pipeNode = $this->findPipeNode($stmt->expr); + if (! $pipeNode instanceof Pipe) { + continue; + } + + $newStmts = $this->processPipeOperation($pipeNode, $stmt); + if ($newStmts === null) { + continue; + } + + // Replace the current statement with new statements + array_splice($node->stmts, $key, 1, $newStmts); + $hasChanged = true; + } + + return $hasChanged ? $node : null; } - private function processPipeOperation(Pipe $pipe): void + private function findPipeNode(Node $node): ?Pipe { - + if ($node instanceof Pipe) { + return $node; + } + + if ($node instanceof Assign && $node->expr instanceof Pipe) { + return $node->expr; + } + + return null; } - -} \ No newline at end of file + /** + * @return Expression[]|null + */ + private function processPipeOperation(Pipe $pipe, Expression $originalExpression): ?array + { + $pipeChain = $this->collectPipeChain($pipe); + + if (count($pipeChain) < 2) { + return null; + } + + // For simple case: single pipe operation + if (count($pipeChain) === 2) { + $replacement = $this->createSimplePipeReplacement($pipeChain[0], $pipeChain[1]); + if ($replacement === null) { + return null; + } + + // If the pipe was part of an assignment, maintain the assignment + if ($originalExpression->expr instanceof Assign) { + $newAssign = new Assign( + $originalExpression->expr->var, + $replacement + ); + return [new Expression($newAssign)]; + } + + return [new Expression($replacement)]; + } + + // For multiple pipe operations + return $this->createMultiplePipeReplacement($pipeChain, $originalExpression); + } + + /** + * @return array + */ + private function collectPipeChain(Pipe $pipe): array + { + $chain = []; + $current = $pipe; + + while ($current instanceof Pipe) { + $chain[] = $current->right; + $current = $current->left; + } + + $chain[] = $current; + + return array_reverse($chain); + } + + private function createSimplePipeReplacement(Node $input, Node $function): ?Node + { + if (! $this->isCallableNode($function)) { + return null; + } + + return $this->createFunctionCall($function, [$input]); + } + + /** + * @param array $pipeChain + * @return Expression[]|null + */ + private function createMultiplePipeReplacement(array $pipeChain, Expression $originalExpression): ?array + { + $input = $pipeChain[0]; + $statements = []; + $tempVariableCounter = 0; + + // Create all intermediate assignments + for ($i = 1; $i < count($pipeChain) - 1; $i++) { + $function = $pipeChain[$i]; + + if (! $this->isCallableNode($function)) { + return null; + } + + $tempVarName = 'result' . (++$tempVariableCounter); + $tempVar = new Variable($tempVarName); + + $functionCall = $this->createFunctionCall($function, [$input]); + $assign = new Assign($tempVar, $functionCall); + $statements[] = new Expression($assign); + + $input = $tempVar; + } + + // Create the final function call + $finalFunction = $pipeChain[count($pipeChain) - 1]; + if (! $this->isCallableNode($finalFunction)) { + return null; + } + + $finalCall = $this->createFunctionCall($finalFunction, [$input]); + + // If the pipe was part of an assignment, maintain the assignment + if ($originalExpression->expr instanceof Assign) { + $newAssign = new Assign( + $originalExpression->expr->var, + $finalCall + ); + $statements[] = new Expression($newAssign); + } else { + $statements[] = new Expression($finalCall); + } + + return $statements; + } + + private function isCallableNode(Node $node): bool + { + return $node instanceof FuncCall || + $node instanceof Closure || + $node instanceof ArrowFunction || + ($node instanceof Variable) || + ($node instanceof MethodCall) || + ($node instanceof StaticCall); + } + + /** + * @param Node[] $arguments + */ + private function createFunctionCall(Node $function, array $arguments): Node + { + if ($function instanceof FuncCall) { + return new FuncCall($function->name, $this->nodeFactory->createArgs($arguments)); + } + + if ($function instanceof Variable) { + return new FuncCall($function, $this->nodeFactory->createArgs($arguments)); + } + + if ($function instanceof Closure || $function instanceof ArrowFunction) { + return new FuncCall($function, $this->nodeFactory->createArgs($arguments)); + } + + if ($function instanceof MethodCall) { + $clonedMethodCall = clone $function; + $clonedMethodCall->args = $this->nodeFactory->createArgs($arguments); + return $clonedMethodCall; + } + + if ($function instanceof StaticCall) { + $clonedStaticCall = clone $function; + $clonedStaticCall->args = $this->nodeFactory->createArgs($arguments); + return $clonedStaticCall; + } + + return new FuncCall($function, $this->nodeFactory->createArgs($arguments)); + } +} From 0fa81308ebb7f1e324cffb54f2db61a107ab0c4b Mon Sep 17 00:00:00 2001 From: Arshid Date: Sun, 12 Oct 2025 10:00:49 +0530 Subject: [PATCH 03/12] Downgrade php85 pipe --- .github/workflows/tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 51982b97..bb8f5956 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -16,7 +16,7 @@ jobs: matrix: php-version: ['8.2', '8.5'] os: [ubuntu-latest] - + runs-on: ${{ matrix.os }} timeout-minutes: 3 @@ -33,4 +33,4 @@ jobs: - uses: "ramsey/composer-install@v2" - - run: vendor/bin/phpunit \ No newline at end of file + - run: vendor/bin/phpunit From e6a37230cf38eb380ddf756fc6d14d3d6a65e77b Mon Sep 17 00:00:00 2001 From: Arshid Date: Sun, 12 Oct 2025 10:17:00 +0530 Subject: [PATCH 04/12] Downgrade php85 pipe --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index bb8f5956..a1b8abb4 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -33,4 +33,4 @@ jobs: - uses: "ramsey/composer-install@v2" - - run: vendor/bin/phpunit + - run: vendor/bin/phpunit \ No newline at end of file From 74dd84b123d9b88236a0624da7b7dd426e06c5b8 Mon Sep 17 00:00:00 2001 From: Arshid Date: Sun, 12 Oct 2025 13:16:04 +0530 Subject: [PATCH 05/12] CI - PHP85 --- .../Fixture/complex_pipe_chain.php.inc | 2 -- 1 file changed, 2 deletions(-) diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc index 3a0bdfaf..3ce8ee5b 100644 --- a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc @@ -10,7 +10,6 @@ $processed = $data |> strtoupper(...) |> (fn($str) => str_replace('WORLD', 'PHP', $str)) |> addslashes(...); - ?> ----- str_replace('WORLD', 'PHP', $str))($result2); $processed = addslashes($result3); - ?> \ No newline at end of file From fb82734789ee7af0dad1b889001c3aed2a009f28 Mon Sep 17 00:00:00 2001 From: Arshid Date: Sun, 12 Oct 2025 13:44:11 +0530 Subject: [PATCH 06/12] Downgrade php85 pipe --- config/set/downgrade-php85.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/set/downgrade-php85.php b/config/set/downgrade-php85.php index 8863cfd5..bac19e15 100644 --- a/config/set/downgrade-php85.php +++ b/config/set/downgrade-php85.php @@ -5,6 +5,7 @@ use Rector\Config\RectorConfig; use Rector\DowngradePhp85\Rector\Class_\DowngradeFinalPropertyPromotionRector; use Rector\DowngradePhp85\Rector\FuncCall\DowngradeArrayFirstLastRector; +use Rector\DowngradePhp85\Rector\StmtsAwareInterface\DowngradePipeOperatorRector; use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector; use Rector\Renaming\Rector\MethodCall\RenameMethodRector; use Rector\Renaming\ValueObject\MethodCallRename; @@ -16,6 +17,7 @@ $rectorConfig->rules([ DowngradeArrayFirstLastRector::class, DowngradeFinalPropertyPromotionRector::class, + DowngradePipeOperatorRector::class, ]); // https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_driver_specific_pdo_constants_and_methods From 9b77861ce50a5c42923e5b19a594de76d717d707 Mon Sep 17 00:00:00 2001 From: Arshid Date: Sun, 12 Oct 2025 13:49:09 +0530 Subject: [PATCH 07/12] Downgrade php85 pipe --- .../DowngradePipeOperatorRector.php | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php index 98079b54..77b25d2a 100644 --- a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php +++ b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php @@ -63,7 +63,7 @@ public function getNodeTypes(): array { return [StmtsAwareInterface::class]; } - + /** * @param StmtsAwareInterface $node */ @@ -125,16 +125,13 @@ private function processPipeOperation(Pipe $pipe, Expression $originalExpression // For simple case: single pipe operation if (count($pipeChain) === 2) { $replacement = $this->createSimplePipeReplacement($pipeChain[0], $pipeChain[1]); - if ($replacement === null) { + if (! $replacement instanceof Node) { return null; } // If the pipe was part of an assignment, maintain the assignment if ($originalExpression->expr instanceof Assign) { - $newAssign = new Assign( - $originalExpression->expr->var, - $replacement - ); + $newAssign = new Assign($originalExpression->expr->var, $replacement); return [new Expression($newAssign)]; } @@ -183,7 +180,7 @@ private function createMultiplePipeReplacement(array $pipeChain, Expression $ori $tempVariableCounter = 0; // Create all intermediate assignments - for ($i = 1; $i < count($pipeChain) - 1; $i++) { + for ($i = 1; $i < count($pipeChain) - 1; ++$i) { $function = $pipeChain[$i]; if (! $this->isCallableNode($function)) { @@ -206,17 +203,14 @@ private function createMultiplePipeReplacement(array $pipeChain, Expression $ori return null; } - $finalCall = $this->createFunctionCall($finalFunction, [$input]); + $node = $this->createFunctionCall($finalFunction, [$input]); // If the pipe was part of an assignment, maintain the assignment if ($originalExpression->expr instanceof Assign) { - $newAssign = new Assign( - $originalExpression->expr->var, - $finalCall - ); + $newAssign = new Assign($originalExpression->expr->var, $node); $statements[] = new Expression($newAssign); } else { - $statements[] = new Expression($finalCall); + $statements[] = new Expression($node); } return $statements; @@ -235,32 +229,32 @@ private function isCallableNode(Node $node): bool /** * @param Node[] $arguments */ - private function createFunctionCall(Node $function, array $arguments): Node + private function createFunctionCall(Node $node, array $arguments): Node { - if ($function instanceof FuncCall) { - return new FuncCall($function->name, $this->nodeFactory->createArgs($arguments)); + if ($node instanceof FuncCall) { + return new FuncCall($node->name, $this->nodeFactory->createArgs($arguments)); } - if ($function instanceof Variable) { - return new FuncCall($function, $this->nodeFactory->createArgs($arguments)); + if ($node instanceof Variable) { + return new FuncCall($node, $this->nodeFactory->createArgs($arguments)); } - if ($function instanceof Closure || $function instanceof ArrowFunction) { - return new FuncCall($function, $this->nodeFactory->createArgs($arguments)); + if ($node instanceof Closure || $node instanceof ArrowFunction) { + return new FuncCall($node, $this->nodeFactory->createArgs($arguments)); } - if ($function instanceof MethodCall) { - $clonedMethodCall = clone $function; + if ($node instanceof MethodCall) { + $clonedMethodCall = clone $node; $clonedMethodCall->args = $this->nodeFactory->createArgs($arguments); return $clonedMethodCall; } - if ($function instanceof StaticCall) { - $clonedStaticCall = clone $function; + if ($node instanceof StaticCall) { + $clonedStaticCall = clone $node; $clonedStaticCall->args = $this->nodeFactory->createArgs($arguments); return $clonedStaticCall; } - return new FuncCall($function, $this->nodeFactory->createArgs($arguments)); + return new FuncCall($node, $this->nodeFactory->createArgs($arguments)); } } From f3afd0c8b87e8954925d72bf5cfef4491b00c9a6 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 25 Oct 2025 22:17:34 +0700 Subject: [PATCH 08/12] clean clone usage --- .../DowngradePipeOperatorRector.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php index 77b25d2a..a4f27182 100644 --- a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php +++ b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php @@ -244,15 +244,13 @@ private function createFunctionCall(Node $node, array $arguments): Node } if ($node instanceof MethodCall) { - $clonedMethodCall = clone $node; - $clonedMethodCall->args = $this->nodeFactory->createArgs($arguments); - return $clonedMethodCall; + $node->args = $this->nodeFactory->createArgs($arguments); + return $node; } if ($node instanceof StaticCall) { - $clonedStaticCall = clone $node; - $clonedStaticCall->args = $this->nodeFactory->createArgs($arguments); - return $clonedStaticCall; + $node->args = $this->nodeFactory->createArgs($arguments); + return $node; } return new FuncCall($node, $this->nodeFactory->createArgs($arguments)); From 2431bb28fd13d6f643b4124895a5ce9a64a11548 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 25 Oct 2025 22:19:27 +0700 Subject: [PATCH 09/12] use NamedVariableFactory --- .../DowngradePipeOperatorRector.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php index a4f27182..05907664 100644 --- a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php +++ b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php @@ -15,6 +15,7 @@ use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Expression; use Rector\Contract\PhpParser\Node\StmtsAwareInterface; +use Rector\NodeFactory\NamedVariableFactory; use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -25,6 +26,12 @@ */ final class DowngradePipeOperatorRector extends AbstractRector { + public function __construct( + private readonly NamedVariableFactory $namedVariableFactory + ) + { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -187,8 +194,7 @@ private function createMultiplePipeReplacement(array $pipeChain, Expression $ori return null; } - $tempVarName = 'result' . (++$tempVariableCounter); - $tempVar = new Variable($tempVarName); + $tempVar = $this->namedVariableFactory->createVariable('result', $originalExpression); $functionCall = $this->createFunctionCall($function, [$input]); $assign = new Assign($tempVar, $functionCall); From 77f0fa6994914ee883c46d4e1936690b1045867b Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 25 Oct 2025 15:20:14 +0000 Subject: [PATCH 10/12] [ci-review] Rector Rectify --- .../StmtsAwareInterface/DowngradePipeOperatorRector.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php index 05907664..0b09dfbc 100644 --- a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php +++ b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php @@ -28,8 +28,7 @@ final class DowngradePipeOperatorRector extends AbstractRector { public function __construct( private readonly NamedVariableFactory $namedVariableFactory - ) - { + ) { } public function getRuleDefinition(): RuleDefinition @@ -184,7 +183,6 @@ private function createMultiplePipeReplacement(array $pipeChain, Expression $ori { $input = $pipeChain[0]; $statements = []; - $tempVariableCounter = 0; // Create all intermediate assignments for ($i = 1; $i < count($pipeChain) - 1; ++$i) { From e7730206bf5bf304acd85de0c8eac62c76ac3eb7 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 25 Oct 2025 22:20:21 +0700 Subject: [PATCH 11/12] clean check --- .../StmtsAwareInterface/DowngradePipeOperatorRector.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php index 0b09dfbc..04e0cbdc 100644 --- a/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php +++ b/rules/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRector.php @@ -225,9 +225,9 @@ private function isCallableNode(Node $node): bool return $node instanceof FuncCall || $node instanceof Closure || $node instanceof ArrowFunction || - ($node instanceof Variable) || - ($node instanceof MethodCall) || - ($node instanceof StaticCall); + $node instanceof Variable || + $node instanceof MethodCall || + $node instanceof StaticCall; } /** From d7f6630c6eeaa57cd97e98d07541a736321b17b7 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 25 Oct 2025 22:29:52 +0700 Subject: [PATCH 12/12] clean up result variable --- .../Fixture/complex_pipe_chain.php.inc | 8 ++++---- .../Fixture/multiple_pipes.php.inc | 6 +++--- .../Fixture/pipe_in_assignment.php.inc | 4 ++-- .../Fixture/pipe_with_closure.php.inc | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc index 3ce8ee5b..4d8502e0 100644 --- a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/complex_pipe_chain.php.inc @@ -19,8 +19,8 @@ declare(strict_types=1); namespace Rector\Tests\DowngradePhp85\Rector\StmtsAwareInterface\DowngradePipeOperatorRector\Fixture; $data = " Hello, World! "; -$result1 = trim($data); -$result2 = strtoupper($result1); -$result3 = (fn($str) => str_replace('WORLD', 'PHP', $str))($result2); -$processed = addslashes($result3); +$result = trim($data); +$result = strtoupper($result); +$result = (fn($str) => str_replace('WORLD', 'PHP', $str))($result); +$processed = addslashes($result); ?> \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/multiple_pipes.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/multiple_pipes.php.inc index 420d8f2c..ed0828c0 100644 --- a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/multiple_pipes.php.inc +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/multiple_pipes.php.inc @@ -19,8 +19,8 @@ declare(strict_types=1); namespace Rector\Tests\DowngradePhp85\Rector\StmtsAwareInterface\DowngradePipeOperatorRector\Fixture; $value = "hello world"; -$result1 = function3($value); -$result2 = function2($result1); -$result = function1($result2); +$result = function3($value); +$result = function2($result); +$result = function1($result); ?> \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_in_assignment.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_in_assignment.php.inc index 12ae6d00..cffe3785 100644 --- a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_in_assignment.php.inc +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_in_assignment.php.inc @@ -16,7 +16,7 @@ declare(strict_types=1); namespace Rector\Tests\DowngradePhp85\Rector\StmtsAwareInterface\DowngradePipeOperatorRector\Fixture; $value = " test "; -$result1 = trim($value); -$result = strtoupper($result1); +$result = trim($value); +$result = strtoupper($result); ?> \ No newline at end of file diff --git a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_closure.php.inc b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_closure.php.inc index fddf8b6a..a22ed306 100644 --- a/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_closure.php.inc +++ b/rules-tests/DowngradePhp85/Rector/StmtsAwareInterface/DowngradePipeOperatorRectorTest/Fixture/pipe_with_closure.php.inc @@ -14,7 +14,7 @@ declare(strict_types=1); namespace Rector\Tests\DowngradePhp85\Rector\StmtsAwareInterface\DowngradePipeOperatorRector\Fixture; -$result1 = (fn($x) => strtoupper($x))("hello"); -$result = (fn($y) => $y . '!')($result1); +$result = (fn($x) => strtoupper($x))("hello"); +$result = (fn($y) => $y . '!')($result); ?> \ No newline at end of file