Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/set/order/order.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
services:
Rector\Order\Rector\Class_\OrderPrivateMethodsByUseRector: null
Rector\Order\Rector\Class_\OrderPublicInterfaceMethodRector: null
Rector\Order\Rector\Class_\OrderPropertyByComplexityRector: null
36 changes: 35 additions & 1 deletion docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# All 500 Rectors Overview
# All 501 Rectors Overview

- [Projects](#projects)
- [General](#general)
Expand Down Expand Up @@ -4873,6 +4873,40 @@ Order private methods in order of their use

<br>

### `OrderPropertyByComplexityRector`

- class: [`Rector\Order\Rector\Class_\OrderPropertyByComplexityRector`](/../master/rules/order/src/Rector/Class_/OrderPropertyByComplexityRector.php)
- [test fixtures](/../master/rules/order/tests/Rector/Class_/OrderPropertyByComplexityRector/Fixture)

Order properties by complexity, from the simplest like scalars to the most complex, like union or collections

```diff
-class SomeClass
+class SomeClass implements FoodRecipeInterface
{
/**
* @var string
*/
private $name;

/**
- * @var Type
+ * @var int
*/
- private $service;
+ private $price;

/**
- * @var int
+ * @var Type
*/
- private $price;
+ private $service;
}
```

<br>

### `OrderPublicInterfaceMethodRector`

- class: [`Rector\Order\Rector\Class_\OrderPublicInterfaceMethodRector`](/../master/rules/order/src/Rector/Class_/OrderPublicInterfaceMethodRector.php)
Expand Down
54 changes: 54 additions & 0 deletions rules/order/src/PropertyRanker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Rector\Order;

use PhpParser\Node\Stmt\Property;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\IterableType;
use PHPStan\Type\StringType;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\Exception\NotImplementedException;
use Rector\NodeTypeResolver\Node\AttributeKey;

final class PropertyRanker
{
public function rank(Property $property): int
{
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return 1;
}

$varType = $phpDocInfo->getVarType();
if ($varType instanceof StringType || $varType instanceof IntegerType || $varType instanceof BooleanType || $varType instanceof FloatType) {
return 5;
}

if ($varType instanceof ArrayType || $varType instanceof IterableType) {
return 10;
}

if ($varType instanceof TypeWithClassName) {
return 15;
}

if ($varType instanceof IntersectionType) {
return 20;
}

if ($varType instanceof UnionType) {
return 25;
}

throw new NotImplementedException(get_class($varType));
}
}
156 changes: 156 additions & 0 deletions rules/order/src/Rector/Class_/OrderPropertyByComplexityRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<?php

declare(strict_types=1);

namespace Rector\Order\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Order\PropertyRanker;
use Rector\Order\StmtOrder;

/**
* @see \Rector\Order\Tests\Rector\Class_\OrderPropertyByComplexityRector\OrderPropertyByComplexityRectorTest
*/
final class OrderPropertyByComplexityRector extends AbstractRector
{
/**
* @var StmtOrder
*/
private $stmtOrder;

/**
* @var PropertyRanker
*/
private $propertyRanker;

public function __construct(StmtOrder $stmtOrder, PropertyRanker $propertyRanker)
{
$this->stmtOrder = $stmtOrder;
$this->propertyRanker = $propertyRanker;
}

public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Order properties by complexity, from the simplest like scalars to the most complex, like union or collections',
[
new CodeSample(
<<<'PHP'
class SomeClass
{
/**
* @var string
*/
private $name;

/**
* @var Type
*/
private $service;

/**
* @var int
*/
private $price;
}
PHP
,
<<<'PHP'
class SomeClass implements FoodRecipeInterface
{
/**
* @var string
*/
private $name;

/**
* @var int
*/
private $price;

/**
* @var Type
*/
private $service;
}
PHP

),
]
);
}

/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Class_::class];
}

/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
$propertyByVisibilityByPosition = $this->resolvePropertyByVisibilityByPosition($node);

foreach ($propertyByVisibilityByPosition as $propertyByPosition) {
$propertyNameToRank = [];
$propertyPositionByName = [];

foreach ($propertyByPosition as $position => $property) {
/** @var string $propertyName */
$propertyName = $this->getName($property);

$propertyPositionByName[$position] = $propertyName;
$propertyNameToRank[$propertyName] = $this->propertyRanker->rank($property);
}

asort($propertyNameToRank);
$sortedPropertyByRank = array_keys($propertyNameToRank);

$oldToNewKeys = $this->stmtOrder->createOldToNewKeys($propertyPositionByName, $sortedPropertyByRank);

$this->stmtOrder->reorderClassStmtsByOldToNewKeys($node, $oldToNewKeys);
}

return $node;
}

private function getVisibilityAsString(Property $property): string
{
if ($property->isPrivate()) {
return 'private';
}

if ($property->isProtected()) {
return 'protected';
}

return 'public';
}

/**
* @return Property[][]
*/
private function resolvePropertyByVisibilityByPosition(Class_ $class): array
{
$propertyByVisibilityByPosition = [];
foreach ($class->stmts as $position => $classStmt) {
if (! $classStmt instanceof Property) {
continue;
}

$visibility = $this->getVisibilityAsString($classStmt);
$propertyByVisibilityByPosition[$visibility][$position] = $classStmt;
}

return $propertyByVisibilityByPosition;
}
}
18 changes: 16 additions & 2 deletions rules/order/src/StmtOrder.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,28 @@ public function createOldToNewKeys(array $desiredStmtOrder, array $currentStmtOr

public function reorderClassStmtsByOldToNewKeys(Class_ $node, array $oldToNewKeys): Class_
{
$reorderedStmts = [];

$stmtCount = count($node->stmts);

foreach ($node->stmts as $key => $stmt) {
if (! isset($oldToNewKeys[$key])) {
if (! array_key_exists($key, $oldToNewKeys)) {
$reorderedStmts[$key] = $stmt;
continue;
}

// reorder here
$newKey = $oldToNewKeys[$key];
$node->stmts[$newKey] = $stmt;

$reorderedStmts[$newKey] = $stmt;
}

for ($i = 0; $i < $stmtCount; ++$i) {
if (! array_key_exists($i, $reorderedStmts)) {
continue;
}

$node->stmts[$i] = $reorderedStmts[$i];
}

return $node;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace Rector\Order\Tests\Rector\Class_\OrderPropertyByComplexityRector\Fixture;

use Rector\Order\Tests\Rector\Class_\OrderPropertyByComplexityRector\Source\AnotherSimpleType;
use Rector\Order\Tests\Rector\Class_\OrderPropertyByComplexityRector\Source\ComplexType;

class ComplexTypes
{
/**
* Static cache
*
* @var string[][]
*/
private static $arrayOfStringsNested = [];

/**
* @var int
*/
private $integer;

/**
* @var ComplexType
*/
private $anotherObject;

/**
* @var string[]
*/
private $arrayOfStrings = [];

/**
* @var AnotherSimpleType
*/
private $someObject;

/**
* @var mixed[]
*
* Rich information about methods, e.g.:
*
* 0 => array (6)
* | start => 18
* | visibility => "public" (6)
* | static => FALSE
* | type => "method" (6)
* | name => "secondMethod" (12)
* | end => 29
*/
private $arrayOfMixedPlusContent = [];
}

?>
-----
<?php

namespace Rector\Order\Tests\Rector\Class_\OrderPropertyByComplexityRector\Fixture;

use Rector\Order\Tests\Rector\Class_\OrderPropertyByComplexityRector\Source\AnotherSimpleType;
use Rector\Order\Tests\Rector\Class_\OrderPropertyByComplexityRector\Source\ComplexType;

class ComplexTypes
{
/**
* @var int
*/
private $integer;
/**
* Static cache
*
* @var string[][]
*/
private static $arrayOfStringsNested = [];
/**
* @var string[]
*/
private $arrayOfStrings = [];
/**
* @var mixed[]
*
* Rich information about methods, e.g.:
*
* 0 => array (6)
* | start => 18
* | visibility => "public" (6)
* | static => FALSE
* | type => "method" (6)
* | name => "secondMethod" (12)
* | end => 29
*/
private $arrayOfMixedPlusContent = [];
/**
* @var ComplexType
*/
private $anotherObject;
/**
* @var AnotherSimpleType
*/
private $someObject;
}

?>
Loading