Skip to content

Commit ade4c81

Browse files
authored
[Php85] Add ShellExecFunctionCallOverBackticksRector (#7445)
* [Php85] Add ShellExecFunctionCallOverBackticksRector * fix phpstan * eol * add fixture with variable * update fixture * update fixture * Fix * final touch: fix escape * final touch: grammar * final touch: fix fixture * final touch: fixture for single quote inside * final touch: clean up loop
1 parent 15d2df3 commit ade4c81

File tree

10 files changed

+257
-2
lines changed

10 files changed

+257
-2
lines changed

config/set/php85.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Rector\Php85\Rector\FuncCall\ChrArgModuloRector;
1717
use Rector\Php85\Rector\FuncCall\OrdSingleByteRector;
1818
use Rector\Php85\Rector\FuncCall\RemoveFinfoBufferContextArgRector;
19+
use Rector\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector;
1920
use Rector\Php85\Rector\Switch_\ColonAfterSwitchCaseRector;
2021
use Rector\Removing\Rector\FuncCall\RemoveFuncCallArgRector;
2122
use Rector\Removing\ValueObject\RemoveFuncCallArg;
@@ -43,6 +44,7 @@
4344
SleepToSerializeRector::class,
4445
OrdSingleByteRector::class,
4546
WakeupToUnserializeRector::class,
47+
ShellExecFunctionCallOverBackticksRector::class,
4648
]
4749
);
4850

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector\Fixture;
4+
5+
class Fixture
6+
{
7+
public function run()
8+
{
9+
$output = `ls -al`;
10+
echo "<pre>$output</pre>";
11+
}
12+
}
13+
14+
?>
15+
-----
16+
<?php
17+
18+
namespace Rector\Tests\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector\Fixture;
19+
20+
class Fixture
21+
{
22+
public function run()
23+
{
24+
$output = shell_exec('ls -al');
25+
echo "<pre>$output</pre>";
26+
}
27+
}
28+
29+
?>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector\Fixture;
4+
5+
class MultiVariables
6+
{
7+
public function run()
8+
{
9+
$a = 'X';
10+
$b = 'Y';
11+
12+
$output = `echo $a ' and ' $b`;
13+
echo $output;
14+
15+
$output = `echo $a and $b`;
16+
echo $output;
17+
}
18+
}
19+
20+
?>
21+
-----
22+
<?php
23+
24+
namespace Rector\Tests\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector\Fixture;
25+
26+
class MultiVariables
27+
{
28+
public function run()
29+
{
30+
$a = 'X';
31+
$b = 'Y';
32+
33+
$output = shell_exec('echo ' . $a . ' \' and \' ' . $b);
34+
echo $output;
35+
36+
$output = shell_exec('echo ' . $a . ' and ' . $b);
37+
echo $output;
38+
}
39+
}
40+
41+
?>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector\Fixture;
4+
5+
class SkipEmptyBackticks
6+
{
7+
public function run()
8+
{
9+
``;
10+
}
11+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector\Fixture;
4+
5+
class WithVariable
6+
{
7+
public function run()
8+
{
9+
$dir = __DIR__;
10+
$var = 'example';
11+
12+
$output = `ls $dir`;
13+
$output2 = `echo "value: $var"`;
14+
echo "<pre>$output</pre>";
15+
echo "<pre>$output2</pre>";
16+
}
17+
}
18+
19+
?>
20+
-----
21+
<?php
22+
23+
namespace Rector\Tests\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector\Fixture;
24+
25+
class WithVariable
26+
{
27+
public function run()
28+
{
29+
$dir = __DIR__;
30+
$var = 'example';
31+
32+
$output = shell_exec('ls ' . $dir);
33+
$output2 = shell_exec('echo "value: ' . $var . '"');
34+
echo "<pre>$output</pre>";
35+
echo "<pre>$output2</pre>";
36+
}
37+
}
38+
39+
?>
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\ShellExec\ShellExecFunctionCallOverBackticksRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class ShellExecFunctionCallOverBackticksRectorTest 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: 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\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector;
7+
use Rector\ValueObject\PhpVersion;
8+
9+
return static function (RectorConfig $rectorConfig): void {
10+
$rectorConfig->rule(ShellExecFunctionCallOverBackticksRector::class);
11+
12+
$rectorConfig->phpVersion(PhpVersion::PHP_85);
13+
};
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Php85\Rector\ShellExec;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Arg;
9+
use PhpParser\Node\Expr\BinaryOp\Concat;
10+
use PhpParser\Node\Expr\ShellExec;
11+
use PhpParser\Node\InterpolatedStringPart;
12+
use PhpParser\Node\Scalar\String_;
13+
use Rector\Rector\AbstractRector;
14+
use Rector\ValueObject\PhpVersionFeature;
15+
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
16+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
17+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
18+
19+
/**
20+
* @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_backticks_as_an_alias_for_shell_exec
21+
* @see \Rector\Tests\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector\ShellExecFunctionCallOverBackticksRectorTest
22+
*/
23+
final class ShellExecFunctionCallOverBackticksRector extends AbstractRector implements MinPhpVersionInterface
24+
{
25+
public function getRuleDefinition(): RuleDefinition
26+
{
27+
return new RuleDefinition(
28+
'Replace backticks based with shell_exec() function calls',
29+
[
30+
new CodeSample(
31+
<<<'CODE_SAMPLE'
32+
$output = `ls -al`;
33+
echo "<pre>$output</pre>";
34+
CODE_SAMPLE
35+
,
36+
<<<'CODE_SAMPLE'
37+
$output = shell_exec('ls -al');
38+
echo "<pre>$output</pre>";
39+
CODE_SAMPLE
40+
),
41+
]
42+
);
43+
}
44+
45+
public function getNodeTypes(): array
46+
{
47+
return [ShellExec::class];
48+
}
49+
50+
/**
51+
* @param ShellExec $node
52+
*/
53+
public function refactor(Node $node): ?Node
54+
{
55+
if ($node->parts === []) {
56+
return null;
57+
}
58+
59+
$exprs = [];
60+
foreach ($node->parts as $part) {
61+
if ($part instanceof InterpolatedStringPart) {
62+
$exprs[] = new String_($part->value);
63+
continue;
64+
}
65+
66+
// other parts are Expr (variables, function calls, etc.)
67+
// keep them as-is so they are concatenated
68+
$exprs[] = $part;
69+
}
70+
71+
// reduce to single concatenated expression
72+
$argExpr = array_shift($exprs);
73+
foreach ($exprs as $expr) {
74+
$argExpr = new Concat($argExpr, $expr);
75+
}
76+
77+
// create single Arg and call shell_exec
78+
return $this->nodeFactory->createFuncCall('shell_exec', [new Arg($argExpr)]);
79+
}
80+
81+
public function provideMinPhpVersion(): int
82+
{
83+
return PhpVersionFeature::DEPRECATE_BACKTICKS;
84+
}
85+
}

rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,9 @@ private function renameProperty(ClassLike $classLike, RenameProperty $renameProp
111111
$property->props[0]->name = new VarLikeIdentifier($newProperty);
112112
}
113113

114-
private function refactorPropertyFetch(PropertyFetch|StaticPropertyFetch $propertyFetch): null|PropertyFetch|StaticPropertyFetch
115-
{
114+
private function refactorPropertyFetch(
115+
PropertyFetch|StaticPropertyFetch $propertyFetch
116+
): null|PropertyFetch|StaticPropertyFetch {
116117
foreach ($this->renamedProperties as $renamedProperty) {
117118
$oldProperty = $renamedProperty->getOldProperty();
118119
if (! $this->isName($propertyFetch, $oldProperty)) {

src/ValueObject/PhpVersionFeature.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,4 +834,10 @@ final class PhpVersionFeature
834834
* @var int
835835
*/
836836
public const DEPRECATE_ORD_WITH_MULTIBYTE_STRING = PhpVersion::PHP_85;
837+
838+
/**
839+
* @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_backticks_as_an_alias_for_shell_exec
840+
* @var int
841+
*/
842+
public const DEPRECATE_BACKTICKS = PhpVersion::PHP_85;
837843
}

0 commit comments

Comments
 (0)