我正在使用两个正则表达式从MySQL查询中提取分配并使用它们来创建审计跟踪。其中一个是“挑剔”的,需要引用的列名/等,另一个不需要。
它们都经过测试并正确解析出值。我遇到的问题是,对于某些查询,“挑剔”的正则表达式实际上只会导致Apache出现段错误。
我尝试了各种各样的事情来确定这是将regexp留在代码中的原因,只是修改条件以确保它没有运行(以排除某种编译时问题或其他问题) 。没有问题。只有当它针对特定查询运行正则表达式时才会出现段错误,而且我找不到任何明显的模式来告诉我原因。
有问题的代码:
if ($picky)
preg_match_all("/[`'\"]((?:[A-Z]|[a-z]|_|[0-9])+)[`'\"] *= *'((?:[^'\\\\]|\\\\.)*)'/", $sql, $matches);
else
preg_match_all("/[`'\"]?((?:[A-Z]|[a-z]|_|[0-9])+)[`'\"]? *= *[`'\"]?([^`'\" ,]+)[`'\"]?/", $sql, $matches);
两者之间的唯一区别是第一个删除了引号上的问号,使它们成为非可选项,并删除了对值使用不同类型引号的选项 - 仅允许使用单引号。用第二个替换第一个正则表达式(用于测试目的)并使用相同的数据可以解决问题 - 肯定与正则表达式有关。
导致我悲伤的特定SQL可在以下位置找到:
http://stackoverflow.pastebin.com/m75c2a2a0
有趣的是,当我删除突出显示的部分时,一切正常。尝试单独提交突出显示的部分不会导致错误。
我对这里发生的事情感到非常困惑。任何人都可以提供任何有关进一步调试或修复的建议吗?
编辑:没有什么非常令人兴奋的,但为了完整起见,这里是Apache的相关日志条目(/var/log/apache2/error.log - 该网站的error.log中没有任何内容。甚至没有提及请求在访问日志中。)[Thu Dec 10 10:08:03 2009] [notice] child pid 20835 exit signal Segmentation fault (11)
包含该查询的每个请求中的一个。
EDIT2:根据Kuroki Kaze的建议,我尝试了相同长度的乱码并得到了同样的段错误。坐下来尝试了一堆不同的长度并找到了极限。 6035个字符工作正常。 6036段错误。
EDIT3:更改pcre.backtrack_limit
中pcre.recursion_limit
和php.ini
的值可以稍微缓解这个问题。 Apache不再是段错误,但我的正则表达式不再匹配字符串中的所有匹配项。显然这是PHP / PCRE中一个众所周知的(2007年)错误:
http://bugs.php.net/bug.php?id=40909
EDIT4:我在下面的答案中发布了我用来替换这个特定正则表达式的代码,因为我的目的无法接受解决方法(产品出售,不能保证php.ini更改和regexp只是部分工作删除我们要求的功能)。我发布的代码已发布到公共领域,不提供任何形式的担保或支持。我希望它可以帮助别人。 :)
谢谢大家的帮助!
亚当
答案 0 :(得分:4)
有趣的是,当我删除突出显示的部分时,一切正常。尝试单独提交突出显示的部分不会导致错误。
提交的大小怎么样?如果你通过相同长度的胡言乱语,会发生什么?
编辑:拆分和合并将看起来像这样:
$strings = explode("\n", $sql);
$matches = array(array(), array(), array());
foreach ($strings AS $string) {
preg_match_all("/[`'\"]?((?:[A-Z]|[a-z]|_|[0-9])+)[`'\"]? *= *[`'\"]?([^`'\" ,]+)[`'\"]?/", $string, $matches_temp);
$matches[0] = array_merge($matches[0], $matches_temp[0]);
$matches[1] = array_merge($matches[1], $matches_temp[1]);
$matches[2] = array_merge($matches[2], $matches_temp[2]);
}
答案 1 :(得分:4)
我遇到了类似的preg_match相关问题,同样是Apache segfault。只有导致它的preg_match被内置到我正在使用的CMS中(WordPress)。
提供的“解决方法”是在php.ini中更改这些设置:
[PCRE] ; PCRE库回溯限制。 ; pcre.backtrack_limit = 100000 pcre.recursion_limit = 2亿 pcre.backtrack_limit =亿
权衡是为了渲染更大的页面(在我的情况下,> 200行;当其中一列被限制为1500个字符的文本描述时),你将获得相当高的CPU利用率,而我我仍然看到了段错误。只是不那么频繁。
我的网站已接近生命终结,因此我并不需要(或预算)来寻找真正的解决方案。但也许这可以缓解你所看到的问题。
答案 2 :(得分:1)
鉴于这只需要在保存页面或执行其他不经常执行的操作时匹配查询,我觉得以下代码的性能损失是可以接受的。它解析SQL查询($sql
)并将name =>值对放入$data
。似乎运作良好并处理大量查询。
$quoted = '';
$escaped = false;
$key = '';
$value = '';
$target = 'key';
for ($i=0; $i<strlen($sql); $i++)
{
if ($escaped)
{
$$target .= $sql[$i];
$escaped = false;
}
else if ($quoted!='')
{
if ($sql[$i]=='\\')
$escaped = true;
else if ($sql[$i]==$quoted)
$quoted = '';
else
$$target .= $sql[$i];
}
else
{
if ($sql[$i]=='\'' || $sql[$i]=='`')
{
$quoted = $sql[$i];
$$target = '';
}
else if ($sql[$i]=='=')
$target = 'value';
else if ($sql[$i]==',')
{
$target = 'key';
$data[$key] = $value;
$key = '';
$value = '';
}
}
}
if ($value!='')
$data[$key] = $value;
感谢大家的帮助和指导!