Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapt PrivateConstantToSelfRector to work on non-final classes, too #3198

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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_false_non_final_classes';
alfredbez marked this conversation as resolved.
Show resolved Hide resolved

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();
}
}