Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Rector\Tests\Php55\Rector\FuncCall\PregReplaceEModifierRector\Fixture;

class InterpolatedPattern
{
public function run($str)
{
$pattern = '\\d+';

$str = preg_replace("/$pattern/e", "str_repeat('#', \\1)", $str);
$str = preg_replace("#({$pattern})#ie", "strtoupper('\\1')", $str);

return $str;
}
}

?>
-----
<?php

namespace Rector\Tests\Php55\Rector\FuncCall\PregReplaceEModifierRector\Fixture;

class InterpolatedPattern
{
public function run($str)
{
$pattern = '\\d+';

$str = preg_replace_callback("/$pattern/", function ($matches) {
return str_repeat('#', $matches[1]);
}, $str);
$str = preg_replace_callback("#({$pattern})#i", function ($matches) {
return strtoupper($matches[1]);
}, $str);

return $str;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Rector\Tests\Php55\Rector\FuncCall\PregReplaceEModifierRector\Fixture;

class SkipInterpolatedPatternNoEModifier
{
public function run($str)
{
$pattern = '\\d+';

$str = preg_replace("/$pattern/i", "str_repeat('#', \\1)", $str);
$str = preg_replace("/e$pattern/", "str_repeat('#', \\1)", $str);
$str = preg_replace("/$pattern/$modifiers", "str_repeat('#', \\1)", $str);

return $str;
}
}
55 changes: 45 additions & 10 deletions rules/Php55/RegexMatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use Nette\Utils\Strings;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\InterpolatedStringPart;
use PhpParser\Node\Scalar\InterpolatedString;
use PhpParser\Node\Scalar\String_;

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

public function resolvePatternExpressionWithoutEIfFound(Expr $expr): Concat|String_|null
public function resolvePatternExpressionWithoutEIfFound(Expr $expr): Concat|String_|InterpolatedString|null
{
if ($expr instanceof String_) {
$pattern = $expr->value;

$delimiter = $pattern[0];
$delimiter = match ($delimiter) {
'(' => ')',
'{' => '}',
'[' => ']',
'<' => '>',
default => $delimiter
};
$delimiter = $this->resolveClosingDelimiter($pattern);

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

if ($expr instanceof InterpolatedString) {
return $this->matchInterpolatedString($expr);
}

return null;
}

private function resolveClosingDelimiter(string $pattern): string
{
$delimiter = $pattern[0];
return match ($delimiter) {
'(' => ')',
'{' => '}',
'[' => ']',
'<' => '>',
default => $delimiter
};
}

private function matchInterpolatedString(InterpolatedString $interpolatedString): ?InterpolatedString
{
$firstPart = $interpolatedString->parts[0] ?? null;
if (! $firstPart instanceof InterpolatedStringPart) {
return null;
}

$lastPart = $interpolatedString->parts[array_key_last($interpolatedString->parts)];
if (! $lastPart instanceof InterpolatedStringPart) {
return null;
}

$delimiter = $this->resolveClosingDelimiter($firstPart->value);
$pattern = $lastPart->value;

$modifiers = $this->resolveModifiers((string) Strings::after($pattern, $delimiter, -1));
if (! \str_contains($modifiers, 'e')) {
return null;
}

$lastPart->value = $this->createPatternWithoutE($pattern, $delimiter, $modifiers);

return $interpolatedString;
}

private function resolveModifiers(string $modifiersCandidate): string
{
$modifiers = '';
Expand Down
Loading