diff --git a/docs/AllRectorsOverview.md b/docs/AllRectorsOverview.md
index ed2b353106e3..ad1826a64db8 100644
--- a/docs/AllRectorsOverview.md
+++ b/docs/AllRectorsOverview.md
@@ -306,14 +306,8 @@ Changes settype() to (type) where possible
Add preg_quote delimiter when missing
```diff
- class SomeClass
- {
- public function test()
- {
-- return preg_quote('name');
-+ return preg_quote('name', '#');
- }
- }
+-'#' . preg_quote('name') . '#';
++'#' . preg_quote('name', '#') . '#';
```
@@ -5923,8 +5917,7 @@ services:
- return SomeStaticClass::go();
+ return $this->someStaticClass->go();
}
--}
-+}
+ }
```
diff --git a/packages/CodeQuality/src/Rector/FuncCall/AddPregQuoteDelimiterRector.php b/packages/CodeQuality/src/Rector/FuncCall/AddPregQuoteDelimiterRector.php
index b523ca10ca4d..82fc97990fbd 100644
--- a/packages/CodeQuality/src/Rector/FuncCall/AddPregQuoteDelimiterRector.php
+++ b/packages/CodeQuality/src/Rector/FuncCall/AddPregQuoteDelimiterRector.php
@@ -6,8 +6,10 @@
use PhpParser\Node;
use PhpParser\Node\Arg;
+use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Scalar\String_;
+use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
@@ -17,28 +19,21 @@
*/
final class AddPregQuoteDelimiterRector extends AbstractRector
{
+ /**
+ * @see https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
+ */
+ private const ALL_MODIFIERS = 'imsxeADSUXJu';
+
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Add preg_quote delimiter when missing', [
new CodeSample(
<<<'PHP'
-class SomeClass
-{
- public function test()
- {
- return preg_quote('name');
- }
-}
+'#' . preg_quote('name') . '#';
PHP
,
<<<'PHP'
-class SomeClass
-{
- public function test()
- {
- return preg_quote('name', '#');
- }
-}
+'#' . preg_quote('name', '#') . '#';
PHP
),
@@ -67,8 +62,54 @@ public function refactor(Node $node): ?Node
return null;
}
- $node->args[1] = new Arg(new String_('#'));
+ $delimiter = $this->determineDelimiter($node);
+ if ($delimiter === null) {
+ return null;
+ }
+
+ $node->args[1] = new Arg(new String_($delimiter));
return $node;
}
+
+ private function determineDelimiter(FuncCall $funcCall)
+ {
+ $parent = $this->getUppermostConcat($funcCall);
+ if ($parent === null) {
+ return null;
+ }
+ $leftMostConcatNode = $parent->left;
+ while ($leftMostConcatNode instanceof Concat) {
+ $leftMostConcatNode = $leftMostConcatNode->left;
+ }
+ $rightMostConcatNode = $parent->right;
+ while ($rightMostConcatNode instanceof Concat) {
+ $rightMostConcatNode = $rightMostConcatNode->right;
+ }
+
+ if (! $leftMostConcatNode instanceof String_) {
+ return null;
+ }
+ $possibleLeftDelimiter = substr($leftMostConcatNode->value, 0, 1);
+ if (! $rightMostConcatNode instanceof String_) {
+ return null;
+ }
+ $possibleRightDelimiter = substr(rtrim($rightMostConcatNode->value, self::ALL_MODIFIERS), -1, 1);
+ if ($possibleLeftDelimiter === $possibleRightDelimiter) {
+ return $possibleLeftDelimiter;
+ }
+
+ return null;
+ }
+
+ private function getUppermostConcat(FuncCall $funcCall): ?Concat
+ {
+ $upperMostConcat = null;
+ $parent = $funcCall->getAttribute(AttributeKey::PARENT_NODE);
+ while ($parent instanceof Concat) {
+ $upperMostConcat = $parent;
+ $parent = $parent->getAttribute(AttributeKey::PARENT_NODE);
+ }
+ return $upperMostConcat;
+ }
}
diff --git a/packages/CodeQuality/tests/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/fixture.php.inc b/packages/CodeQuality/tests/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/fixture.php.inc
index 1ea30f88248b..c0a45db183bd 100644
--- a/packages/CodeQuality/tests/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/fixture.php.inc
+++ b/packages/CodeQuality/tests/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/fixture.php.inc
@@ -4,9 +4,11 @@ namespace Rector\CodeQuality\Tests\Rector\FuncCall\AddPregQuoteDelimiterRector\F
class SomeClass
{
- public function test()
+ public function test($input)
{
- return preg_quote('name');
+ $patttern1 = '/' . preg_quote('name'). '/';
+ $patttern2 = '#' . preg_quote('name'). '#';
+ $patttern3 = '#\d' . preg_quote('name'). '\d#';
}
}
@@ -18,9 +20,11 @@ namespace Rector\CodeQuality\Tests\Rector\FuncCall\AddPregQuoteDelimiterRector\F
class SomeClass
{
- public function test()
+ public function test($input)
{
- return preg_quote('name', '#');
+ $patttern1 = '/' . preg_quote('name', '/'). '/';
+ $patttern2 = '#' . preg_quote('name', '#'). '#';
+ $patttern3 = '#\d' . preg_quote('name', '#'). '\d#';
}
}
diff --git a/packages/CodeQuality/tests/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/unknown_delimiter.inc b/packages/CodeQuality/tests/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/unknown_delimiter.inc
new file mode 100644
index 000000000000..db567cc37586
--- /dev/null
+++ b/packages/CodeQuality/tests/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/unknown_delimiter.inc
@@ -0,0 +1,16 @@
+
diff --git a/packages/CodeQuality/tests/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/with_modifiers.php.inc b/packages/CodeQuality/tests/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/with_modifiers.php.inc
new file mode 100644
index 000000000000..935c201fe7fd
--- /dev/null
+++ b/packages/CodeQuality/tests/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/with_modifiers.php.inc
@@ -0,0 +1,29 @@
+
+-----
+