mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-18 05:48:21 +01:00
[Order] Add OrderPublicInterfaceMethodRector
This commit is contained in:
parent
82f48f5d84
commit
68680a3cdb
@ -1,2 +1,3 @@
|
||||
services:
|
||||
Rector\Order\Rector\Class_\OrderPrivateMethodsByUseRector: null
|
||||
Rector\Order\Rector\Class_\OrderPublicInterfaceMethodRector: null
|
||||
|
@ -1,4 +1,4 @@
|
||||
# All 499 Rectors Overview
|
||||
# All 500 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
- [General](#general)
|
||||
@ -4873,6 +4873,41 @@ Order private methods in order of their use
|
||||
|
||||
<br>
|
||||
|
||||
### `OrderPublicInterfaceMethodRector`
|
||||
|
||||
- class: [`Rector\Order\Rector\Class_\OrderPublicInterfaceMethodRector`](/../master/rules/order/src/Rector/Class_/OrderPublicInterfaceMethodRector.php)
|
||||
- [test fixtures](/../master/rules/order/tests/Rector/Class_/OrderPublicInterfaceMethodRector/Fixture)
|
||||
|
||||
Order public methods required by interface in custom orderer
|
||||
|
||||
```yaml
|
||||
services:
|
||||
Rector\Order\Rector\Class_\OrderPublicInterfaceMethodRector:
|
||||
$methodOrderByInterfaces:
|
||||
FoodRecipeInterface:
|
||||
- getDescription
|
||||
- process
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```diff
|
||||
class SomeClass implements FoodRecipeInterface
|
||||
{
|
||||
- public function process()
|
||||
+ public function getDescription()
|
||||
{
|
||||
}
|
||||
-
|
||||
- public function getDescription()
|
||||
+ public function process()
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## Oxid
|
||||
|
||||
### `OxidReplaceBackwardsCompatabilityClassRector`
|
||||
|
@ -306,3 +306,4 @@ parameters:
|
||||
|
||||
- '#Use explicit return value over magic &reference#'
|
||||
- '#Method Rector\\Order\\Rector\\Class_\\OrderPrivateMethodsByUseRector\:\:createOldToNewKeys\(\) should return array<int\> but returns array\|false#'
|
||||
- '#Method Rector\\Order\\StmtOrder\:\:createOldToNewKeys\(\) should return array<int\> but returns array\|false#'
|
||||
|
9
rules/order/config/config.yaml
Normal file
9
rules/order/config/config.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
services:
|
||||
_defaults:
|
||||
public: true
|
||||
autowire: true
|
||||
|
||||
Rector\Order\:
|
||||
resource: '../src'
|
||||
exclude:
|
||||
- '../src/Rector/**/*Rector.php'
|
@ -11,12 +11,23 @@ use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Order\StmtOrder;
|
||||
|
||||
/**
|
||||
* @see \Rector\Order\Tests\Rector\Class_\OrderPrivateMethodsByUseRector\OrderPrivateMethodsByUseRectorTest
|
||||
*/
|
||||
final class OrderPrivateMethodsByUseRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var StmtOrder
|
||||
*/
|
||||
private $stmtOrder;
|
||||
|
||||
public function __construct(StmtOrder $stmtOrder)
|
||||
{
|
||||
$this->stmtOrder = $stmtOrder;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Order private methods in order of their use', [
|
||||
@ -77,26 +88,10 @@ PHP
|
||||
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);
|
||||
$privateClassMethodsByKey = $this->resolvePrivateClassMethods($node);
|
||||
|
||||
// order is correct, nothing to change
|
||||
if ($privateClassMethods === $desiredClassMethodOrder) {
|
||||
if ($privateClassMethodsByKey === $desiredClassMethodOrder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -105,19 +100,9 @@ PHP
|
||||
return null;
|
||||
}
|
||||
|
||||
$oldToNewKeys = $this->createOldToNewKeys($desiredClassMethodOrder, $privateClassMethods);
|
||||
$oldToNewKeys = $this->stmtOrder->createOldToNewKeys($desiredClassMethodOrder, $privateClassMethodsByKey);
|
||||
|
||||
foreach ($node->stmts as $key => $stmt) {
|
||||
if (! isset($oldToNewKeys[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// reodre here
|
||||
$newKey = $oldToNewKeys[$key];
|
||||
$node->stmts[$newKey] = $stmt;
|
||||
}
|
||||
|
||||
return $node;
|
||||
return $this->stmtOrder->reorderClassStmtsByOldToNewKeys($node, $oldToNewKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,24 +134,24 @@ PHP
|
||||
return array_unique($localMethodCallInOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $desiredClassMethodOrder
|
||||
* @return int[]
|
||||
*/
|
||||
private function createOldToNewKeys(array $desiredClassMethodOrder, array $privateClassMethods): array
|
||||
private function resolvePrivateClassMethods(Class_ $class): array
|
||||
{
|
||||
$newKeys = [];
|
||||
foreach ($desiredClassMethodOrder as $desiredClassMethod) {
|
||||
foreach ($privateClassMethods as $currentKey => $classMethodName) {
|
||||
if ($classMethodName === $desiredClassMethod) {
|
||||
$newKeys[] = $currentKey;
|
||||
}
|
||||
$privateClassMethods = [];
|
||||
|
||||
foreach ($class->stmts as $key => $classStmt) {
|
||||
if (! $classStmt instanceof ClassMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $classStmt->isPrivate()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var string $classMethodName */
|
||||
$classMethodName = $this->getName($classStmt);
|
||||
$privateClassMethods[$key] = $classMethodName;
|
||||
}
|
||||
|
||||
$oldKeys = array_values($newKeys);
|
||||
sort($oldKeys);
|
||||
|
||||
return array_combine($oldKeys, $newKeys);
|
||||
return $privateClassMethods;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Order\Rector\Class_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\ClassManipulator;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Order\StmtOrder;
|
||||
|
||||
/**
|
||||
* @see \Rector\Order\Tests\Rector\Class_\OrderPublicInterfaceMethodRector\OrderPublicInterfaceMethodRectorTest
|
||||
*/
|
||||
final class OrderPublicInterfaceMethodRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var string[][]
|
||||
*/
|
||||
private $methodOrderByInterfaces = [];
|
||||
|
||||
/**
|
||||
* @var ClassManipulator
|
||||
*/
|
||||
private $classManipulator;
|
||||
|
||||
/**
|
||||
* @var StmtOrder
|
||||
*/
|
||||
private $stmtOrder;
|
||||
|
||||
/**
|
||||
* @param string[][] $methodOrderByInterfaces
|
||||
*/
|
||||
public function __construct(
|
||||
ClassManipulator $classManipulator,
|
||||
StmtOrder $stmtOrder,
|
||||
array $methodOrderByInterfaces = []
|
||||
) {
|
||||
$this->classManipulator = $classManipulator;
|
||||
$this->methodOrderByInterfaces = $methodOrderByInterfaces;
|
||||
$this->stmtOrder = $stmtOrder;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Order public methods required by interface in custom orderer', [
|
||||
new ConfiguredCodeSample(
|
||||
<<<'PHP'
|
||||
class SomeClass implements FoodRecipeInterface
|
||||
{
|
||||
public function process()
|
||||
{
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
}
|
||||
}
|
||||
PHP
|
||||
,
|
||||
<<<'PHP'
|
||||
class SomeClass implements FoodRecipeInterface
|
||||
{
|
||||
public function getDescription()
|
||||
{
|
||||
}
|
||||
public function process()
|
||||
{
|
||||
}
|
||||
}
|
||||
PHP
|
||||
, [
|
||||
'$methodOrderByInterfaces' => [
|
||||
'FoodRecipeInterface' => ['getDescription', 'process'],
|
||||
],
|
||||
]
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$implementedInterfaces = $this->classManipulator->getImplementedInterfaceNames($node);
|
||||
$publicMethodOrderByKey = $this->collectPublicMethods($node);
|
||||
|
||||
foreach ($implementedInterfaces as $implementedInterface) {
|
||||
$methodOrder = $this->methodOrderByInterfaces[$implementedInterface] ?? null;
|
||||
if ($methodOrder === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oldToNewKeys = $this->stmtOrder->createOldToNewKeys($publicMethodOrderByKey, $methodOrder);
|
||||
$this->stmtOrder->reorderClassStmtsByOldToNewKeys($node, $oldToNewKeys);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function collectPublicMethods(Class_ $class): array
|
||||
{
|
||||
$publicClassMethods = [];
|
||||
|
||||
foreach ($class->stmts as $key => $classStmt) {
|
||||
if (! $classStmt instanceof ClassMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $classStmt->isPublic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var string $classMethodName */
|
||||
$classMethodName = $this->getName($classStmt);
|
||||
$publicClassMethods[$key] = $classMethodName;
|
||||
}
|
||||
|
||||
return $publicClassMethods;
|
||||
}
|
||||
}
|
46
rules/order/src/StmtOrder.php
Normal file
46
rules/order/src/StmtOrder.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Order;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
|
||||
final class StmtOrder
|
||||
{
|
||||
/**
|
||||
* @param string[] $desiredStmtOrder
|
||||
* @return int[]
|
||||
*/
|
||||
public function createOldToNewKeys(array $desiredStmtOrder, array $currentStmtOrder): array
|
||||
{
|
||||
$newKeys = [];
|
||||
foreach ($desiredStmtOrder as $desiredClassMethod) {
|
||||
foreach ($currentStmtOrder as $currentKey => $classMethodName) {
|
||||
if ($classMethodName === $desiredClassMethod) {
|
||||
$newKeys[] = $currentKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oldKeys = array_values($newKeys);
|
||||
sort($oldKeys);
|
||||
|
||||
return array_combine($oldKeys, $newKeys);
|
||||
}
|
||||
|
||||
public function reorderClassStmtsByOldToNewKeys(Class_ $node, array $oldToNewKeys): Class_
|
||||
{
|
||||
foreach ($node->stmts as $key => $stmt) {
|
||||
if (! isset($oldToNewKeys[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// reorder here
|
||||
$newKey = $oldToNewKeys[$key];
|
||||
$node->stmts[$newKey] = $stmt;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Order\Tests\Rector\Class_\OrderPublicInterfaceMethodRector\Fixture;
|
||||
|
||||
use Rector\Order\Tests\Rector\Class_\OrderPublicInterfaceMethodRector\Source\FoodRecipeInterface;
|
||||
|
||||
class SomeClass implements FoodRecipeInterface
|
||||
{
|
||||
public function process()
|
||||
{
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Order\Tests\Rector\Class_\OrderPublicInterfaceMethodRector\Fixture;
|
||||
|
||||
use Rector\Order\Tests\Rector\Class_\OrderPublicInterfaceMethodRector\Source\FoodRecipeInterface;
|
||||
|
||||
class SomeClass implements FoodRecipeInterface
|
||||
{
|
||||
public function getDescription()
|
||||
{
|
||||
}
|
||||
public function process()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Order\Tests\Rector\Class_\OrderPublicInterfaceMethodRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Rector\Order\Rector\Class_\OrderPublicInterfaceMethodRector;
|
||||
use Rector\Order\Tests\Rector\Class_\OrderPublicInterfaceMethodRector\Source\FoodRecipeInterface;
|
||||
|
||||
final class OrderPublicInterfaceMethodRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(string $file): void
|
||||
{
|
||||
$this->doTestFile($file);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function getRectorsWithConfiguration(): array
|
||||
{
|
||||
return [
|
||||
OrderPublicInterfaceMethodRector::class => [
|
||||
'$methodOrderByInterfaces' => [
|
||||
FoodRecipeInterface::class => ['getDescription', 'process'],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Order\Tests\Rector\Class_\OrderPublicInterfaceMethodRector\Source;
|
||||
|
||||
interface FoodRecipeInterface
|
||||
{
|
||||
public function getDescription();
|
||||
|
||||
public function process();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user