Skip to content

Commit 42cc5ad

Browse files
authored
[Php55] Handle variable interpolated in pattern on PregReplaceEModifierRector (#8922)
1 parent 56f92f7 commit 42cc5ad

3 files changed

Lines changed: 103 additions & 10 deletions

File tree

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\Php55\Rector\FuncCall\PregReplaceEModifierRector\Fixture;
4+
5+
class InterpolatedPattern
6+
{
7+
public function run($str)
8+
{
9+
$pattern = '\\d+';
10+
11+
$str = preg_replace("/$pattern/e", "str_repeat('#', \\1)", $str);
12+
$str = preg_replace("#({$pattern})#ie", "strtoupper('\\1')", $str);
13+
14+
return $str;
15+
}
16+
}
17+
18+
?>
19+
-----
20+
<?php
21+
22+
namespace Rector\Tests\Php55\Rector\FuncCall\PregReplaceEModifierRector\Fixture;
23+
24+
class InterpolatedPattern
25+
{
26+
public function run($str)
27+
{
28+
$pattern = '\\d+';
29+
30+
$str = preg_replace_callback("/$pattern/", function ($matches) {
31+
return str_repeat('#', $matches[1]);
32+
}, $str);
33+
$str = preg_replace_callback("#({$pattern})#i", function ($matches) {
34+
return strtoupper($matches[1]);
35+
}, $str);
36+
37+
return $str;
38+
}
39+
}
40+
41+
?>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php55\Rector\FuncCall\PregReplaceEModifierRector\Fixture;
4+
5+
class SkipInterpolatedPatternNoEModifier
6+
{
7+
public function run($str)
8+
{
9+
$pattern = '\\d+';
10+
11+
$str = preg_replace("/$pattern/i", "str_repeat('#', \\1)", $str);
12+
$str = preg_replace("/e$pattern/", "str_repeat('#', \\1)", $str);
13+
$str = preg_replace("/$pattern/$modifiers", "str_repeat('#', \\1)", $str);
14+
15+
return $str;
16+
}
17+
}

‎rules/Php55/RegexMatcher.php‎

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use Nette\Utils\Strings;
88
use PhpParser\Node\Expr;
99
use PhpParser\Node\Expr\BinaryOp\Concat;
10+
use PhpParser\Node\InterpolatedStringPart;
11+
use PhpParser\Node\Scalar\InterpolatedString;
1012
use PhpParser\Node\Scalar\String_;
1113

1214
final readonly class RegexMatcher
@@ -27,19 +29,11 @@
2729
*/
2830
private const array ALL_MODIFIERS_VALUES = ['i', 'm', 's', 'x', 'e', 'A', 'D', 'S', 'U', 'X', 'J', 'u'];
2931

30-
public function resolvePatternExpressionWithoutEIfFound(Expr $expr): Concat|String_|null
32+
public function resolvePatternExpressionWithoutEIfFound(Expr $expr): Concat|String_|InterpolatedString|null
3133
{
3234
if ($expr instanceof String_) {
3335
$pattern = $expr->value;
34-
35-
$delimiter = $pattern[0];
36-
$delimiter = match ($delimiter) {
37-
'(' => ')',
38-
'{' => '}',
39-
'[' => ']',
40-
'<' => '>',
41-
default => $delimiter
42-
};
36+
$delimiter = $this->resolveClosingDelimiter($pattern);
4337

4438
$modifiers = $this->resolveModifiers((string) Strings::after($pattern, $delimiter, -1));
4539
if (! \str_contains($modifiers, 'e')) {
@@ -55,9 +49,50 @@ public function resolvePatternExpressionWithoutEIfFound(Expr $expr): Concat|Stri
5549
return $this->matchConcat($expr);
5650
}
5751

52+
if ($expr instanceof InterpolatedString) {
53+
return $this->matchInterpolatedString($expr);
54+
}
55+
5856
return null;
5957
}
6058

59+
private function resolveClosingDelimiter(string $pattern): string
60+
{
61+
$delimiter = $pattern[0];
62+
return match ($delimiter) {
63+
'(' => ')',
64+
'{' => '}',
65+
'[' => ']',
66+
'<' => '>',
67+
default => $delimiter
68+
};
69+
}
70+
71+
private function matchInterpolatedString(InterpolatedString $interpolatedString): ?InterpolatedString
72+
{
73+
$firstPart = $interpolatedString->parts[0] ?? null;
74+
if (! $firstPart instanceof InterpolatedStringPart) {
75+
return null;
76+
}
77+
78+
$lastPart = $interpolatedString->parts[array_key_last($interpolatedString->parts)];
79+
if (! $lastPart instanceof InterpolatedStringPart) {
80+
return null;
81+
}
82+
83+
$delimiter = $this->resolveClosingDelimiter($firstPart->value);
84+
$pattern = $lastPart->value;
85+
86+
$modifiers = $this->resolveModifiers((string) Strings::after($pattern, $delimiter, -1));
87+
if (! \str_contains($modifiers, 'e')) {
88+
return null;
89+
}
90+
91+
$lastPart->value = $this->createPatternWithoutE($pattern, $delimiter, $modifiers);
92+
93+
return $interpolatedString;
94+
}
95+
6196
private function resolveModifiers(string $modifiersCandidate): string
6297
{
6398
$modifiers = '';

0 commit comments

Comments
 (0)