Skip to content

Commit 66ac8c6

Browse files
authored
[Php85] Remove calls to deprecated no-op functions (#7128)
* [Php85] Remove calls to deprecated no-op functions * Change namespace from DeadCode to Transform * Check if wrapped func call is already wrapped * Add additional function_exists condition * Move value object into correct namespace * Update code sample * Fix PHPStan issue
1 parent c5876ad commit 66ac8c6

File tree

11 files changed

+326
-1
lines changed

11 files changed

+326
-1
lines changed

config/set/php85.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
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;
1110
use Rector\Php85\Rector\ArrayDimFetch\ArrayFirstLastRector;
1211
use Rector\Php85\Rector\ClassMethod\NullDebugInfoReturnRector;
12+
use Rector\Php85\Rector\Const_\DeprecatedAnnotationToDeprecatedAttributeRector;
1313
use Rector\Php85\Rector\FuncCall\RemoveFinfoBufferContextArgRector;
1414
use Rector\Removing\Rector\FuncCall\RemoveFuncCallArgRector;
1515
use Rector\Removing\ValueObject\RemoveFuncCallArg;
@@ -20,6 +20,8 @@
2020
use Rector\Renaming\ValueObject\MethodCallRename;
2121
use Rector\Renaming\ValueObject\RenameCast;
2222
use Rector\Renaming\ValueObject\RenameClassAndConstFetch;
23+
use Rector\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector;
24+
use Rector\Transform\ValueObject\WrapFuncCallWithPhpVersionIdChecker;
2325

2426
return static function (RectorConfig $rectorConfig): void {
2527
$rectorConfig->rules(
@@ -180,4 +182,16 @@
180182
new RenameCast(String_::class, String_::KIND_BINARY, String_::KIND_STRING),
181183
]
182184
);
185+
186+
// https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_no-op_functions_from_the_resource_to_object_conversion
187+
$rectorConfig->ruleWithConfiguration(
188+
WrapFuncCallWithPhpVersionIdCheckerRector::class,
189+
[
190+
new WrapFuncCallWithPhpVersionIdChecker('curl_close', 80500),
191+
new WrapFuncCallWithPhpVersionIdChecker('curl_share_close', 80500),
192+
new WrapFuncCallWithPhpVersionIdChecker('finfo_close', 80500),
193+
new WrapFuncCallWithPhpVersionIdChecker('imagedestroy', 80500),
194+
new WrapFuncCallWithPhpVersionIdChecker('xml_parser_free', 80500),
195+
]
196+
);
183197
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Rector\Tests\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector\Fixture;
4+
5+
different_function();
6+
different_function(1, 2);
7+
8+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Rector\Tests\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector\Fixture;
4+
5+
if (PHP_VERSION_ID < 80500) {
6+
no_op_function();
7+
}
8+
if (PHP_VERSION_ID < 80500) {
9+
no_op_function(1, 2);
10+
}
11+
12+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Rector\Tests\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector\Fixture;
4+
5+
if (function_exists('no_op_function') && PHP_VERSION_ID < 80500) {
6+
no_op_function();
7+
}
8+
if (PHP_VERSION_ID < 80500 && function_exists('no_op_function')) {
9+
no_op_function(1, 2);
10+
}
11+
12+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Rector\Tests\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector\Fixture;
4+
5+
$foo = no_op_function();
6+
7+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Rector\Tests\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector\Fixture;
4+
5+
if (no_op_function()) {
6+
7+
}
8+
9+
?>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Rector\Tests\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector\Fixture;
4+
5+
no_op_function();
6+
no_op_function(1, 2);
7+
8+
?>
9+
-----
10+
<?php
11+
12+
namespace Rector\Tests\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector\Fixture;
13+
14+
if (function_exists('no_op_function') && PHP_VERSION_ID < 80500) {
15+
no_op_function();
16+
}
17+
if (function_exists('no_op_function') && PHP_VERSION_ID < 80500) {
18+
no_op_function(1, 2);
19+
}
20+
21+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Rector\Tests\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector;
4+
5+
use Iterator;
6+
use PHPUnit\Framework\Attributes\DataProvider;
7+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
8+
9+
final class WrapFuncCallWithPhpVersionIdCheckerRectorTest extends AbstractRectorTestCase
10+
{
11+
#[DataProvider('provideData')]
12+
public function test(string $filePath): void
13+
{
14+
$this->doTestFile($filePath);
15+
}
16+
17+
public static function provideData(): Iterator
18+
{
19+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
20+
}
21+
22+
public function provideConfigFilePath(): string
23+
{
24+
return __DIR__ . '/config/configured_rule.php';
25+
}
26+
}
Lines changed: 13 additions & 0 deletions
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\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector;
7+
use Rector\Transform\ValueObject\WrapFuncCallWithPhpVersionIdChecker;
8+
9+
return RectorConfig::configure()
10+
->withConfiguredRule(
11+
WrapFuncCallWithPhpVersionIdCheckerRector::class,
12+
[new WrapFuncCallWithPhpVersionIdChecker('no_op_function', 80500)]
13+
);
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Transform\Rector\FuncCall;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Arg;
9+
use PhpParser\Node\Expr;
10+
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
11+
use PhpParser\Node\Expr\BinaryOp\Smaller;
12+
use PhpParser\Node\Expr\ConstFetch;
13+
use PhpParser\Node\Expr\FuncCall;
14+
use PhpParser\Node\Name;
15+
use PhpParser\Node\Scalar\Int_;
16+
use PhpParser\Node\Scalar\String_;
17+
use PhpParser\Node\Stmt\Expression;
18+
use PhpParser\Node\Stmt\If_;
19+
use PhpParser\NodeVisitor;
20+
use Rector\Contract\PhpParser\Node\StmtsAwareInterface;
21+
use Rector\Contract\Rector\ConfigurableRectorInterface;
22+
use Rector\Rector\AbstractRector;
23+
use Rector\Transform\ValueObject\WrapFuncCallWithPhpVersionIdChecker;
24+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
25+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
26+
use Webmozart\Assert\Assert;
27+
28+
/**
29+
* @see \Rector\Tests\Transform\Rector\FuncCall\WrapFuncCallWithPhpVersionIdCheckerRector\WrapFuncCallWithPhpVersionIdCheckerRectorTest
30+
*/
31+
final class WrapFuncCallWithPhpVersionIdCheckerRector extends AbstractRector implements ConfigurableRectorInterface
32+
{
33+
/**
34+
* @var WrapFuncCallWithPhpVersionIdChecker[]
35+
*/
36+
private array $wrapFuncCallWithPhpVersionIdCheckers = [];
37+
38+
public function getRuleDefinition(): RuleDefinition
39+
{
40+
return new RuleDefinition(
41+
'Wraps function calls without assignment in a condition to check for a PHP version id',
42+
[
43+
new ConfiguredCodeSample(
44+
<<<'CODE_SAMPLE'
45+
no_op_function();
46+
CODE_SAMPLE
47+
48+
,
49+
<<<'CODE_SAMPLE'
50+
if (function_exists('no_op_function') && PHP_VERSION_ID < 80500) {
51+
no_op_function();
52+
}
53+
CODE_SAMPLE
54+
,
55+
[new WrapFuncCallWithPhpVersionIdChecker('no_op_function', 80500)]
56+
),
57+
]
58+
);
59+
}
60+
61+
/**
62+
* @return array<class-string<Node>>
63+
*/
64+
public function getNodeTypes(): array
65+
{
66+
return [StmtsAwareInterface::class];
67+
}
68+
69+
/**
70+
* @param StmtsAwareInterface $node
71+
*/
72+
public function refactor(Node $node): null|Node|int
73+
{
74+
if ($node->stmts === null) {
75+
return null;
76+
}
77+
78+
if ($this->isWrappedFuncCall($node)) {
79+
return NodeVisitor::DONT_TRAVERSE_CHILDREN;
80+
}
81+
82+
$hasChanged = false;
83+
foreach ($node->stmts as $key => $stmt) {
84+
if (! $stmt instanceof Expression || ! $stmt->expr instanceof FuncCall) {
85+
continue;
86+
}
87+
88+
$funcCall = $stmt->expr;
89+
90+
foreach ($this->wrapFuncCallWithPhpVersionIdCheckers as $wrapFuncCallWithPhpVersionIdChecker) {
91+
if ($this->getName($funcCall) !== $wrapFuncCallWithPhpVersionIdChecker->getFunctionName()) {
92+
continue;
93+
}
94+
95+
$phpVersionIdConst = new ConstFetch(new Name('PHP_VERSION_ID'));
96+
$if = new If_(new BooleanAnd(
97+
new FuncCall(new Name('function_exists'), [new Arg(new String_(
98+
$wrapFuncCallWithPhpVersionIdChecker->getFunctionName()
99+
))]),
100+
new Smaller($phpVersionIdConst, new Int_($wrapFuncCallWithPhpVersionIdChecker->getPhpVersionId())),
101+
));
102+
$if->stmts = [$stmt];
103+
104+
$node->stmts[$key] = $if;
105+
106+
$hasChanged = true;
107+
}
108+
}
109+
110+
if ($hasChanged) {
111+
return $node;
112+
}
113+
114+
return null;
115+
}
116+
117+
public function configure(array $configuration): void
118+
{
119+
Assert::allIsInstanceOf($configuration, WrapFuncCallWithPhpVersionIdChecker::class);
120+
121+
$this->wrapFuncCallWithPhpVersionIdCheckers = $configuration;
122+
}
123+
124+
private function isWrappedFuncCall(StmtsAwareInterface $node): bool
125+
{
126+
if (! $node instanceof If_) {
127+
return false;
128+
}
129+
130+
$phpVersionId = $this->getPhpVersionId($node->cond);
131+
if ($phpVersionId === null) {
132+
return false;
133+
}
134+
135+
if (count($node->stmts) !== 1) {
136+
return false;
137+
}
138+
139+
$childStmt = $node->stmts[0];
140+
141+
if (! $childStmt instanceof Expression || ! $childStmt->expr instanceof FuncCall) {
142+
return false;
143+
}
144+
145+
foreach ($this->wrapFuncCallWithPhpVersionIdCheckers as $wrapFuncCallWithPhpVersionIdChecker) {
146+
if (
147+
$this->getName($childStmt->expr) !== $wrapFuncCallWithPhpVersionIdChecker->getFunctionName()
148+
|| $phpVersionId->value !== $wrapFuncCallWithPhpVersionIdChecker->getPhpVersionId()
149+
) {
150+
continue;
151+
}
152+
153+
return true;
154+
}
155+
156+
return false;
157+
}
158+
159+
private function getPhpVersionId(Expr $expr): ?Int_
160+
{
161+
if ($expr instanceof BooleanAnd) {
162+
return $this->getPhpVersionId($expr->left) ?? $this->getPhpVersionId($expr->right);
163+
}
164+
165+
if (! $expr instanceof Smaller) {
166+
return null;
167+
}
168+
169+
if (! $expr->left instanceof ConstFetch || ! $this->isName($expr->left->name, 'PHP_VERSION_ID')) {
170+
return null;
171+
}
172+
173+
if (! $expr->right instanceof Int_) {
174+
return null;
175+
}
176+
177+
return $expr->right;
178+
}
179+
}

0 commit comments

Comments
 (0)