preg_replace() 替换太多
preg_replace() is replacing too much
我正在开发一个简单的 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'
请注意,位置 1 的 'monkeyzeus@example.com'
正在进入位置 0 的 :b
。
我发现了这个问题,Can preg_replace make multiple search and replace operations in one shot?,但我无法弄清它的正反面,因为我当然不是正则表达式专家。
更新。只是想分享最终产品:
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
);
}
经过 jeroen 的一些有见地的建议:
First replace the placeholders with some kind of hash / placeholder that will never appear and then replace these with your replacement strings.
我想出了这个,它似乎适用于我所有的测试用例:
<?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))
的确切输出,唯一可能失败的情况。
另一种没有正则表达式的方法是递归地explode/implode查询:
$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];
有一个专门针对这种情况的特殊函数:
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);
我正在开发一个简单的 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'
请注意,位置 1 的 'monkeyzeus@example.com'
正在进入位置 0 的 :b
。
我发现了这个问题,Can preg_replace make multiple search and replace operations in one shot?,但我无法弄清它的正反面,因为我当然不是正则表达式专家。
更新。只是想分享最终产品:
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
);
}
经过 jeroen 的一些有见地的建议:
First replace the placeholders with some kind of hash / placeholder that will never appear and then replace these with your replacement strings.
我想出了这个,它似乎适用于我所有的测试用例:
<?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))
的确切输出,唯一可能失败的情况。
另一种没有正则表达式的方法是递归地explode/implode查询:
$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];
有一个专门针对这种情况的特殊函数:
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);