mirror of https://github.com/rectorphp/rector.git
[SOLID] Add ChangeReadOnlyPropertyWithDefaultValueToConstantRector
This commit is contained in:
parent
4c47186b76
commit
00a3fb3a17
|
@ -3,3 +3,4 @@ services:
|
|||
|
||||
Rector\SOLID\Rector\ClassConst\PrivatizeLocalClassConstantRector: null
|
||||
Rector\SOLID\Rector\Class_\MakeUnusedClassesWithChildrenAbstractRector: null
|
||||
Rector\SOLID\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector: null
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# All 450 Rectors Overview
|
||||
# All 451 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
- [General](#general)
|
||||
|
@ -7662,6 +7662,36 @@ Change nested ifs to early return
|
|||
|
||||
<br>
|
||||
|
||||
### `ChangeReadOnlyPropertyWithDefaultValueToConstantRector`
|
||||
|
||||
- class: `Rector\SOLID\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector`
|
||||
|
||||
Change property with read only status with default value to constant
|
||||
|
||||
```diff
|
||||
class SomeClass
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
- private $magicMethods = [
|
||||
+ private const MAGIC_METHODS = [
|
||||
'__toString',
|
||||
'__wakeup',
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
- foreach ($this->magicMethods as $magicMethod) {
|
||||
+ foreach (self::MAGIC_METHODS as $magicMethod) {
|
||||
echo $magicMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### `FinalizeClassesWithoutChildrenRector`
|
||||
|
||||
- class: `Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector`
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\SOLID\Rector\Property;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Const_ as ConstConst;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\ClassConst;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\Stmt\PropertyProperty;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\PropertyManipulator;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Core\Util\RectorStrings;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
/**
|
||||
* @see \Rector\SOLID\Tests\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector\ChangeReadOnlyPropertyWithDefaultValueToConstantRectorTest
|
||||
*/
|
||||
final class ChangeReadOnlyPropertyWithDefaultValueToConstantRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var PropertyManipulator
|
||||
*/
|
||||
private $propertyManipulator;
|
||||
|
||||
public function __construct(PropertyManipulator $propertyManipulator)
|
||||
{
|
||||
$this->propertyManipulator = $propertyManipulator;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Change property with read only status with default value to constant', [
|
||||
new CodeSample(
|
||||
<<<'PHP'
|
||||
class SomeClass
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $magicMethods = [
|
||||
'__toString',
|
||||
'__wakeup',
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
foreach ($this->magicMethods as $magicMethod) {
|
||||
echo $magicMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
PHP
|
||||
,
|
||||
<<<'PHP'
|
||||
class SomeClass
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private const MAGIC_METHODS = [
|
||||
'__toString',
|
||||
'__wakeup',
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
foreach (self::MAGIC_METHODS as $magicMethod) {
|
||||
echo $magicMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
PHP
|
||||
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Property::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Property $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (count($node->props) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var PropertyProperty $onlyProperty */
|
||||
$onlyProperty = $node->props[0];
|
||||
|
||||
// we need default value
|
||||
if ($onlyProperty->default === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $node->isPrivate()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// is property read only?
|
||||
if (! $this->propertyManipulator->isReadyOnlyProperty($onlyProperty)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->replacePropertyFetchWithClassConstFetch($node, $onlyProperty);
|
||||
|
||||
return $this->createClassConst($node, $onlyProperty);
|
||||
}
|
||||
|
||||
private function createClassConst(Property $property, PropertyProperty $propertyProperty): ClassConst
|
||||
{
|
||||
$constantName = $this->createConstantNameFromProperty($propertyProperty);
|
||||
|
||||
/** @var Expr $defaultValue */
|
||||
$defaultValue = $propertyProperty->default;
|
||||
$constant = new ConstConst($constantName, $defaultValue);
|
||||
|
||||
$classConst = new ClassConst([$constant]);
|
||||
$classConst->flags = $property->flags;
|
||||
$classConst->setAttribute(AttributeKey::PHP_DOC_INFO, $property->getAttribute(AttributeKey::PHP_DOC_INFO));
|
||||
|
||||
return $classConst;
|
||||
}
|
||||
|
||||
private function createConstantNameFromProperty(PropertyProperty $propertyProperty): string
|
||||
{
|
||||
$propertyName = $this->getName($propertyProperty);
|
||||
$constantName = RectorStrings::camelCaseToUnderscore($propertyName);
|
||||
|
||||
return strtoupper($constantName);
|
||||
}
|
||||
|
||||
private function replacePropertyFetchWithClassConstFetch(Node $node, PropertyProperty $propertyProperty): void
|
||||
{
|
||||
$classNode = $node->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$propertyName = $this->getName($propertyProperty);
|
||||
$constantName = $this->createConstantNameFromProperty($propertyProperty);
|
||||
|
||||
$this->traverseNodesWithCallable($classNode, function (Node $node) use ($propertyName, $constantName) {
|
||||
if (! $node instanceof PropertyFetch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isName($node->var, 'this')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isName($node->name, $propertyName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// replace with constant fetch
|
||||
return new ClassConstFetch(new Name('self'), $constantName);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\SOLID\Tests\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Rector\SOLID\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector;
|
||||
|
||||
final class ChangeReadOnlyPropertyWithDefaultValueToConstantRectorTest 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 ChangeReadOnlyPropertyWithDefaultValueToConstantRector::class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\SOLID\Tests\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector\Fixture;
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $magicMethods = [
|
||||
'__toString',
|
||||
'__wakeup',
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
foreach ($this->magicMethods as $magicMethod) {
|
||||
echo $magicMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\SOLID\Tests\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector\Fixture;
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private const MAGIC_METHODS = [
|
||||
'__toString',
|
||||
'__wakeup',
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
foreach (self::MAGIC_METHODS as $magicMethod) {
|
||||
echo $magicMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\SOLID\Tests\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector\Fixture;
|
||||
|
||||
class SkipMultipleProperties
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $moreMagic, $magicMethods = [
|
||||
'__toString',
|
||||
'__wakeup',
|
||||
];
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\SOLID\Tests\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector\Fixture;
|
||||
|
||||
class SkipOverriden
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $magicMethods = [
|
||||
'__toString',
|
||||
'__wakeup',
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
foreach ($this->magicMethods as $magicMethod) {
|
||||
echo $magicMethod;
|
||||
}
|
||||
|
||||
$this->magicMethods = 123;
|
||||
}
|
||||
}
|
|
@ -107,10 +107,20 @@ final class PropertyManipulator
|
|||
return $propertyFetches;
|
||||
}
|
||||
|
||||
public function isReadyOnlyProperty(PropertyProperty $propertyProperty): bool
|
||||
{
|
||||
foreach ($this->getAllPropertyFetch($propertyProperty) as $propertyFetch) {
|
||||
if (! $this->isReadContext($propertyFetch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isPropertyUsedInReadContext(PropertyProperty $propertyProperty): bool
|
||||
{
|
||||
$property = $this->getProperty($propertyProperty);
|
||||
|
||||
if ($this->isDoctrineProperty($property)) {
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue