Skip to content

Commit 3f19970

Browse files
authored
add custom rector to transform EntityTrait set method to patch if first param is an array (#315)
* add custom rector to transform EntityTrait set method to patch if first param is an array * add custom rector rule to 5.2 config + add test * fix custom rector applying to non EntityTrait objects
1 parent 776ae17 commit 3f19970

File tree

8 files changed

+187
-0
lines changed

8 files changed

+187
-0
lines changed

config/rector/sets/cakephp52.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22
declare(strict_types=1);
33

4+
use Cake\Upgrade\Rector\Rector\MethodCall\ChangeEntityTraitSetArrayToPatchRector;
45
use Rector\Config\RectorConfig;
56
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
67
use Rector\Renaming\ValueObject\MethodCallRename;
@@ -10,4 +11,5 @@
1011
$rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [
1112
new MethodCallRename('Cake\Console\Arguments', 'getMultipleOption', 'getArrayOption'),
1213
]);
14+
$rectorConfig->rule(ChangeEntityTraitSetArrayToPatchRector::class);
1315
};
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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\Array_;
8+
use PhpParser\Node\Expr\MethodCall;
9+
use PhpParser\Node\Identifier;
10+
use PHPStan\Type\ObjectType;
11+
use Rector\Rector\AbstractRector;
12+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
13+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
14+
15+
final class ChangeEntityTraitSetArrayToPatchRector extends AbstractRector
16+
{
17+
public function getRuleDefinition(): RuleDefinition
18+
{
19+
return new RuleDefinition(
20+
'Replaces $this->set(array) with $this->patch(array) when the first param is an array',
21+
[
22+
new ConfiguredCodeSample(
23+
<<<'CODE_SAMPLE'
24+
$this->set(['key' => 'value']);
25+
CODE_SAMPLE
26+
,
27+
<<<'CODE_SAMPLE'
28+
$this->patch(['key' => 'value']);
29+
CODE_SAMPLE,
30+
),
31+
],
32+
);
33+
}
34+
35+
public function getNodeTypes(): array
36+
{
37+
return [MethodCall::class];
38+
}
39+
40+
public function refactor(Node $node): ?Node
41+
{
42+
if (! $node instanceof MethodCall) {
43+
return null;
44+
}
45+
46+
// Check the method name is "set"
47+
if (! $this->isName($node->name, 'set')) {
48+
return null;
49+
}
50+
51+
// Check that the first argument exists and is an array
52+
if (! isset($node->args[0])) {
53+
return null;
54+
}
55+
56+
$firstArg = $node->args[0]->value;
57+
if (! $firstArg instanceof Array_) {
58+
return null;
59+
}
60+
61+
// Make sure the method is called on an object that uses EntityTrait
62+
$callerType = $this->getType($node->var);
63+
if (! $callerType instanceof ObjectType) {
64+
return null;
65+
}
66+
67+
$classReflection = $callerType->getClassReflection();
68+
if ($classReflection === null) {
69+
return null;
70+
}
71+
if (! $classReflection->hasTraitUse('Cake\Datasource\EntityTrait')) {
72+
return null;
73+
}
74+
75+
// Rename the method to "patch"
76+
$node->name = new Identifier('patch');
77+
78+
return $node;
79+
}
80+
}
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\ChangeEntityTraitSetArrayToPatch;
5+
6+
use Iterator;
7+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
8+
9+
final class ChangeEntityTraitSetArrayToPatchTest 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: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\AddMethodCallArgsRectorTest\Fixture;
4+
5+
use Cake\ORM\Entity;
6+
use Cake\Upgrade\Test\TestCase\Rector\MethodCall\ChangeEntityTraitSetArrayToPatch\Source\OtherClassWithSetMethod;
7+
8+
function mymethod()
9+
{
10+
$object = new Entity();
11+
// This should be changed to patch
12+
$object->set(['paging' => 'test']);
13+
// This should not be changed
14+
$object->set('paging', 'test');
15+
16+
$otherObject = new OtherClassWithSetMethod();
17+
// This should not be changed as well
18+
$otherObject->set(['paging' => 'test']);
19+
}
20+
21+
?>
22+
-----
23+
<?php
24+
25+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\AddMethodCallArgsRectorTest\Fixture;
26+
27+
use Cake\ORM\Entity;
28+
use Cake\Upgrade\Test\TestCase\Rector\MethodCall\ChangeEntityTraitSetArrayToPatch\Source\OtherClassWithSetMethod;
29+
30+
function mymethod()
31+
{
32+
$object = new Entity();
33+
// This should be changed to patch
34+
$object->patch(['paging' => 'test']);
35+
// This should not be changed
36+
$object->set('paging', 'test');
37+
38+
$otherObject = new OtherClassWithSetMethod();
39+
// This should not be changed as well
40+
$otherObject->set(['paging' => 'test']);
41+
}
42+
43+
?>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\ChangeEntityTraitSetArrayToPatch\Source;
5+
6+
class OtherClassWithSetMethod
7+
{
8+
public function set(array $arg1): void
9+
{
10+
}
11+
}
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\ChangeEntityTraitSetArrayToPatchRector;
5+
use Rector\Config\RectorConfig;
6+
7+
return static function (RectorConfig $rectorConfig): void {
8+
$rectorConfig->rule(ChangeEntityTraitSetArrayToPatchRector::class);
9+
};

tests/test_apps/original/RectorCommand-testApply52/src/SomeTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,19 @@
44
namespace MyPlugin;
55

66
use Cake\Console\Arguments;
7+
use Cake\ORM\Entity;
78

89
class SomeTest extends TestCase
910
{
1011
public function testRenames(): void
1112
{
1213
$args = new Arguments([], ['a' => [1, 2]], []);
1314
$option = $args->getMultipleOption('a');
15+
16+
$entity = new Entity();
17+
// This should be changed to patch
18+
$entity->set(['paging' => 'test']);
19+
// This should not be changed
20+
$entity->set('paging', 'test');
1421
}
1522
}

tests/test_apps/upgraded/RectorCommand-testApply52/src/SomeTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,19 @@
44
namespace MyPlugin;
55

66
use Cake\Console\Arguments;
7+
use Cake\ORM\Entity;
78

89
class SomeTest extends TestCase
910
{
1011
public function testRenames(): void
1112
{
1213
$args = new Arguments([], ['a' => [1, 2]], []);
1314
$option = $args->getArrayOption('a');
15+
16+
$entity = new Entity();
17+
// This should be changed to patch
18+
$entity->patch(['paging' => 'test']);
19+
// This should not be changed
20+
$entity->set('paging', 'test');
1421
}
1522
}

0 commit comments

Comments
 (0)