проблема с 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>$1</blockquote>";

    while(preg_match($pattern, $body))
    {
        $body = preg_replace($pattern, $replace, $body);
    }

    return $body;
}

$body = фактический текст, отправленный откуда-то, например, комментарий к чему-либо

Чего мне не хватает, чтобы вложенные вещи тоже работали? Как и положено заменять каждую отдельную цитату.


person NaughtySquid    schedule 16.12.2016    source источник
comment
Этот вопрос уже задавался (вероятно, несколько раз). Если вы хотите это сделать, вам нужно создать шаблон, который запрещает вложенным тегам кавычек совпадать с самыми внутренними тегами кавычек, поместить вашу замену в цикл do..while и использовать параметр count preg_replace в качестве условия (пока не будет ничего заменять ).   -  person Casimir et Hippolyte    schedule 17.12.2016
comment
Есть пример этого?   -  person NaughtySquid    schedule 17.12.2016


Ответы (1)


Идея состоит в том, чтобы переписать вашу функцию так (не проверено):

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).

person Casimir et Hippolyte    schedule 16.12.2016
comment
Спасибо за это, я протестировал пример на сайте PHP, и он действительно не работает должным образом, он захватывает слишком много. Я протестировал ваше решение, и оно оказалось не только удобным, но и молниеносно быстрым при проверке с использованием: regex101.com огромное спасибо за это, так как я терпеть не могу регулярные выражения! - person NaughtySquid; 17.12.2016