preg_replace () заменяет слишком много

Я работаю над простым отладчиком SQL, который будет принимать параметризованные переменные и попытаться их заменить соответственно, чтобы, если у части SQL возникла проблема, я могу скопировать + вставить ее непосредственно в RDBMS для работы с запросом и, надеюсь, отладить проблему быстрее.

До сих пор я по существу имел это, но он меняет слишком много:

<?php $sql = "select * from table_name where comment like :a and email = :b and status = :c"; $patterns = array(); $patterns[0] = '/:a/'; $patterns[1] = '/:b/'; $patterns[2] = '/:c/'; $replacements = array(); $replacements[0] = "'%that is a nice :b but this one%'"; $replacements[1] = "'monkeyzeus@example.com'"; $replacements[2] = "'active'"; echo preg_replace($patterns, $replacements, $sql); 

В результате чего

 select * from table_name where comment like '%that is a nice 'monkeyzeus@example.com' but this one%' and email = 'monkeyzeus@example.com' and status = 'active' 

Обратите внимание, что 'monkeyzeus@example.com' из позиции 1 превращает его в :b из позиции 0.

Я нашел этот вопрос: может ли preg_replace выполнять несколько операций поиска и замены одним выстрелом? , но я не могу делать головы или хвосты, потому что я, конечно, не специалист по регулярному выражению.


Обновить. Просто хотел поделиться конечным продуктом:

 function debug_sql($sql = NULL, $params = NULL) { return ( $sql !== NULL && is_array($params) && $params ? // $sql and $params is required strtr( // Feed this function the sql and the params which need to be replaced $sql, array_map( // Replace single-quotes within the param items with two single-quotes and surround param in single-quotes function($p) { return "'".str_replace("'", "''", $p)."'"; // Basic Oracle escaping }, $params ) ) : $sql ); } 

Для этого случая существует специальная функция: strtr – переводить символы или заменять подстроки http://php.net/manual/en/function.strtr.php

 <?php $sql = "select * from table_name where comment like :a and email = :b and status = :c"; $map = [ ':a' => "'%that is a nice :b but this one%'", ':b' => "'monkeyzeus@example.com'", ':c' => "'active'" ]; echo strtr($sql, $map); 

После некоторых проницательных предложений от jeroen :

Сначала замените заполнители каким-то хеш-заполнителем, который никогда не появится, а затем замените их вашими новыми строками.

Я придумал это и, похоже, работает для всех моих тестовых случаев:

 <?php $sql = "select * from table_name where comment like :a and email = :b and status = :c and something = :bb"; $patterns = array(); $replacements = array(); $patterns[0][0] = '/(:a)\\b/'; $patterns[0][1] = '/(:b)\\b/'; // Use word-boundary to prevent :b from being found in :bb $patterns[0][2] = '/(:c)\\b/'; $patterns[0][3] = '/(:bb)\\b/'; $replacements[0][0] = str_replace('.', '', uniqid('', TRUE)); $replacements[0][1] = str_replace('.', '', uniqid('', TRUE)); $replacements[0][2] = str_replace('.', '', uniqid('', TRUE)); $replacements[0][3] = str_replace('.', '', uniqid('', TRUE)); $patterns[1][0] = '/('.$replacements[0][0].')\\b/'; $patterns[1][1] = '/('.$replacements[0][1].')\\b/'; $patterns[1][2] = '/('.$replacements[0][2].')\\b/'; $patterns[1][3] = '/('.$replacements[0][3].')\\b/'; $replacements[1][0] = "'%that is a nice :b but this one%'"; $replacements[1][1] = "'monkeyzeus@example.com'"; $replacements[1][2] = "'active'"; $replacements[1][3] = "'another thing'"; $sql = preg_replace($patterns[0], $replacements[0], $sql); $sql = preg_replace($patterns[1], $replacements[1], $sql); echo $sql; 

Единственный способ, которым это может быть неудачным, – это то, что пользователь запрашивает точный вывод str_replace('.', '', uniqid('', TRUE)) во время обработки.

Альтернативный подход без regexp заключается в том, чтобы рекурсивно взорвать / разложить запрос:

 $sql = "select * from table_name where comment like :a and email = :b and status = :c "; $patterns = array(); $patterns[0] = ' :a '; $patterns[1] = ' :b '; $patterns[2] = ' :c '; $replacements = array(); $replacements[0] = " '%that is a nice :b but this one%' "; $replacements[1] = " 'monkeyzeus@example.com' "; $replacements[2] = " 'active' "; function replace($substr, $replacement, $subj) { if (empty($substr)) { return $subj; } $s = array_shift($substr); $r = array_shift($replacement); foreach($subj as &$str) { $str = implode($r, replace($substr, $replacement, explode($s, $str))); } return $subj; } echo replace($patterns, $replacements, [$sql])[0];