[Order] Add OrderPrivateMethodsByUseRector

This commit is contained in:
TomasVotruba 2020-05-03 22:23:58 +02:00
parent 19e71eeb17
commit c3e82579e4
8 changed files with 340 additions and 3 deletions

View File

@ -134,7 +134,8 @@
"Rector\\VendorLocker\\": "packages/vendor-locker/src",
"Rector\\ZendToSymfony\\": "rules/zend-to-symfony/src",
"Rector\\Performance\\": "rules/performance/src",
"Rector\\Naming\\": "rules/naming/src"
"Rector\\Naming\\": "rules/naming/src",
"Rector\\Order\\": "rules/order/src"
}
},
"autoload-dev": {
@ -210,7 +211,8 @@
"Rector\\Utils\\PHPStanStaticTypeMapperChecker\\": "utils/phpstan-static-type-mapper-checker/src",
"Rector\\ZendToSymfony\\Tests\\": "rules/zend-to-symfony/tests",
"Rector\\Performance\\Tests\\": "rules/performance/tests",
"Rector\\Naming\\Tests\\": "rules/naming/tests"
"Rector\\Naming\\Tests\\": "rules/naming/tests",
"Rector\\Order\\Tests\\": "rules/order/tests"
},
"classmap": [
"rules/cakephp/tests/Rector/Name/ImplicitShortClassNameUseStatementRector/Source",

View File

@ -0,0 +1,2 @@
services:
Rector\Order\Rector\Class_\OrderPrivateMethodsByUseRector: null

View File

@ -1,4 +1,4 @@
# All 511 Rectors Overview
# All 512 Rectors Overview
- [Projects](#projects)
- [General](#general)
@ -30,6 +30,7 @@
- [Nette](#nette)
- [NetteTesterToPHPUnit](#nettetestertophpunit)
- [NetteToSymfony](#nettetosymfony)
- [Order](#order)
- [Oxid](#oxid)
- [PHPOffice](#phpoffice)
- [PHPStan](#phpstan)
@ -5161,6 +5162,38 @@ Adds %% to placeholder name of trans() method if missing
<br>
## Order
### `OrderPrivateMethodsByUseRector`
- class: [`Rector\Order\Rector\Class_\OrderPrivateMethodsByUseRector`](/../master/rules/order/src/Rector/Class_/OrderPrivateMethodsByUseRector.php)
- [test fixtures](/../master/rules/order/tests/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture)
Order private methods in order of their use
```diff
class SomeClass
{
public function run()
{
$this->call1();
$this->call2();
}
- private function call2()
+ private function call1()
{
}
- private function call1()
+ private function call2()
{
}
}
```
<br>
## Oxid
### `OxidReplaceBackwardsCompatabilityClassRector`

View File

@ -305,3 +305,4 @@ parameters:
- '#Use explicit return value over magic &reference#'
- '#Method Rector\\Order\\Rector\\Class_\\OrderPrivateMethodsByUseRector\:\:createOldToNewKeys\(\) should return array<int\> but returns array\|false#'

View File

@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
namespace Rector\Order\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
/**
* @see \Rector\Order\Tests\Rector\Class_\OrderPrivateMethodsByUseRector\OrderPrivateMethodsByUseRectorTest
*/
final class OrderPrivateMethodsByUseRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Order private methods in order of their use', [
new CodeSample(
<<<'PHP'
class SomeClass
{
public function run()
{
$this->call1();
$this->call2();
}
private function call2()
{
}
private function call1()
{
}
}
PHP
,
<<<'PHP'
class SomeClass
{
public function run()
{
$this->call1();
$this->call2();
}
private function call1()
{
}
private function call2()
{
}
}
PHP
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Class_::class];
}
/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
$desiredClassMethodOrder = $this->getLocalMethodCallOrder($node);
$privateClassMethods = [];
foreach ($node->stmts as $key => $classStmt) {
if (! $classStmt instanceof ClassMethod) {
continue;
}
if (! $classStmt->isPrivate()) {
continue;
}
/** @var string $classMethodName */
$classMethodName = $this->getName($classStmt);
$privateClassMethods[$key] = $classMethodName;
}
//$privateClassMethodsNames = array_keys($privateClassMethods);
// order is correct, nothing to change
if ($privateClassMethods === $desiredClassMethodOrder) {
return null;
}
// different private method count, one of them is dead probably
if (count($desiredClassMethodOrder) !== count($desiredClassMethodOrder)) {
return null;
}
$oldToNewKeys = $this->createOldToNewKeys($desiredClassMethodOrder, $privateClassMethods);
foreach ($node->stmts as $key => $stmt) {
if (! isset($oldToNewKeys[$key])) {
continue;
}
// reodre here
$newKey = $oldToNewKeys[$key];
$node->stmts[$newKey] = $stmt;
}
return $node;
}
/**
* @return string[]
*/
private function getLocalMethodCallOrder(Class_ $class): array
{
$localMethodCallInOrder = [];
$this->traverseNodesWithCallable($class->getMethods(), function (Node $node) use (&$localMethodCallInOrder) {
if (! $node instanceof MethodCall) {
return null;
}
if (! $this->isVariableName($node->var, 'this')) {
return null;
}
$methodName = $this->getName($node->name);
if ($methodName === null) {
return null;
}
$localMethodCallInOrder[] = $methodName;
return null;
});
return array_unique($localMethodCallInOrder);
}
/**
* @param string[] $desiredClassMethodOrder
* @return int[]
*/
private function createOldToNewKeys(array $desiredClassMethodOrder, array $privateClassMethods): array
{
$newKeys = [];
foreach ($desiredClassMethodOrder as $desiredClassMethod) {
foreach ($privateClassMethods as $currentKey => $classMethodName) {
if ($classMethodName === $desiredClassMethod) {
$newKeys[] = $currentKey;
}
}
}
$oldKeys = array_values($newKeys);
sort($oldKeys);
return array_combine($oldKeys, $newKeys);
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Rector\Order\Tests\Rector\Class_\OrderPrivateMethodsByUseRector\Fixture;
class SomeClass
{
public function run()
{
$this->call1();
$this->call2();
}
private function call2()
{
}
private function call1()
{
}
}
?>
-----
<?php
namespace Rector\Order\Tests\Rector\Class_\OrderPrivateMethodsByUseRector\Fixture;
class SomeClass
{
public function run()
{
$this->call1();
$this->call2();
}
private function call1()
{
}
private function call2()
{
}
}
?>

View File

@ -0,0 +1,53 @@
<?php
namespace Rector\Order\Tests\Rector\Class_\OrderPrivateMethodsByUseRector\Fixture;
class MultiCall
{
public function run()
{
$this->call3();
}
private function call2()
{
}
private function call1()
{
}
private function call3()
{
$this->call1();
$this->call2();
}
}
?>
-----
<?php
namespace Rector\Order\Tests\Rector\Class_\OrderPrivateMethodsByUseRector\Fixture;
class MultiCall
{
public function run()
{
$this->call3();
}
private function call3()
{
$this->call1();
$this->call2();
}
private function call1()
{
}
private function call2()
{
}
}
?>

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Rector\Order\Tests\Rector\Class_\OrderPrivateMethodsByUseRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Order\Rector\Class_\OrderPrivateMethodsByUseRector;
final class OrderPrivateMethodsByUseRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return OrderPrivateMethodsByUseRector::class;
}
}