Метод PHP для замены кавычек BBCode

Мне нужно заменить цитаты BBCode с форума phpBB3 с помощью PHP. Цитаты постов выглядят так:

[quote="John Doe":2sxn61wz][quote="Bob":2sxn61wz]Some text from Bob[/quote:2sxn61wz]Some text from John Doe[/quote:2sxn61wz]Some more text

Я хотел бы проанализировать эту строку и получить массив, например:

Array (

    [0] => Array (

        [0] => 'John Doe' 
        [1] => 'Some text from John Doe'
    )

    [1] => Array (

        [0] => 'Bob' 
        [1] => 'Some text from Bob'
    )
)

Каким будет наилучший подход для рекурсивного поиска этих блоков цитат и их содержимого? Заранее спасибо за любую помощь в этом!

Как предложено в комментариях:

$str = '[quote="John Doe":2sxn61wz][quote="Bob":2sxn61wz]Some text from Bob[/quote:2sxn61wz]Some text from John Doe[/quote:2sxn61wz]Some more text';
$uid = '2sxn61wz';

print_r(quoteParser($str, $uid));

function quoteParser($str, $uid) {

    $pattern = "#\[quote(?:="(.*?)")?:$uid\]((?!\[quote(?:=".*?")?:$uid\]).)?#ise";

    echo "Unparsed string: " . $str . "<br /><br />";
    echo "Pattern: " . $pattern . "<br /><br />";

    preg_match_all($pattern, $str, $matches);

    return $matches;
}

Вывод:

Array ( [0] => Array ( [0] => [quote="John Doe":2sxn61wz] [1] => [quote="Bob":2sxn61wz]S ) [1] => Array ( [0] => John Doe [1] => Bob ) [2] => Array ( [0] => [1] => S ) ) 

Это то, что мне нужно, но я не понимаю цитируемый текст. Только имена пользователей. Любая помощь? Спасибо.


person MrUpsidown    schedule 15.01.2013    source источник
comment
Я бы посмотрел, как ваш синтаксический анализатор BBCode выполняет преобразование в HTML, а затем вынул биты, необходимые для создания вашего массива.   -  person BenLanc    schedule 15.01.2013
comment
Вы имеете в виду парсер, используемый в phpBB3?   -  person MrUpsidown    schedule 15.01.2013
comment
Да, в этой строке есть то, что вам нужно для начала работы: github.com/phpbb/phpbb3/blob/develop/phpBB/includes/   -  person BenLanc    schedule 15.01.2013
comment
Пожалуйста, смотрите мое обновление в вопросе.   -  person MrUpsidown    schedule 15.01.2013
comment
Вы смотрели парсер BBCode в PHP? php.net/manual/en/book.bbcode.php   -  person Andy Lester    schedule 16.01.2013
comment
@Энди Лестер Я попробовал HTML_BBCodeParser2, но 1) он не обрабатывает кавычки в этом формате и 2) не так просто изменить библиотеку, чтобы она выводила кавычки в пользовательском стиле HTML.   -  person MrUpsidown    schedule 17.01.2013


Ответы (3)


По-видимому, вы хотите превратить свой вывод BBCode в HTML. Вы можете проверить функцию bbcode_create(), если как. В противном случае, я считаю, что другая рекурсия - для форматирования текста - является лучшим вариантом.

<style type="text/css">

    body {
        font-size:.9em;
        font-family:Arial, Helvetica, sans-serif;
    }

    .post {
        margin:1em;
        padding:1em;
        font-size:1em;
        border:1px solid #999;
    }
    .post > .text {margin:1em .4em}
    .post > div:first-child {margin-top:0}
    .post > div:last-child {margin-bottom:0}

    .post > .quote {border-color:#CCC}
    .quote {
        margin:1em 2em;
        background-color:#F9F9F9;
        border:1px solid #AAA;
        font-style:italic;
    }
    .quote .text {margin:.7em 1em}
    .quote .author {
        color:#039;
        font-size:.8em;
        background-color:#E0E0E0;
        padding:.5em .8em;
        margin:.5em;
    }
    .author a {
        font-weight:bold;
        text-decoration:none;
        color:inherit;
    }
    .author a:hover {text-decoration:underline;}

</style>

function str2html($str,$className='post') {
    $patPost =
    '~(
        \[quote=&quot;(?:(?!&quot;).)+&quot;:(\w+)(?:\])
            (?:
                (?(?=\[quote=&quot;(?:(?!&quot;).+)&quot;:\w+\])
                    (?R)
                    |
                    (?(?!\[/quote:\2\]).)
                )*
            )
        \[/quote:\2\]
    )~xis';
    // note that these 2 are not identical
    $patQuote =
    '~
        (\[quote=&quot;((?:(?!&quot;).)+)&quot;:(\w+)(?:\]))
            (
                (?(?=\[quote=&quot;(?:(?!&quot;).+)&quot;:\w+\])
                    (?R)
                    |
                    (?(?!\[/quote:\3\]).)
                )*
            )
        (\[/quote:\3\])
    ~xis';

    $arr = preg_split($patPost,$str,-1,PREG_SPLIT_DELIM_CAPTURE);
    foreach ($arr as &$value) $value = trim($value);
    unset($value);
    //echo '<h2>split post</h2>'; print_r($arr);

    $res = empty($className) ? '' : "<div class=\"{$className}\">";
    for ($i = 0; $i < count($arr); $i += 3) {
        if (!empty($arr[$i]))
            $res .= '<div class="text">' . $arr[$i] . '</div>';
        if (!empty($arr[$i+1]))
            $res .= preg_replace_callback($patQuote,'replaceQuote',$arr[$i+1]);
    }
    $res .= empty($className) ? '' : '</div>';
    return $res;
}
function replaceQuote($m) {
    //echo '<h2>replacing quotes</h2>'; print_r($m);
    $res = '<div class="quote">';
    $res .= '<div class="author">';
    $res .= '<a href="">' . $m[2] . '</a> wrote:';
    $res .= '</div>';
    $res .= str2html($m[4],'');
    $res .= '</div>';
    return $res;
}

Вы можете позвонить по echo str2html('some string here'). Вы поняли идею.

person inhan    schedule 18.01.2013
comment
Большое спасибо @inhan за вашу помощь. К сожалению, я не могу заставить ваш скрипт работать. Кажется, он никогда не входит в функцию replaceQuote. Пытаюсь отладить... - person MrUpsidown; 21.01.2013
comment
Должна быть проблема в $patPost, но я не могу сказать где. - person MrUpsidown; 21.01.2013
comment
@MrUpsidown Если вы можете опубликовать примерную строку или ссылку на нее, я могу рассказать вам, что происходит, и соответствующим образом настроить шаблоны. Со всеми строками, которые я пробовал, все работало так, как должно, поэтому на данный момент мне любопытно, что происходит с вашей строкой. - person inhan; 21.01.2013
comment
Плохо... Я использовал строку с " вместо &quot;, которая работает как шарм! Еще раз спасибо за ваше время! - person MrUpsidown; 21.01.2013
comment
@MrUpsidown Иронично. Для удобочитаемости я начал строить шаблон, чтобы он соответствовал символам кавычек (вместо &quot;), а затем я изменил их обратно, прежде чем публиковать здесь;) - person inhan; 21.01.2013

Вот рекурсивная версия. Соответствует всем кавычкам в строке. Вот как бы я это сделал. Если вся цель состоит в том, чтобы лучше отображать каждый код в html, то вместо того, чтобы сопоставлять их, я бы вместо этого изменил содержимое с помощью правильного шаблона Regex.

$str =  '[quote=&quot;John Doe&quot;:2sxn61wz]' .
        '[quote=&quot;Bob&quot;:2sxn61wz]' .
        'Some text from Bob[/quote:2sxn61wz]' .
        'Some text from John Doe[/quote:2sxn61wz]' . 
        'Some more text';
$str .= '[quote=&quot;MrUpsidown&quot;:2sxn61wz]Some other quote[/quote:2sxn61wz]';
//$str .= '[quote=&quot;MrUpsidown&quot;:2sxn61wz]Yet another one[/quote:2sxn61wz]';

$pat =
'~
    \[quote=&quot;((?:(?!&quot;).)+)&quot;:(\w+)(?:\])
        (
            (?(?=\[quote=&quot;(?:(?!&quot;).+)&quot;:\w+\])
                (?R)
                |
                (?(?!\[/quote:\2\]).)
            )*
        )
    \[/quote:\2\]
~xis';

preg_match_all($pat,$str,$matches);
echo '<h2>Matches:</h2>'; print_r($matches);

$quotes = array();

function replaceQuote($m) {
    global $curArr; global $pat;
    $curArr[] = array(
        'name'  => $m[1],
        'quote' => preg_replace_callback(
            $pat,'replaceQuote',$m[3]
        )
    );
    return preg_replace($pat,'',$m[0]);
}

if (!empty($matches[0])) {
    foreach ($matches[0] as $k => $v) {
        $quotes[] = array(
            array(
                'name'  => $matches[1][$k],
                'quote' => $matches[3][$k]
            )
        );
        $curArr = &$quotes[count($quotes)-1];
        $curArr[0]['quote'] = preg_replace_callback(
            $pat,'replaceQuote',$curArr[0]['quote']
        );
    }
    unset($curArr);
}

echo '<h2>Result:</h2>'; print_r($quotes);

И вот вывод.

Спички:

  Array
        (
            [0] => Array
                (
                    [0] => [quote="John Doe":2sxn61wz][quote="Bob":2sxn61wz]Some text from Bob[/quote:2sxn61wz]Some text from John Doe[/quote:2sxn61wz]
                    [1] => [quote="MrUpsidown":2sxn61wz]Some other quote[/quote:2sxn61wz]
                )

            [1] => Array
                (
                    [0] => John Doe
                    [1] => MrUpsidown
                )

            [2] => Array
                (
                    [0] => 2sxn61wz
                    [1] => 2sxn61wz
                )

            [3] => Array
                (
                    [0] => [quote="Bob":2sxn61wz]Some text from Bob[/quote:2sxn61wz]Some text from John Doe
                    [1] => Some other quote
                )

        )

Результат:

  Array
    (
        [0] => Array
            (
                [0] => Array
                    (
                        [name] => John Doe
                        [quote] => Some text from John Doe
                    )

                [1] => Array
                    (
                        [name] => Bob
                        [quote] => Some text from Bob
                    )

            )

        [1] => Array
            (
                [0] => Array
                    (
                        [name] => MrUpsidown
                        [quote] => Some other quote
                    )

            )

    )
person inhan    schedule 16.01.2013
comment
Спасибо за Ваш ответ! Я попробую это завтра. - person MrUpsidown; 17.01.2013
comment
Это огромное улучшение! Из вашего примера, как мне также получить текст Some more и любой другой текст, который будет либо до первого [quote], либо после последнего [/quote]? - person MrUpsidown; 17.01.2013
comment
@MrUpsidown Как бы вы интерпретировали текст в txt1[quote]quote1[/quote]txt2[quote]quote1[/quote]txt3? - person inhan; 17.01.2013
comment
текст 1 [quote][quote] цитата 1 [/quote] цитата 2 [/quote] текст 2 [quote] цитата 3 [/quote] текст 3 --- В основном мне нужно заменить эти кавычки некоторыми тегами HTML в строке . Хотя не уверен, что это лучший подход. - person MrUpsidown; 18.01.2013
comment
@MrUpsidown Почему вы не подумали о замене каждой цитаты соответствующими тегами, например «‹div class=quote›‹/div›», и не написали какие-то жесткие правила CSS? - person inhan; 18.01.2013
comment
Может быть, потому что я слишком хотел выучить какое-нибудь регулярное выражение :) Но мне понадобится аналогичная техника для замены вхождений, при этом сохраняя имя пользователя и текст в кавычках, верно? - person MrUpsidown; 18.01.2013
comment
@MrUpsidown Не волнуйтесь, для этого все равно потребуется регулярное выражение. Смотрите мой другой ответ о том, как я его использую. Раскомментируйте закомментированные строки в коде, чтобы увидеть, что делают preg_split() и preg_replace_callback(). - person inhan; 18.01.2013

Попробуйте что-то вроде этого:

$str = '[quote="John Doe":2sxn61wz][quote="Bob":2sxn61wz]Some text from Bob[/quote:2sxn61wz]Some text from John Doe[/quote:2sxn61wz]Some more text';

$quotes=array();

$quotes[]=quoteParser($str,$quotes);
print_r($quotes);

function quoteParser($str,&$quotes) {

    $pattern = "/\[quote(?:=&quot;(.*?)&quot;)?:.{8}?\](.*)\[\/quote:2sxn61wz\](.*)/ise";

    echo "Unparsed string: " . $str . "&lt;br /&gt;&lt;br /&gt;";
        echo "Pattern: " . $pattern . "&lt;br /&gt;&lt;br /&gt;";
    preg_match_all($pattern, $str, $matches);
        if(empty($matches[0][0]))
        return false;

    $var=quoteParser($matches[2][0],$quotes);
        $quotes[]=array($matches[1][0],$var?$var:$matches[2][0]);
    return $matches[3][0];
    }

вывод:

Массив ( [0] => Массив ( [0] => Боб [1] => Некоторый текст от Боба ) [1] => Массив ( [0] => Джон Доу [1] => Некоторый текст от Джона Доу ) [2] => Еще текст )

person QArea    schedule 16.01.2013