Skip to content

Commit c5876ad

Browse files
[PHP 8.5] Convert @deprecated to #[\Deprecated] on constants (#7155)
Based on the PHP 8.4 rule from #6923, logic was moved to a new `DeprecatedAnnotationToDeprecatedAttributeConverter` service to avoid duplication.
1 parent d0b22d4 commit c5876ad

File tree

8 files changed

+277
-110
lines changed

8 files changed

+277
-110
lines changed

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\Const_\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\Const_\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\Const_\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+
};

rules/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector.php

Lines changed: 3 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,11 @@
44

55
namespace Rector\Php84\Rector\Class_;
66

7-
use Nette\Utils\Strings;
87
use PhpParser\Node;
9-
use PhpParser\Node\Arg;
10-
use PhpParser\Node\Attribute;
11-
use PhpParser\Node\AttributeGroup;
12-
use PhpParser\Node\Identifier;
13-
use PhpParser\Node\Name\FullyQualified;
14-
use PhpParser\Node\Scalar\String_;
158
use PhpParser\Node\Stmt\ClassConst;
169
use PhpParser\Node\Stmt\ClassMethod;
1710
use PhpParser\Node\Stmt\Function_;
18-
use PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode;
19-
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
20-
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
21-
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
22-
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
23-
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
24-
use Rector\NodeTypeResolver\Node\AttributeKey;
25-
use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory;
11+
use Rector\PhpAttribute\DeprecatedAnnotationToDeprecatedAttributeConverter;
2612
use Rector\Rector\AbstractRector;
2713
use Rector\ValueObject\PhpVersionFeature;
2814
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
@@ -34,23 +20,8 @@
3420
*/
3521
final class DeprecatedAnnotationToDeprecatedAttributeRector extends AbstractRector implements MinPhpVersionInterface
3622
{
37-
/**
38-
* @see https://regex101.com/r/qNytVk/1
39-
* @var string
40-
*/
41-
private const VERSION_MATCH_REGEX = '/^(?:(\d+\.\d+\.\d+)\s+)?(.*)$/';
42-
43-
/**
44-
* @see https://regex101.com/r/SVDPOB/1
45-
* @var string
46-
*/
47-
private const START_STAR_SPACED_REGEX = '#^ *\*#ms';
48-
4923
public function __construct(
50-
private readonly PhpDocTagRemover $phpDocTagRemover,
51-
private readonly PhpAttributeGroupFactory $phpAttributeGroupFactory,
52-
private readonly DocBlockUpdater $docBlockUpdater,
53-
private readonly PhpDocInfoFactory $phpDocInfoFactory,
24+
private readonly DeprecatedAnnotationToDeprecatedAttributeConverter $converter,
5425
) {
5526
}
5627

@@ -104,88 +75,11 @@ public function getNodeTypes(): array
10475
*/
10576
public function refactor(Node $node): ?Node
10677
{
107-
$hasChanged = false;
108-
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
109-
if ($phpDocInfo instanceof PhpDocInfo) {
110-
$deprecatedAttributeGroup = $this->handleDeprecated($phpDocInfo);
111-
if ($deprecatedAttributeGroup instanceof AttributeGroup) {
112-
$this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node);
113-
$node->attrGroups = array_merge($node->attrGroups, [$deprecatedAttributeGroup]);
114-
$this->removeDeprecatedAnnotations($phpDocInfo);
115-
$hasChanged = true;
116-
}
117-
}
118-
119-
return $hasChanged ? $node : null;
78+
return $this->converter->convert($node);
12079
}
12180

12281
public function provideMinPhpVersion(): int
12382
{
12483
return PhpVersionFeature::DEPRECATED_ATTRIBUTE;
12584
}
126-
127-
private function handleDeprecated(PhpDocInfo $phpDocInfo): ?AttributeGroup
128-
{
129-
$attributeGroup = null;
130-
$desiredTagValueNodes = $phpDocInfo->getTagsByName('deprecated');
131-
foreach ($desiredTagValueNodes as $desiredTagValueNode) {
132-
if (! $desiredTagValueNode->value instanceof DeprecatedTagValueNode) {
133-
continue;
134-
}
135-
136-
$attributeGroup = $this->createAttributeGroup($desiredTagValueNode->value->description);
137-
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $desiredTagValueNode);
138-
139-
break;
140-
}
141-
142-
return $attributeGroup;
143-
}
144-
145-
private function createAttributeGroup(string $annotationValue): AttributeGroup
146-
{
147-
$matches = Strings::match($annotationValue, self::VERSION_MATCH_REGEX);
148-
149-
if ($matches === null) {
150-
$annotationValue = Strings::replace($annotationValue, self::START_STAR_SPACED_REGEX, '');
151-
152-
return new AttributeGroup([
153-
new Attribute(
154-
new FullyQualified('Deprecated'),
155-
[new Arg(
156-
value: new String_($annotationValue, [
157-
AttributeKey::KIND => String_::KIND_NOWDOC,
158-
AttributeKey::DOC_LABEL => 'TXT',
159-
]),
160-
name: new Identifier('message')
161-
)]
162-
),
163-
]);
164-
}
165-
166-
$since = $matches[1] ?? null;
167-
$message = $matches[2] ?? null;
168-
169-
return $this->phpAttributeGroupFactory->createFromClassWithItems('Deprecated', array_filter([
170-
'message' => $message,
171-
'since' => $since,
172-
]));
173-
}
174-
175-
private function removeDeprecatedAnnotations(PhpDocInfo $phpDocInfo): bool
176-
{
177-
$hasChanged = false;
178-
179-
$desiredTagValueNodes = $phpDocInfo->getTagsByName('deprecated');
180-
foreach ($desiredTagValueNodes as $desiredTagValueNode) {
181-
if (! $desiredTagValueNode->value instanceof GenericTagValueNode) {
182-
continue;
183-
}
184-
185-
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $desiredTagValueNode);
186-
$hasChanged = true;
187-
}
188-
189-
return $hasChanged;
190-
}
19185
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Php85\Rector\Const_;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Stmt\Const_;
9+
use Rector\PhpAttribute\DeprecatedAnnotationToDeprecatedAttributeConverter;
10+
use Rector\Rector\AbstractRector;
11+
use Rector\ValueObject\PhpVersionFeature;
12+
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
13+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
14+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
15+
16+
/**
17+
* @see \Rector\Tests\Php85\Rector\Const_\DeprecatedAnnotationToDeprecatedAttributeRector\DeprecatedAnnotationToDeprecatedAttributeRectorTest
18+
*/
19+
final class DeprecatedAnnotationToDeprecatedAttributeRector extends AbstractRector implements MinPhpVersionInterface
20+
{
21+
22+
public function __construct(
23+
private readonly DeprecatedAnnotationToDeprecatedAttributeConverter $converter,
24+
) {
25+
}
26+
27+
public function getRuleDefinition(): RuleDefinition
28+
{
29+
return new RuleDefinition('Change @deprecated annotation to Deprecated attribute', [
30+
new CodeSample(
31+
<<<'CODE_SAMPLE'
32+
/**
33+
* @deprecated 1.0.0 Use SomeOtherConstant instead
34+
*/
35+
const SomeConstant = 'irrelevant';
36+
CODE_SAMPLE
37+
,
38+
<<<'CODE_SAMPLE'
39+
#[\Deprecated(message: 'Use SomeOtherConstant instead', since: '1.0.0')]
40+
const SomeConstant = 'irrelevant';
41+
CODE_SAMPLE
42+
),
43+
]);
44+
}
45+
46+
public function getNodeTypes(): array
47+
{
48+
return [Const_::class];
49+
}
50+
51+
/**
52+
* @param Const_ $node
53+
*/
54+
public function refactor(Node $node): ?Node
55+
{
56+
return $this->converter->convert($node);
57+
}
58+
59+
public function provideMinPhpVersion(): int
60+
{
61+
return PhpVersionFeature::DEPRECATED_ATTRIBUTE_ON_CONSTANT;
62+
}
63+
}

0 commit comments

Comments
 (0)