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 @@ + +----- +