8000 [PHP84] Deprecated annotation to Deprecated attribute (#6923) · rectorphp/rector-src@8874702 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8874702

Browse files
peterfoxsamsonasik
andauthored
[PHP84] Deprecated annotation to Deprecated attribute (#6923)
* working rule * fixes * improvements + tests * clean up * doc change * Update rules/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector.php Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com> * changes * improvements on feedback * better link * Update rules/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector.php Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com> * fixes --------- Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>
1 parent 865e85b commit 8874702

File tree

8 files changed

+300
-0
lines changed

8 files changed

+300
-0
lines changed

config/set/php84.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\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector;
67
use Rector\Php84\Rector\FuncCall\AddEscapeArgumentRector;
78
use Rector\Php84\Rector\FuncCall\RoundingModeEnumRector;
89
use Rector\Php84\Rector\MethodCall\NewMethodCallWithoutParenthesesRector;
@@ -15,6 +16,7 @@
1516
RoundingModeEnumRector::class,
1617
AddEscapeArgumentRector::class,
1718
NewMethodCallWithoutParenthesesRector::class,
19+
DeprecatedAnnotationToDeprecatedAttributeRector::class,
1820
]
1921
);
2022
};
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\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class DeprecatedAnnotationToDeprecatedAttributeRectorTest 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: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector;
4+
5+
final class Fixture
6+
{
7+
/**
8+
* @deprecated use new constant.
9+
*/
10+
public const CONSTANT = 'some reason.';
11+
12+
/**
13+
* @deprecated 1.0.1 use new method.
14+
*/
15+
public function run()
16+
{
17+
}
18+
}
19+
20+
?>
21+
-----
22+
<?php
23+
24+
namespace Rector\Tests\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector;
25+
26+
final class Fixture
27+
{
28+
#[\Deprecated(message: 'use new constant.')]
29+
public const CONSTANT = 'some reason.';
30+
31+
#[\Deprecated(message: 'use new method.', since: '1.0.1')]
32+
public function run()
33+
{
34+
}
35+
}
36+
37+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector;
4+
5+
final class GracefullyRemovesAnnotation
6+
{
7+
/**
8+
* @deprecated some reason.
9+
* @see https://getrector.com
10+
*/
11+
public const FOO = 'foo';
12+
}
13+
14+
?>
15+
-----
16+
<?php
17+
18+
namespace Rector\Tests\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector;
19+
20+
final class GracefullyRemovesAnnotation
21+
{
22+
/**
23+
* @see https://getrector.com
24+
*/
25+
#[\Deprecated(message: 'some reason.')]
26+
public const FOO = 'foo';
27+
}
28+
29+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector;
4+
5+
/**
6+
* @deprecated some reason.
7+
*/
8+
function works_with_functions()
9+
{
10+
}
11+
12+
?>
13+
-----
14+
<?php
15+
16+
namespace Rector\Tests\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector;
17+
18+
#[\Deprecated(message: 'some reason.')]
19+
function works_with_functions()
20+
{
21+
}
22+
23+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector;
7+
use Rector\ValueObject\PhpVersion;
8+
9+
return static function (RectorConfig $rectorConfig): void {
10+
$rectorConfig->rule(DeprecatedAnnotationToDeprecatedAttributeRector::class);
11+
12+
$rectorConfig->phpVersion(PhpVersion::PHP_84);
13+
};
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Php84\Rector\Class_;
6+
7+
use Nette\Utils\Strings;
8+
use PhpParser\Node;
9+
use PhpParser\Node\AttributeGroup;
10+
use PhpParser\Node\Stmt\ClassConst;
11+
use PhpParser\Node\Stmt\ClassMethod;
12+
use PhpParser\Node\Stmt\Function_;
13+
use PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode;
14+
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
15+
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
16+
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
17+
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
18+
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
19+
use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory;
20+
use Rector\Rector\AbstractRector;
21+
use Rector\ValueObject\PhpVersionFeature;
22+
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
23+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
24+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
25+
26+
/**
27+
* @see \Rector\Tests\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector\DeprecatedAnnotationToDeprecatedAttributeRectorTest
28+
*/
29+
final class DeprecatedAnnotationToDeprecatedAttributeRector extends AbstractRector implements MinPhpVersionInterface
30+
{
31+
/**
32+
* @see https://regex101.com/r/qNytVk/1
33+
* @var string
34+
*/
35+
private const VERSION_MATCH_REGEX = '/^(?:(\d+\.\d+\.\d+)\s+)?(.*)$/';
36+
37+
public function __construct(
38+
private readonly PhpDocTagRemover $phpDocTagRemover,
39+
private readonly PhpAttributeGroupFactory $phpAttributeGroupFactory,
40+
private readonly DocBlockUpdater $docBlockUpdater,
41+
private readonly PhpDocInfoFactory $phpDocInfoFactory,
42+
) {
43+
}
44+
45+
public function getRuleDefinition(): RuleDefinition
46+
{
47+
return new RuleDefinition('Change @deprecated annotation to Deprecated attribute', [
48+
new CodeSample(
49+
<<<'CODE_SAMPLE'
50+
/**
51+
* @deprecated 1.0.0 Use SomeOtherClass instead
52+
*/
53+
class SomeClass
54+
{
55+
}
56+
CODE_SAMPLE
57+
,
58+
<<<'CODE_SAMPLE'
59+
#[\Deprecated(message: 'Use SomeOtherClass instead', since: '1.0.0')]
60+
class SomeClass
61+
{
62+
}
63+
CODE_SAMPLE
64+
),
65+
new CodeSample(
66+
<<<'CODE_SAMPLE'
67+
/**
68+
* @deprecated 1.0.0 Use SomeOtherFunction instead
69+
*/
70+
function someFunction()
71+
{
72+
}
73+
CODE_SAMPLE
74+
,
75+
<<<'CODE_SAMPLE'
76+
#[\Deprecated(message: 'Use SomeOtherFunction instead', since: '1.0.0')]
77+
function someFunction()
78+
{
79+
}
80+
CODE_SAMPLE
81+
),
82+
]);
83+
}
84+
85+
public function getNodeTypes(): array
86+
{
87+
return [Function_::class, ClassMethod::class, ClassConst::class];
88+
}
89+
90+
/**
91+
* @param ClassConst|Function_|ClassMethod $node
92+
*/
93+
public function refactor(Node $node): ?Node
94+
{
95+
$hasChanged = false;
96+
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
97+
if ($phpDocInfo instanceof PhpDocInfo) {
98+
$deprecatedAttributeGroup = $this->handleDeprecated($phpDocInfo);
99+
if ($deprecatedAttributeGroup instanceof AttributeGroup) {
100+
$this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node);
101+
$node->attrGroups = array_merge($node->attrGroups, [$deprecatedAttributeGroup]);
102+
$this->removeDeprecatedAnnotations($phpDocInfo);
103+
$hasChanged = true;
104+
}
105+
}
106+
107+
return $hasChanged ? $node : null;
108+
}
109+
110+
public function provideMinPhpVersion(): int
111+
{
112+
return PhpVersionFeature::DEPRECATED_ATTRIBUTE;
113+
}
114+
115+
private function handleDeprecated(PhpDocInfo $phpDocInfo): ?AttributeGroup
116+
{
117+
$attributeGroup = null;
118+
$desiredTagValueNodes = $phpDocInfo->getTagsByName('deprecated');
119+
foreach ($desiredTagValueNodes as $desiredTagValueNode) {
120+
if (! $desiredTagValueNode->value instanceof DeprecatedTagValueNode) {
121+
continue;
122+
}
123+
124+
$attributeGroup = $this->createAttributeGroup($desiredTagValueNode->value->description);
125+
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $desiredTagValueNode);
126+
127+
break;
128+
}
129+
130+
return $attributeGroup;
131+
}
132+
133+
private function createAttributeGroup(string $annotationValue): AttributeGroup
134+
{
135+
$matches = Strings::match($annotationValue, self::VERSION_MATCH_REGEX);
136+
137+
$since = $matches[1] ?? null;
138+
$message = $matches[2] ?? null;
139+
140+
return $this->phpAttributeGroupFactory->createFromClassWithItems('Deprecated', array_filter([
141+
'message' => $message,
142+
'since' => $since,
143+
]));
144+
}
145+
146+
private function removeDeprecatedAnnotations(PhpDocInfo $phpDocInfo): bool
147+
{
148+
$hasChanged = false;
149+
150+
$desiredTagValueNodes = $phpDocInfo->getTagsByName('deprecated');
151+
foreach ($desiredTagValueNodes as $desiredTagValueNode) {
152+
if (! $desiredTagValueNode->value instanceof GenericTagValueNode) {
153+
continue;
154+
}
155+
156+
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $desiredTagValueNode);
157+
$hasChanged = true;
158+
}
159+
160+
return $hasChanged;
161+
}
162+
}

src/ValueObject/PhpVersionFeature.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,4 +732,10 @@ final class PhpVersionFeature
732732
* @var int
733733
*/
734734
public const DEPRECATE_GET_CLASS_WITHOUT_ARGS = PhpVersion::PHP_83;
735+
736+
/**
737+
* @see https://wiki.php.net/rfc/deprecated_attribute
738+
* @var int
739+
*/
740+
public const DEPRECATED_ATTRIBUTE = PhpVersion::PHP_84;
735741
}

0 commit comments

Comments
 (0)
0