preg_replace_callback 搞砸文本的麻烦
trouble with preg_replace_callback screwing up text
我正在尝试像这样替换 bbcode 引号:
[quote=username]text[/quote]
效果很好。
如果有人在引用别人,谁也在引用别人,问题就出现了,它似乎只替换了其中一个,就像这样的文字:
[quote=person1][quote=person2][quote]test quoted text[/quote]
another quote[/quote]
one more quote[/quote]
这是我的功能:
// replace specific-user quotes, called by quotes()
function replace_quotes($matches)
{
global $db;
$find_quoted = $db->sqlquery("SELECT `username`, `user_id` FROM `users` WHERE `username` = ?", array($matches[1]));
if ($db->num_rows() == 1)
{
$get_quoted = $find_quoted->fetch();
if (core::config('pretty_urls') == 1)
{
$profile_link = '/profiles/' . $get_quoted['user_id'];
}
else
{
$profile_link = '/index.php?module=profile&user_id=' . $get_quoted['user_id'];
}
return '<blockquote><cite><a href="'.$profile_link.'">'.$matches[1].'</a></cite>'.$matches[2].'</blockquote>';
}
else
{
return '<blockquote><cite>'.$matches[1].'</cite>'.$matches[2].'</blockquote>';
}
}
// find all quotes
function quotes($body)
{
// Quoting an actual person, book or whatever
$pattern = '/\[quote\=(.+?)\](.+?)\[\/quote\]/is';
$body = preg_replace_callback($pattern, 'replace_quotes', $body);
// Quote on its own
$pattern = '/\[quote\](.+?)\[\/quote\]/is';
$replace = "<blockquote><cite>Quote</cite></blockquote>";
while(preg_match($pattern, $body))
{
$body = preg_replace($pattern, $replace, $body);
}
return $body;
}
$body = 从某处发送给它的实际文本,例如对某事的评论
我还缺少什么让嵌套的东西也能工作?因为它应该替换每个单独的引号。
想法是像那样重写你的函数(未测试):
function quotes($body)
{
$pattern = '~\[quote=([^]]+)]([^[]*+(?:\[(?!/?quote\b)[^[]*)*+)\[/quote]~i';
do {
$body = preg_replace_callback($pattern, 'replace_quotes', $body, -1, $count);
} while ($count);
return $body;
}
其中 ([^[]*(?:\[(?!/?quote\b)[^[]*)*)
仅匹配没有左引号或右引号标记的子字符串。这样你肯定只会得到最里面的引号标签。
请注意,PHP 手册中还有另一种解析递归结构的方法,但我不确定它是否非常有效。 (参见 preg_replace_callback
页)。
我正在尝试像这样替换 bbcode 引号:
[quote=username]text[/quote]
效果很好。
如果有人在引用别人,谁也在引用别人,问题就出现了,它似乎只替换了其中一个,就像这样的文字:
[quote=person1][quote=person2][quote]test quoted text[/quote]
another quote[/quote]
one more quote[/quote]
这是我的功能:
// replace specific-user quotes, called by quotes()
function replace_quotes($matches)
{
global $db;
$find_quoted = $db->sqlquery("SELECT `username`, `user_id` FROM `users` WHERE `username` = ?", array($matches[1]));
if ($db->num_rows() == 1)
{
$get_quoted = $find_quoted->fetch();
if (core::config('pretty_urls') == 1)
{
$profile_link = '/profiles/' . $get_quoted['user_id'];
}
else
{
$profile_link = '/index.php?module=profile&user_id=' . $get_quoted['user_id'];
}
return '<blockquote><cite><a href="'.$profile_link.'">'.$matches[1].'</a></cite>'.$matches[2].'</blockquote>';
}
else
{
return '<blockquote><cite>'.$matches[1].'</cite>'.$matches[2].'</blockquote>';
}
}
// find all quotes
function quotes($body)
{
// Quoting an actual person, book or whatever
$pattern = '/\[quote\=(.+?)\](.+?)\[\/quote\]/is';
$body = preg_replace_callback($pattern, 'replace_quotes', $body);
// Quote on its own
$pattern = '/\[quote\](.+?)\[\/quote\]/is';
$replace = "<blockquote><cite>Quote</cite></blockquote>";
while(preg_match($pattern, $body))
{
$body = preg_replace($pattern, $replace, $body);
}
return $body;
}
$body = 从某处发送给它的实际文本,例如对某事的评论
我还缺少什么让嵌套的东西也能工作?因为它应该替换每个单独的引号。
想法是像那样重写你的函数(未测试):
function quotes($body)
{
$pattern = '~\[quote=([^]]+)]([^[]*+(?:\[(?!/?quote\b)[^[]*)*+)\[/quote]~i';
do {
$body = preg_replace_callback($pattern, 'replace_quotes', $body, -1, $count);
} while ($count);
return $body;
}
其中 ([^[]*(?:\[(?!/?quote\b)[^[]*)*)
仅匹配没有左引号或右引号标记的子字符串。这样你肯定只会得到最里面的引号标签。
请注意,PHP 手册中还有另一种解析递归结构的方法,但我不确定它是否非常有效。 (参见 preg_replace_callback
页)。