Skip to content

Commit 968f660

Browse files
arshidkv12Arshidsamsonasik
authored
DowngradeFinalPropertyPromotionRector (#317)
* DowngradeFinalPropertyPromotionRector * DowngradeFinalPropertyPromotionRector * DowngradeFinalPropertyPromotionRector * Support Param nodes in makeNonFinal() * Support Param nodes in makeNonFinal() * DowngradeFinalPropertyPromotionRector * Update rules/DowngradePhp85/Rector/Class_/DowngradeFinalPropertyPromotionRector.php * Update rules/DowngradePhp85/Rector/Class_/DowngradeFinalPropertyPromotionRector.php * Update rules/DowngradePhp85/Rector/Class_/DowngradeFinalPropertyPromotionRector.php * Update rules/DowngradePhp85/Rector/Class_/DowngradeFinalPropertyPromotionRector.php * Update rules/DowngradePhp85/Rector/Class_/DowngradeFinalPropertyPromotionRector.php * Update rules/DowngradePhp85/Rector/Class_/DowngradeFinalPropertyPromotionRector.php * Update rules/DowngradePhp85/Rector/Class_/DowngradeFinalPropertyPromotionRector.php * Update rules/DowngradePhp85/Rector/Class_/DowngradeFinalPropertyPromotionRector.php * Update rules/DowngradePhp85/Rector/Class_/DowngradeFinalPropertyPromotionRector.php --------- Co-authored-by: Arshid <[email protected]> Co-authored-by: Abdul Malik Ikhsan <[email protected]>
1 parent ca20ab3 commit 968f660

File tree

7 files changed

+226
-0
lines changed

7 files changed

+226
-0
lines changed

config/set/downgrade-php85.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
declare(strict_types=1);
44

55
use Rector\Config\RectorConfig;
6+
use Rector\DowngradePhp85\Rector\Class_\DowngradeFinalPropertyPromotionRector;
67
use Rector\DowngradePhp85\Rector\FuncCall\DowngradeArrayFirstLastRector;
78
use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector;
89
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
@@ -14,6 +15,7 @@
1415
$rectorConfig->phpVersion(PhpVersion::PHP_84);
1516
$rectorConfig->rules([
1617
DowngradeArrayFirstLastRector::class,
18+
DowngradeFinalPropertyPromotionRector::class,
1719
]);
1820

1921
// https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_driver_specific_pdo_constants_and_methods
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\DowngradePhp85\Rector\Class_\DowngradeFinalPropertyPromotionRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class DowngradeFinalPropertyPromotionRectorTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Rector\Tests\DowngradeFinalPropertyPromotionRector\Rector\Class_\DowngradePropertyPromotionRector\Fixture;
4+
5+
class Fixture
6+
{
7+
public function __construct(
8+
final public string $id
9+
) {}
10+
}
11+
12+
?>
13+
-----
14+
<?php
15+
16+
namespace Rector\Tests\DowngradeFinalPropertyPromotionRector\Rector\Class_\DowngradePropertyPromotionRector\Fixture;
17+
18+
class Fixture
19+
{
20+
public function __construct(
21+
/**
22+
* @final
23+
*/
24+
public string $id
25+
) {}
26+
}
27+
28+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
namespace Rector\Tests\DowngradeFinalPropertyPromotionRector\Rector\Class_\DowngradePropertyPromotionRector\Fixture;
3+
4+
class ImplicitFinalPropertyPromotion
5+
{
6+
public function __construct(final string $foo)
7+
{
8+
}
9+
}
10+
?>
11+
-----
12+
<?php
13+
namespace Rector\Tests\DowngradeFinalPropertyPromotionRector\Rector\Class_\DowngradePropertyPromotionRector\Fixture;
14+
15+
class ImplicitFinalPropertyPromotion
16+
{
17+
public function __construct(
18+
/**
19+
* @final
20+
*/
21+
public string $foo
22+
)
23+
{
24+
}
25+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Rector\Tests\DowngradeFinalPropertyPromotionRector\Rector\Class_\DowngradePropertyPromotionRector\Fixture;
4+
5+
class SkipFixtureReadonly
6+
{
7+
public function __construct(readonly string $id)
8+
{
9+
}
10+
}
11+
12+
?>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\DowngradePhp85\Rector\Class_\DowngradeFinalPropertyPromotionRector;
7+
8+
return static function (RectorConfig $rectorConfig): void {
9+
$rectorConfig->rule(DowngradeFinalPropertyPromotionRector::class);
10+
};
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\DowngradePhp85\Rector\Class_;
6+
7+
use PhpParser\Builder\Property;
8+
use PhpParser\Node;
9+
use PhpParser\Node\Param;
10+
use PhpParser\Node\Stmt\ClassMethod;
11+
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
12+
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
13+
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
14+
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
15+
use Rector\Privatization\NodeManipulator\VisibilityManipulator;
16+
use Rector\Rector\AbstractRector;
17+
use Rector\ValueObject\MethodName;
18+
use Rector\ValueObject\Visibility;
19+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
20+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
21+
22+
/**
23+
* @changelog https://wiki.php.net/rfc/final_promotion
24+
*
25+
* @see \Rector\Tests\DowngradePhp85\Rector\Class_\DowngradeFinalPropertyPromotionRector\DowngradeFinalPropertyPromotionRectorTest
26+
*/
27+
final class DowngradeFinalPropertyPromotionRector extends AbstractRector
28+
{
29+
/**
30+
* @var string
31+
*/
32+
private const TAGNAME = 'final';
33+
34+
public function __construct(
35+
private readonly VisibilityManipulator $visibilityManipulator,
36+
private readonly DocBlockUpdater $docBlockUpdater,
37+
private readonly PhpDocInfoFactory $phpDocInfoFactory,
38+
) {
39+
}
40+
41+
public function getRuleDefinition(): RuleDefinition
42+
{
43+
return new RuleDefinition('Change constructor final property promotion to @final annotation assign', [
44+
new CodeSample(
45+
<<<'CODE_SAMPLE'
46+
class SomeClass
47+
{
48+
public function __construct(
49+
final public string $id
50+
){}
51+
}
52+
CODE_SAMPLE
53+
54+
,
55+
<<<'CODE_SAMPLE'
56+
class SomeClass
57+
{
58+
public function __construct(
59+
/** @final */
60+
public string $id
61+
) {}
62+
}
63+
CODE_SAMPLE
64+
),
65+
]);
66+
}
67+
68+
/**
69+
* @return array<class-string<Node>>
70+
*/
71+
public function getNodeTypes(): array
72+
{
73+
return [ClassMethod::class];
74+
}
75+
76+
/**
77+
* @param ClassMethod $node
78+
*/
79+
public function refactor(Node $node): ?ClassMethod
80+
{
81+
if (! $this->isName($node, MethodName::CONSTRUCT)) {
82+
return null;
83+
}
84+
85+
$hasChanged = false;
86+
foreach ($node->params as $param) {
87+
if (! $param->isPromoted()) {
88+
continue;
89+
}
90+
91+
if (! $this->visibilityManipulator->hasVisibility($param, Visibility::FINAL)) {
92+
continue;
93+
}
94+
95+
$hasChanged = true;
96+
$this->visibilityManipulator->makeNonFinal($param);
97+
98+
$this->addPhpDocTag($param);
99+
100+
}
101+
102+
if ($hasChanged) {
103+
return $node;
104+
}
105+
106+
return null;
107+
}
108+
109+
private function addPhpDocTag(Property|Param $node): bool
110+
{
111+
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
112+
113+
if ($phpDocInfo->hasByName(self::TAGNAME)) {
114+
return false;
115+
}
116+
117+
$phpDocInfo->addPhpDocTagNode(new PhpDocTagNode('@' . self::TAGNAME, new GenericTagValueNode('')));
118+
$this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node);
119+
return true;
120+
}
121+
}

0 commit comments

Comments
 (0)