Add conditional method renaming rector (#2047)

Add conditional method renaming rector
This commit is contained in:
Tomáš Votruba 2019-09-30 09:22:06 +02:00 committed by GitHub
commit 3191a615c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 247 additions and 0 deletions

View File

@ -0,0 +1,132 @@
<?php declare(strict_types=1);
namespace Rector\CakePHP\Rector\MethodCall;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Scalar\String_;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\ConfiguredCodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @see https://book.cakephp.org/4.0/en/appendices/4-0-migration-guide.html
* @see https://github.com/cakephp/cakephp/commit/77017145961bb697b4256040b947029259f66a9b
* @see \Rector\CakePHP\Tests\Rector\MethodCall\RenameMethodCallBasedOnParameterRector\RenameMethodCallBasedOnParameterRectorTest
*/
final class RenameMethodCallBasedOnParameterRector extends AbstractRector
{
/**
* @var mixed[]
*/
private $methodNamesByTypes = [];
/**
* @param mixed[] $methodNamesByTypes
*/
public function __construct(array $methodNamesByTypes = [])
{
$this->methodNamesByTypes = $methodNamesByTypes;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Changes method calls based on matching the first parameter value.',
[
new ConfiguredCodeSample(
<<<'PHP'
$object = new ServerRequest();
$config = $object->getParam('paging');
$object = $object->withParam('paging', ['a value']);
PHP
,
<<<'PHP'
$object = new ServerRequest();
$config = $object->getAttribute('paging');
$object = $object->withAttribute('paging', ['a value']);
PHP
,
[
'getParam' => [
'match_parameter' => 'paging',
'replace_with' => 'getAttribute'
],
'withParam' => [
'match_parameter' => 'paging',
'replace_with' => 'withAttribute'
]
]
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [MethodCall::class];
}
/**
* @param MethodCall $node
*/
public function refactor(Node $node): ?Node
{
$config = $this->matchTypeAndMethodName($node);
if ($config === null) {
return null;
}
$node->name = new Identifier($config['replace_with']);
return $node;
}
/**
* @return string[]
*/
private function matchTypeAndMethodName(MethodCall $methodCall): ?array
{
foreach ($this->methodNamesByTypes as $type => $methodMapping) {
/** @var string[] $methodNames */
$methodNames = array_keys($methodMapping);
if (! $this->isObjectType($methodCall, $type)) {
continue;
}
if (! $this->isNames($methodCall, $methodNames)) {
continue;
}
$currentMethodName = $this->getName($methodCall);
if ($currentMethodName === null) {
continue;
}
$config = $methodMapping[$currentMethodName];
if (empty($config['match_parameter'])) {
continue;
}
if (count($methodCall->args) < 1) {
continue;
}
$arg = $methodCall->args[0];
if (! ($arg->value instanceof String_)) {
continue;
}
if ($arg->value->value !== $config['match_parameter']) {
continue;
}
return $config;
}
return null;
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Rector\CakePHP\Tests\Rector\MethodCall\RenameMethodCallBasedOnParameterRector;
function renameMethodCallBasedOnParameter()
{
$object = new Source\SomeModelType;
$config = $object->getParam('paging');
$object->withParam('paging', 'value');
}
?>
-----
<?php
namespace Rector\CakePHP\Tests\Rector\MethodCall\RenameMethodCallBasedOnParameterRector;
function renameMethodCallBasedOnParameter()
{
$object = new Source\SomeModelType;
$config = $object->getAttribute('paging');
$object->withAttribute('paging', 'value');
}
?>

View File

@ -0,0 +1,29 @@
<?php
namespace Rector\CakePHP\Tests\Rector\MethodCall\RenameMethodCallBasedOnParameterRector;
function renameMethodCallBasedOnParameterNoop()
{
$object = new Source\SomeModelType;
$config = $object->getParam($value);
$config = $object->getParam('other');
$object->withParam('other', 'value');
}
?>
-----
<?php
namespace Rector\CakePHP\Tests\Rector\MethodCall\RenameMethodCallBasedOnParameterRector;
function renameMethodCallBasedOnParameterNoop()
{
$object = new Source\SomeModelType;
$config = $object->getParam($value);
$config = $object->getParam('other');
$object->withParam('other', 'value');
}
?>

View File

@ -0,0 +1,51 @@
<?php declare(strict_types=1);
namespace Rector\CakePHP\Tests\Rector\MethodCall\RenameMethodCallBasedOnParameterRector;
use Rector\CakePHP\Rector\MethodCall\RenameMethodCallBasedOnParameterRector;
use Rector\CakePHP\Tests\Rector\MethodCall\RenameMethodCallBasedOnParameterRector\Source\SomeModelType;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class RenameMethodCallBasedOnParameterRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideDataForTest()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}
/**
* @return string[]
*/
public function provideDataForTest(): iterable
{
yield [__DIR__ . '/Fixture/fixture.php.inc'];
yield [__DIR__ . '/Fixture/fixture2.php.inc'];
}
/**
* @return mixed[]
*/
protected function getRectorsWithConfiguration(): array
{
return [
RenameMethodCallBasedOnParameterRector::class => [
'$methodNamesByTypes' => [
SomeModelType::class => [
'invalidNoOptions' => [],
'getParam' => [
'match_parameter' => 'paging',
'replace_with' => 'getAttribute',
],
'withParam' => [
'match_parameter' => 'paging',
'replace_with' => 'withAttribute',
],
],
],
],
];
}
}

View File

@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace Rector\CakePHP\Tests\Rector\MethodCall\RenameMethodCallBasedOnParameterRector\Source;
final class SomeModelType
{
}