2016-03-30 2 views
4

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

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

<?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] = "'[email protected]'"; 
$replacements[2] = "'active'"; 

echo preg_replace($patterns, $replacements, $sql); 

Результирующее в

select * 
    from table_name 
where comment like '%that is a nice '[email protected]' but this one%' and 
     email = '[email protected]' and 
     status = 'active' 

Обратите внимание, что '[email protected]' от позиции 1 делает его в :b из положения 0.

Я нашел этот вопрос, Can preg_replace make multiple search and replace operations in one shot?, но я не могу сделать головы или хвосты, потому что я уверен y no regex expert.


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

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 
    ); 
} 
+2

Вопрос, который вы нашли, также использует несколько раундов замены, один за другим, так что это вам не поможет. Возможным решением было бы сделать это в 2 раундах: сначала замените заполнители каким-то хеш-заполнителем, который никогда не появится, а затем замените их вашими новыми строками. Обратите внимание, что вам понадобятся также разрывы слов вокруг строк шаблонов, поскольку заполнители могут быть ': b',': bb' и т. Д. – jeroen

+1

@jeroen Это фантастическое звучащее решение моей проблемы; двойная замена с помощью хэш-раунда. К счастью, я на шаг впереди с проблемой ': b' и': bb', о которой вы указали. Я сортирую параметры с помощью 'strlen()' их ключа, чтобы они всегда были заменены в следующем порядке: '$ patterns [0] = '/: bb /'; $ patterns [1] = '/: b /'; ' – MonkeyZeus

+0

@jeroen Благодарим вас за отличное предложение. Я написал ответ ниже. Если у вас есть какая-то минута, я, конечно же, буду рад получить ваши отзывы о любых проблемах, которые вы могли бы обнаружить, чего я не знаю. В частности, будет ли мой «хеш» уникальным для обозримого будущего. – MonkeyZeus

ответ

2

Существует специальная функция именно для этого случая: 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' => "'[email protected]'", 
    ':c' => "'active'" 
]; 

echo strtr($sql, $map); 
+0

Может ли это обработать '$ sql =" select * from table_name, где такие комментарии, как: a и email =: b и status =: c и something =: bb ";' вместе с '': bb '=> "" что-то еще "" добавляется в конец массива? – MonkeyZeus

+0

@MonkeyZeus да, это также будет работать нормально. 'echo strtr (': a: aa: a', [': a' =>": apple ", ': aa' =>" pear "]);' будет печатать *: яблочная груша: яблоко * – Axalix

+2

PHP имеет функция для ** ВСЕ! ** lol. Очень вероятно, что я буду принимать ваш ответ. Он довольно чистый и работает намного быстрее, чем решение, которое я разместил. Kudos :) – MonkeyZeus

2

После некоторых проницательных предложений от 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] = "'[email protected]'"; 
$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)) во время обработки.

+0

работает ли это? – Martin

+0

@Martin работает для меня в моем простом отладчике, и когда я проверяю его на http: //sandbox.onlinephpfunctions.com/:) – MonkeyZeus

+0

Это всего лишь случай ясности; как «Я придумал это:« Мне не кажется, что я окончательный ответ, но больше или предложение. Если ваш ответ работает, вы можете отредактировать свой ответ с заметкой о том, что он действительно работает? Cheers :-) – Martin

1

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

$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] = " '[email protected]' "; 
$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]; 
+0

Благодарим вас за ответ. Я вижу, что вы приложили немало усилий в свой код, и, похоже, он работает для ограниченных тестовых случаев, с которыми я его запускал, но, к сожалению, он добавляет гораздо больше строк кода, чем я хочу использовать в своем отладчике , +1 :-) – MonkeyZeus

Смежные вопросы