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 页)。