При использовании preg_replace () в PHP со строками, генерируемыми во время выполнения, можно защитить специальные символы регулярного выражения (такие как '$' или '+') в строке поиска с помощью preg_quote (). Но как правильно обработать это в строке замены? Возьмите этот код, например:
<?php
$haystack = '...a bit of sample text...';
$replacement = '\\HELLO WORLD$1.+-';
$replacement_quoted = preg_quote($replacement);
var_dump('--replacement', $replacement, '--replacement_quoted',
$replacement_quoted, '--haystack', $haystack);
$result1 = preg_replace("@(bit) (of) (sample)@is", "\${1}" . $replacement ."$3", $haystack);
$result2 = preg_replace("@(bit) (of) (sample)@is", "\${1}" . $replacement_quoted ."$3", $haystack);
$replacement_new1 = str_replace('$', '\$', $replacement);
$replacement_new2 = str_replace('\\', '\\\\', $replacement_new1);
$result3 = preg_replace("@(bit) (of) (sample)@is", "\${1}" . $replacement_new1 ."$3", $haystack);
$result4 = preg_replace("@(bit) (of) (sample)@is", "\${1}" . $replacement_new2 ."$3", $haystack);
var_dump('--result1 (not quoted)', $result1, '--result2 (quoted)', $result2,
'--result3 ($ escaped)', $result3, '--result4 (\ and $ escaped)', $result3);
?>
Вот вывод:
string(13) "--replacement"
string(17) "\HELLO WORLD$1.+-"
string(20) "--replacement_quoted"
string(22) "\\HELLO WORLD\$1\.\+\-"
string(10) "--haystack"
string(26) "...a bit of sample text..."
string(22) "--result1 (not quoted)"
string(40) "...a bit\HELLO WORLDbit.+-sample text..."
string(18) "--result2 (quoted)"
string(42) "...a bit\HELLO WORLD$1\.\+\-sample text..."
string(21) "--result3 ($ escaped)"
string(39) "...a bit\HELLO WORLD$1.+-sample text..."
string(27) "--result4 (\ and $ escaped)"
string(39) "...a bit\HELLO WORLD$1.+-sample text..."
Как видите, вы не можете выиграть с помощью preg_quote (). Если вы не вызываете его и просто передаете строку в неизмененном виде (result1), все, что похоже на токен захвата ($ 1 выше), заменяется на
что бы ни содержала соответствующая группа захвата. Если вы назовете его (result2), у вас не возникнет проблем с группами захвата, но любые другие специальные символы PCRE (такие как *) также будут экранированы, и экранированные символы сохранятся в выходных данных. Также интересно, что обе версии выдают один \ в выходной файл.
Вы можете заставить это работать, только указав символы в кавычках вручную, в частности, $. Это можно увидеть в result3 и result4. Однако, продолжая странность с \, оба result3, который добавляет экранирование для \, и result4 снова выдают одиночный \ в выходных данных. Добавление шести символов \ в начале строки замены приводит к получению только двух символов \ в конечном выводе для result1, result3 и result4 и трех из них для result2.
Таким образом, кажется, что большинство проблем решается путем ручного экранирования символа $. Кажется, что \ символ также должен быть экранирован, но мне нужно подумать об этом еще немного, чтобы точно выяснить, что происходит. В любом случае, все это довольно уродливо - между надоедливым синтаксисом \ $ {1} и необходимостью вручную экранировать определенные символы, код просто пахнет очень гнилым и подверженным ошибкам. Я что-то упускаю? Есть ли чистый способ сделать это?