Skip to content

Commit 806c421

Browse files
authored
Add QueryParamAccessRector (#352)
1 parent 7ff6c9e commit 806c421

6 files changed

Lines changed: 241 additions & 0 deletions

File tree

config/rector/sets/cakephp53.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use Cake\Upgrade\Rector\Rector\MethodCall\EntityIsEmptyRector;
55
use Cake\Upgrade\Rector\Rector\MethodCall\EntityPatchRector;
66
use Cake\Upgrade\Rector\Rector\MethodCall\NewExprToFuncRector;
7+
use Cake\Upgrade\Rector\Rector\MethodCall\QueryParamAccessRector;
78
use Rector\Config\RectorConfig;
89
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
910
use Rector\Renaming\Rector\Name\RenameClassRector;
@@ -24,4 +25,5 @@
2425
]);
2526
$rectorConfig->rule(EntityIsEmptyRector::class);
2627
$rectorConfig->rule(EntityPatchRector::class);
28+
$rectorConfig->rule(QueryParamAccessRector::class);
2729
};
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Cake\Upgrade\Rector\Rector\MethodCall;
5+
6+
use PhpParser\Node;
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PhpParser\Node\Identifier;
9+
use PHPStan\Type\ObjectType;
10+
use Rector\PhpParser\Node\Value\ValueResolver;
11+
use Rector\Rector\AbstractRector;
12+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
13+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
14+
15+
/**
16+
* Transforms $request->getParam('?') to $request->getQueryParams()
17+
*
18+
* In CakePHP 6.0, the getParam('?') shortcut for accessing query parameters
19+
* is removed. Instead, use getQueryParams() directly.
20+
*
21+
* @see https://book.cakephp.org/6/en/appendices/6-0-migration-guide.html
22+
*/
23+
final class QueryParamAccessRector extends AbstractRector
24+
{
25+
public function __construct(
26+
private ValueResolver $valueResolver,
27+
) {
28+
}
29+
30+
public function getRuleDefinition(): RuleDefinition
31+
{
32+
return new RuleDefinition(
33+
'Change $request->getParam(\'?\') to $request->getQueryParams()',
34+
[
35+
new CodeSample(
36+
<<<'CODE_SAMPLE'
37+
$queryParams = $request->getParam('?');
38+
CODE_SAMPLE
39+
,
40+
<<<'CODE_SAMPLE'
41+
$queryParams = $request->getQueryParams();
42+
CODE_SAMPLE,
43+
),
44+
],
45+
);
46+
}
47+
48+
public function getNodeTypes(): array
49+
{
50+
return [MethodCall::class];
51+
}
52+
53+
public function refactor(Node $node): ?Node
54+
{
55+
if (!$node instanceof MethodCall) {
56+
return null;
57+
}
58+
59+
// Must be ->getParam() call
60+
if (!$node->name instanceof Identifier || $node->name->toString() !== 'getParam') {
61+
return null;
62+
}
63+
64+
// Must have at least one argument
65+
if (count($node->args) < 1) {
66+
return null;
67+
}
68+
69+
// First argument must be the string '?'
70+
$firstArg = $node->args[0]->value;
71+
if (!$this->valueResolver->isValue($firstArg, '?')) {
72+
return null;
73+
}
74+
75+
// Check if this is called on a Request object
76+
$callerType = $this->getType($node->var);
77+
if (!$callerType instanceof ObjectType) {
78+
return null;
79+
}
80+
81+
if (
82+
!$callerType->isInstanceOf('Cake\Http\ServerRequest')->yes() &&
83+
!$callerType->isInstanceOf('Psr\Http\Message\ServerRequestInterface')->yes()
84+
) {
85+
return null;
86+
}
87+
88+
// Transform to getQueryParams() with no arguments
89+
$node->name = new Identifier('getQueryParams');
90+
$node->args = [];
91+
92+
return $node;
93+
}
94+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\QueryParamAccessRector\Fixture;
4+
5+
use Cake\Http\ServerRequest;
6+
7+
class MyController
8+
{
9+
public function index()
10+
{
11+
$request = new ServerRequest();
12+
13+
// Should transform: getParam('?')
14+
$queryParams = $request->getParam('?');
15+
16+
// Should also work with method chaining
17+
$params = $this->getRequest()->getParam('?');
18+
19+
return $queryParams;
20+
}
21+
22+
protected function getRequest(): ServerRequest
23+
{
24+
return new ServerRequest();
25+
}
26+
}
27+
28+
?>
29+
-----
30+
<?php
31+
32+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\QueryParamAccessRector\Fixture;
33+
34+
use Cake\Http\ServerRequest;
35+
36+
class MyController
37+
{
38+
public function index()
39+
{
40+
$request = new ServerRequest();
41+
42+
// Should transform: getParam('?')
43+
$queryParams = $request->getQueryParams();
44+
45+
// Should also work with method chaining
46+
$params = $this->getRequest()->getQueryParams();
47+
48+
return $queryParams;
49+
}
50+
51+
protected function getRequest(): ServerRequest
52+
{
53+
return new ServerRequest();
54+
}
55+
}
56+
57+
?>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\QueryParamAccessRector\Fixture;
4+
5+
use Cake\Http\ServerRequest;
6+
7+
class MyController
8+
{
9+
public function index()
10+
{
11+
$request = new ServerRequest();
12+
13+
// Should NOT transform: getParam with different argument
14+
$id = $request->getParam('id');
15+
$action = $request->getParam('action');
16+
$paging = $request->getParam('paging');
17+
18+
// Should NOT transform: non-Request object
19+
$someObject->getParam('?');
20+
21+
return compact('id', 'action', 'paging');
22+
}
23+
}
24+
25+
?>
26+
-----
27+
<?php
28+
29+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\QueryParamAccessRector\Fixture;
30+
31+
use Cake\Http\ServerRequest;
32+
33+
class MyController
34+
{
35+
public function index()
36+
{
37+
$request = new ServerRequest();
38+
39+
// Should NOT transform: getParam with different argument
40+
$id = $request->getParam('id');
41+
$action = $request->getParam('action');
42+
$paging = $request->getParam('paging');
43+
44+
// Should NOT transform: non-Request object
45+
$someObject->getParam('?');
46+
47+
return compact('id', 'action', 'paging');
48+
}
49+
}
50+
51+
?>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\QueryParamAccessRector;
5+
6+
use Iterator;
7+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
8+
9+
final class QueryParamAccessRectorTest extends AbstractRectorTestCase
10+
{
11+
/**
12+
* @dataProvider provideData()
13+
*/
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
use Cake\Upgrade\Rector\Rector\MethodCall\QueryParamAccessRector;
5+
use Rector\Config\RectorConfig;
6+
7+
return static function (RectorConfig $rectorConfig): void {
8+
$rectorConfig->rule(QueryParamAccessRector::class);
9+
};

0 commit comments

Comments
 (0)