From 9efe8afb193958ac387841ad701dd1f9c710b3a8 Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 10 Nov 2025 16:41:58 +0100 Subject: [PATCH] Add QueryParamAccessRector --- config/rector/sets/cakephp53.php | 2 + .../MethodCall/QueryParamAccessRector.php | 94 +++++++++++++++++++ .../Fixture/fixture.php.inc | 57 +++++++++++ .../Fixture/skip_other_params.php.inc | 51 ++++++++++ .../QueryParamAccessRectorTest.php | 28 ++++++ .../config/configured_rule.php | 9 ++ 6 files changed, 241 insertions(+) create mode 100644 src/Rector/Rector/MethodCall/QueryParamAccessRector.php create mode 100644 tests/TestCase/Rector/MethodCall/QueryParamAccessRector/Fixture/fixture.php.inc create mode 100644 tests/TestCase/Rector/MethodCall/QueryParamAccessRector/Fixture/skip_other_params.php.inc create mode 100644 tests/TestCase/Rector/MethodCall/QueryParamAccessRector/QueryParamAccessRectorTest.php create mode 100644 tests/TestCase/Rector/MethodCall/QueryParamAccessRector/config/configured_rule.php diff --git a/config/rector/sets/cakephp53.php b/config/rector/sets/cakephp53.php index 356eac2..55bb177 100644 --- a/config/rector/sets/cakephp53.php +++ b/config/rector/sets/cakephp53.php @@ -4,6 +4,7 @@ use Cake\Upgrade\Rector\Rector\MethodCall\EntityIsEmptyRector; use Cake\Upgrade\Rector\Rector\MethodCall\EntityPatchRector; use Cake\Upgrade\Rector\Rector\MethodCall\NewExprToFuncRector; +use Cake\Upgrade\Rector\Rector\MethodCall\QueryParamAccessRector; use Rector\Config\RectorConfig; use Rector\Renaming\Rector\MethodCall\RenameMethodRector; use Rector\Renaming\Rector\Name\RenameClassRector; @@ -24,4 +25,5 @@ ]); $rectorConfig->rule(EntityIsEmptyRector::class); $rectorConfig->rule(EntityPatchRector::class); + $rectorConfig->rule(QueryParamAccessRector::class); }; diff --git a/src/Rector/Rector/MethodCall/QueryParamAccessRector.php b/src/Rector/Rector/MethodCall/QueryParamAccessRector.php new file mode 100644 index 0000000..b684685 --- /dev/null +++ b/src/Rector/Rector/MethodCall/QueryParamAccessRector.php @@ -0,0 +1,94 @@ +getParam('?') to $request->getQueryParams() + * + * In CakePHP 6.0, the getParam('?') shortcut for accessing query parameters + * is removed. Instead, use getQueryParams() directly. + * + * @see https://book.cakephp.org/6/en/appendices/6-0-migration-guide.html + */ +final class QueryParamAccessRector extends AbstractRector +{ + public function __construct( + private ValueResolver $valueResolver, + ) { + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Change $request->getParam(\'?\') to $request->getQueryParams()', + [ + new CodeSample( + <<<'CODE_SAMPLE' +$queryParams = $request->getParam('?'); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +$queryParams = $request->getQueryParams(); +CODE_SAMPLE, + ), + ], + ); + } + + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + public function refactor(Node $node): ?Node + { + if (!$node instanceof MethodCall) { + return null; + } + + // Must be ->getParam() call + if (!$node->name instanceof Identifier || $node->name->toString() !== 'getParam') { + return null; + } + + // Must have at least one argument + if (count($node->args) < 1) { + return null; + } + + // First argument must be the string '?' + $firstArg = $node->args[0]->value; + if (!$this->valueResolver->isValue($firstArg, '?')) { + return null; + } + + // Check if this is called on a Request object + $callerType = $this->getType($node->var); + if (!$callerType instanceof ObjectType) { + return null; + } + + if ( + !$callerType->isInstanceOf('Cake\Http\ServerRequest')->yes() && + !$callerType->isInstanceOf('Psr\Http\Message\ServerRequestInterface')->yes() + ) { + return null; + } + + // Transform to getQueryParams() with no arguments + $node->name = new Identifier('getQueryParams'); + $node->args = []; + + return $node; + } +} diff --git a/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/Fixture/fixture.php.inc b/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/Fixture/fixture.php.inc new file mode 100644 index 0000000..0bcfadb --- /dev/null +++ b/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/Fixture/fixture.php.inc @@ -0,0 +1,57 @@ +getParam('?'); + + // Should also work with method chaining + $params = $this->getRequest()->getParam('?'); + + return $queryParams; + } + + protected function getRequest(): ServerRequest + { + return new ServerRequest(); + } +} + +?> +----- +getQueryParams(); + + // Should also work with method chaining + $params = $this->getRequest()->getQueryParams(); + + return $queryParams; + } + + protected function getRequest(): ServerRequest + { + return new ServerRequest(); + } +} + +?> diff --git a/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/Fixture/skip_other_params.php.inc b/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/Fixture/skip_other_params.php.inc new file mode 100644 index 0000000..a54cbb6 --- /dev/null +++ b/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/Fixture/skip_other_params.php.inc @@ -0,0 +1,51 @@ +getParam('id'); + $action = $request->getParam('action'); + $paging = $request->getParam('paging'); + + // Should NOT transform: non-Request object + $someObject->getParam('?'); + + return compact('id', 'action', 'paging'); + } +} + +?> +----- +getParam('id'); + $action = $request->getParam('action'); + $paging = $request->getParam('paging'); + + // Should NOT transform: non-Request object + $someObject->getParam('?'); + + return compact('id', 'action', 'paging'); + } +} + +?> diff --git a/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/QueryParamAccessRectorTest.php b/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/QueryParamAccessRectorTest.php new file mode 100644 index 0000000..50e89e3 --- /dev/null +++ b/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/QueryParamAccessRectorTest.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/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/config/configured_rule.php b/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/config/configured_rule.php new file mode 100644 index 0000000..b847bd0 --- /dev/null +++ b/tests/TestCase/Rector/MethodCall/QueryParamAccessRector/config/configured_rule.php @@ -0,0 +1,9 @@ +rule(QueryParamAccessRector::class); +};