Add SwapClassMethodArgumentsRector

This commit is contained in:
TomasVotruba 2019-12-08 11:03:08 +01:00
parent ea41267161
commit ae073206d4
6 changed files with 238 additions and 0 deletions

View File

@ -1,5 +1,11 @@
# https://docs.phalcon.io/4.0/en/upgrade#general-notes
services:
# !!! be careful not to run this twice, since it swaps arguments back and forth
# see https://github.com/rectorphp/rector/issues/2408#issue-534441142
Rector\Rector\StaticCall\SwapClassMethodArgumentsRector:
Phalcon\Model:
assign: [0, 2, 1]
Rector\Renaming\Rector\Class_\RenameClassRector:
Phalcon\Acl\Adapter: 'Phalcon\Acl\Adapter\AbstractAdapter'
Phalcon\Acl\Resource: 'Phalcon\Acl\Component'

View File

@ -20,6 +20,7 @@ use Rector\RectorDefinition\RectorDefinition;
final class AddPregQuoteDelimiterRector extends AbstractRector
{
/**
* @var string
* @see https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
*/
private const ALL_MODIFIERS = 'imsxeADSUXJu';

View File

@ -230,3 +230,7 @@ parameters:
# mixed removed
- '#In method "(.*?)", parameter (.*?) has no type\-hint and no @param annotation\. More info\: http\://bit\.ly/usetypehint#'
- '#In method "(.*?)", there is no return type and no @return annotation\. More info\: http\://bit\.ly/usetypehint#'
-
message: '#Class Rector\\Tests\\Rector\\StaticCall\\SwapClassMethodArgumentsRector\\Fixture\\SomeClass not found#'
path: tests/Rector/StaticCall/SwapClassMethodArgumentsRector/SwapClassMethodArgumentsRectorTest.php

View File

@ -0,0 +1,161 @@
<?php
declare(strict_types=1);
namespace Rector\Rector\StaticCall;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\ConfiguredCodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @see \Rector\Tests\Rector\StaticCall\SwapClassMethodArgumentsRector\SwapClassMethodArgumentsRectorTest
*/
final class SwapClassMethodArgumentsRector extends AbstractRector
{
/**
* @var int[][][]
*/
private $newArgumentPositionsByMethodAndClass = [];
/**
* @param int[][][] $newArgumentPositionsByMethodAndClass
*/
public function __construct(array $newArgumentPositionsByMethodAndClass = [])
{
$this->newArgumentPositionsByMethodAndClass = $newArgumentPositionsByMethodAndClass;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Reorder class method arguments, including their calls', [
new ConfiguredCodeSample(
<<<'PHP'
class SomeClass
{
public static function run($first, $second)
{
self::run($first, $second);
}
}
PHP
,
<<<'PHP'
class SomeClass
{
public static function run($second, $first)
{
self::run($second, $first);
}
}
PHP
,
[
'$newArgumentPositionsByMethodAndClass' => [
'SomeClass' => [
'run' => [1, 0],
],
],
]),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [StaticCall::class, MethodCall::class, ClassMethod::class];
}
/**
* @param StaticCall|MethodCall|ClassMethod $node
*/
public function refactor(Node $node): ?Node
{
foreach ($this->newArgumentPositionsByMethodAndClass as $class => $methodNameAndNewArgumentPositions) {
if (! $this->isMethodStaticCallOrClassMethodObjectType($node, $class)) {
continue;
}
foreach ($methodNameAndNewArgumentPositions as $methodName => $newArgumentPositions) {
if (! $this->isMethodStaticCallOrClassMethodName($node, $methodName)) {
continue;
}
if ($node instanceof ClassMethod) {
$this->swapParameters($node, $newArgumentPositions);
} else {
$this->swapArguments($node, $newArgumentPositions);
}
}
}
return $node;
}
/**
* @param StaticCall|MethodCall|ClassMethod $node
*/
private function isMethodStaticCallOrClassMethodName(Node $node, string $methodName): bool
{
if ($node instanceof MethodCall || $node instanceof StaticCall) {
if ($node->name instanceof Expr) {
return false;
}
return $this->isName($node->name, $methodName);
}
if ($node instanceof ClassMethod) {
return $this->isName($node->name, $methodName);
}
return false;
}
/**
* @param MethodCall|StaticCall $node
* @param int[] $newArgumentPositions
*/
private function swapArguments(Node $node, array $newArgumentPositions): void
{
$newArguments = [];
foreach ($newArgumentPositions as $oldPosition => $newPosition) {
if (! isset($node->args[$oldPosition]) || ! isset($node->args[$newPosition])) {
continue;
}
$newArguments[$newPosition] = $node->args[$oldPosition];
}
foreach ($newArguments as $newPosition => $argument) {
$node->args[$newPosition] = $argument;
}
}
/**
* @param int[] $newParameterPositions
*/
private function swapParameters(ClassMethod $classMethod, array $newParameterPositions): void
{
$newArguments = [];
foreach ($newParameterPositions as $oldPosition => $newPosition) {
if (! isset($classMethod->params[$oldPosition]) || ! isset($classMethod->params[$newPosition])) {
continue;
}
$newArguments[$newPosition] = $classMethod->params[$oldPosition];
}
foreach ($newArguments as $newPosition => $argument) {
$classMethod->params[$newPosition] = $argument;
}
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Rector\Tests\Rector\StaticCall\SwapClassMethodArgumentsRector\Fixture;
class SomeClass
{
public static function run($first, $second)
{
self::run($first, $second);
}
}
?>
-----
<?php
namespace Rector\Tests\Rector\StaticCall\SwapClassMethodArgumentsRector\Fixture;
class SomeClass
{
public static function run($second, $first)
{
self::run($second, $first);
}
}
?>

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Rector\StaticCall\SwapClassMethodArgumentsRector;
use Iterator;
use Rector\Rector\StaticCall\SwapClassMethodArgumentsRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Tests\Rector\StaticCall\SwapClassMethodArgumentsRector\Fixture\SomeClass;
final class SwapClassMethodArgumentsRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideDataForTest()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}
public function provideDataForTest(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorsWithConfiguration(): array
{
return [
SwapClassMethodArgumentsRector::class => [
'newArgumentPositionsByMethodAndClass' => [
SomeClass::class => [
'run' => [1, 0],
],
],
],
];
}
}