Skip to content

Commit

Permalink
Adapt PrivateConstantToSelfRector to work on non-final classes, too (#…
Browse files Browse the repository at this point in the history
…3198)

* Adapt PrivateConstantToSelfRector to work on non-final classes, too

* Update rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>

* Update rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>
  • Loading branch information
alfredbez and samsonasik committed Dec 17, 2022
1 parent 14e60b0 commit 21c9e59
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 20 deletions.
36 changes: 33 additions & 3 deletions build/target-repository/docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 412 Rules Overview
# 413 Rules Overview

<br>

Expand Down Expand Up @@ -48,7 +48,7 @@

- [Php81](#php81) (11)

- [Php82](#php82) (1)
- [Php82](#php82) (2)

- [Privatization](#privatization) (8)

Expand Down Expand Up @@ -596,10 +596,25 @@ Change multiple null compares to ?? queue

### ConvertStaticPrivateConstantToSelfRector

Replaces static::* access to private constants with self::* on final classes
Replaces static::* access to private constants with self::*

:wrench: **configure it!**

- class: [`Rector\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector`](../rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php)

```php
use Rector\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector;
use Rector\Config\RectorConfig;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(ConvertStaticPrivateConstantToSelfRector::class, [
ConvertStaticPrivateConstantToSelfRector::ENABLE_FOR_NON_FINAL_CLASSES => false,
]);
};
```


```diff
final class Foo {
private const BAR = 'bar';
Expand Down Expand Up @@ -6511,6 +6526,21 @@ Decorate read-only class with `readonly` attribute

<br>

### Utf8DecodeEncodeToMbConvertEncodingRector

Change deprecated utf8_decode and utf8_encode to mb_convert_encoding

- class: [`Rector\Php82\Rector\FuncCall\Utf8DecodeEncodeToMbConvertEncodingRector`](../rules/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector.php)

```diff
-utf8_decode($value);
-utf8_encode($value);
+mb_convert_encoding($value, 'ISO-8859-1');
+mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1');
```

<br>

## Privatization

### ChangeGlobalVariablesToPropertiesRector
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector;

use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class ConvertStaticPrivateConstantToSelfRectorForNonFinalClassesTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

/**
* @return Iterator<array<string>>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureEnableForNonFinalClasses');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/config_non_final_classes.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Utils\Rector\Tests\Rector\UseDateTimeImmutableRector\Fixture;

class Foo
{
private const BAR = 1;
private function baz(): void
{
echo static::BAR;
}
}
?>
-----
<?php

namespace Utils\Rector\Tests\Rector\UseDateTimeImmutableRector\Fixture;

class Foo
{
private const BAR = 1;
private function baz(): void
{
echo self::BAR;
}
}
?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Utils\Rector\Tests\Rector\UseDateTimeImmutableRector\Fixture;

class ParentClass2
{
private const BAR2 = 1;
public function baz(): void
{
echo static::BAR2;
}
}

class ChildClass2 extends ParentClass2 {
protected const BAR2 = 2;
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Utils\Rector\Tests\Rector\UseDateTimeImmutableRector\Fixture;

class ParentClass3
{
private const BAR3 = 1;
public function baz(): void
{
echo static::BAR3;
}
}

class ChildClass3 extends ParentClass3 {
public const BAR3 = 2;
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Utils\Rector\Tests\Rector\UseDateTimeImmutableRector\Fixture;

class ParentClass1
{
private const BAR1 = 1;
public function baz(): void
{
echo static::BAR1;
}
}

class ChildClass1 extends ParentClass1 {}

?>
-----
<?php

namespace Utils\Rector\Tests\Rector\UseDateTimeImmutableRector\Fixture;

class ParentClass1
{
private const BAR1 = 1;
public function baz(): void
{
echo self::BAR1;
}
}

class ChildClass1 extends ParentClass1 {}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

use Rector\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector;
use Rector\Config\RectorConfig;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig
->ruleWithConfiguration(
ConvertStaticPrivateConstantToSelfRector::class,
[
ConvertStaticPrivateConstantToSelfRector::ENABLE_FOR_NON_FINAL_CLASSES => true,
]
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,41 @@
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Reflection\ClassReflection;
use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Rector\Tests\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector\ConvertStaticPrivateConstantToSelfRectorTest
* @see https://3v4l.org/8Y0ba
* @see https://phpstan.org/r/11d4c850-1a40-4fae-b665-291f96104d11
*/
final class ConvertStaticPrivateConstantToSelfRector extends AbstractRector
final class ConvertStaticPrivateConstantToSelfRector extends AbstractRector implements AllowEmptyConfigurableRectorInterface
{
/**
* @api
* @var string
*/
public const ENABLE_FOR_NON_FINAL_CLASSES = 'enable_for_non_final_classes';

private bool $enableForNonFinalClasses = false;

public function __construct(
private readonly FamilyRelationsAnalyzer $familyRelationsAnalyzer,
private readonly ReflectionResolver $reflectionResolver,
) {
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Replaces static::* access to private constants with self::* on final classes',
'Replaces static::* access to private constants with self::*',
[
new CodeSample(
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
final class Foo {
private const BAR = 'bar';
Expand All @@ -46,6 +64,9 @@ public function run()
}
CODE_SAMPLE
,
[
self::ENABLE_FOR_NON_FINAL_CLASSES => false,
],
),
],
);
Expand All @@ -56,16 +77,24 @@ public function getNodeTypes(): array
return [ClassConstFetch::class];
}

public function configure(array $configuration): void
{
$this->enableForNonFinalClasses = $configuration[self::ENABLE_FOR_NON_FINAL_CLASSES] ?? (bool) current(
$configuration
);
}

/**
* @param ClassConstFetch $node
*/
public function refactor(Node $node): ?ClassConstFetch
{
if (! $this->isUsingStatic($node)) {
$class = $this->betterNodeFinder->findParentType($node, Class_::class);
if (! $class instanceof Class_) {
return null;
}

if (! $this->isPrivateConstant($node)) {
if ($this->shouldBeSkipped($class, $node)) {
return null;
}

Expand All @@ -83,30 +112,82 @@ private function isUsingStatic(ClassConstFetch $classConstFetch): bool
return $classConstFetch->class->toString() === 'static';
}

private function isPrivateConstant(ClassConstFetch $classConstFetch): bool
private function isPrivateConstant(ClassConstFetch $constant, Class_ $class): bool
{
$class = $this->betterNodeFinder->findParentType($classConstFetch, Class_::class);
if (! $class instanceof Class_) {
$constantName = $this->getConstantName($constant);
if ($constantName === null) {
return false;
}
foreach ($class->getConstants() as $classConst) {
if (! $this->nodeNameResolver->isName($classConst, $constantName)) {
continue;
}

return $classConst->isPrivate();
}

return false;
}

private function isUsedInPrivateMethod(ClassConstFetch $node): bool
{
$method = $this->betterNodeFinder->findParentType($node, Node\Stmt\ClassMethod::class);

if (! $class->isFinal()) {
if (! $method instanceof Node\Stmt\ClassMethod) {
return false;
}

$constantName = $classConstFetch->name;
if (! $constantName instanceof Identifier) {
return $method->flags === Class_::MODIFIER_PRIVATE;
}

private function shouldBeSkipped(Class_ $class, ClassConstFetch $classConstFetch): bool
{
if (! $this->isUsingStatic($classConstFetch)) {
return true;
}
if (! $this->isPrivateConstant($classConstFetch, $class)) {
return true;
}
if ($this->isUsedInPrivateMethod($classConstFetch)) {
return false;
}

foreach ($class->getConstants() as $classConst) {
if (! $this->nodeNameResolver->isName($classConst, $constantName->toString())) {
continue;
}
if ($this->enableForNonFinalClasses) {
return $this->isOverwrittenInChildClass($classConstFetch);
}

return $classConst->isPrivate();
return ! $class->isFinal();
}

private function isOverwrittenInChildClass(ClassConstFetch $classConstFetch): bool
{
$constantName = $this->getConstantName($classConstFetch);
if ($constantName === null) {
return false;
}

$classReflection = $this->reflectionResolver->resolveClassReflection($classConstFetch);
if (! $classReflection instanceof ClassReflection) {
return false;
}
$childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);

foreach ($childrenClassReflections as $childrenClassReflection) {
if ($childrenClassReflection->hasConstant($constantName)) {
return true;
}
}

return false;
}

private function getConstantName(ClassConstFetch $classConstFetch): ?string
{
$constantNameIdentifier = $classConstFetch->name;
if (! $constantNameIdentifier instanceof Identifier) {
return null;
}

return $constantNameIdentifier->toString();
}
}

0 comments on commit 21c9e59

Please sign in to comment.