Skip to content

Commit c46b62b

Browse files
[PHP 8.5] Convert @deprecated to #[\Deprecated] on constants
Based on the PHP 8.4 rule from #6923
1 parent d0b22d4 commit c46b62b

6 files changed

Lines changed: 219 additions & 1 deletion

File tree

config/set/php85.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PhpParser\Node\Expr\Cast\Int_;
88
use PhpParser\Node\Expr\Cast\String_;
99
use Rector\Config\RectorConfig;
10+
use Rector\Php85\Rector\Const_\DeprecatedAnnotationToDeprecatedAttributeRector;
1011
use Rector\Php85\Rector\ArrayDimFetch\ArrayFirstLastRector;
1112
use Rector\Php85\Rector\ClassMethod\NullDebugInfoReturnRector;
1213
use Rector\Php85\Rector\FuncCall\RemoveFinfoBufferContextArgRector;
@@ -22,7 +23,12 @@
2223

2324
return static function (RectorConfig $rectorConfig): void {
2425
$rectorConfig->rules(
25-
[ArrayFirstLastRector::class, RemoveFinfoBufferContextArgRector::class, NullDebugInfoReturnRector::class]
26+
[
27+
ArrayFirstLastRector::class,
28+
RemoveFinfoBufferContextArgRector::class,
29+
NullDebugInfoReturnRector::class,
30+
DeprecatedAnnotationToDeprecatedAttributeRector::class,
31+
]
2632
);
2733

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

src/ValueObject/PhpVersionFeature.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,4 +780,10 @@ final class PhpVersionFeature
780780
* @var int
781781
*/
782782
public const DEPRECATED_NULL_DEBUG_INFO_RETURN = PhpVersion::PHP_85;
783+
784+
/**
785+
* @see https://wiki.php.net/rfc/attributes-on-constants
786+
* @var int
787+
*/
788+
public const DEPRECATED_ATTRIBUTE_ON_CONSTANT = PhpVersion::PHP_85;
783789
}

0 commit comments

Comments
 (0)