Объявление

Хотите приглашение на сайт? Пишите: niikto@samovarchik.info


 

Вы долго пользовались движком и в один прекрасный день решили, что вам недостаточно функциональности встроенных в движок BBCode-тэгов?
Если вы решили самостоятельно расширить функционал парсера, эта статья поможет вам разобраться, как это сделать.

Парсер используется почти в чистом виде такой же, как в FluxBB ветки 1.4. Он лишь превращен из набора функций в класс и немного адаптирован под наш движок. Поэтому если вы хорошо знакомы с парсером FluxBB, вы не почувствуете разницы.

При написании данной статьи предполагалось, что пользователь знает HTML, CSS и хотя бы основы регулярных выражений. Еще лучше, если пользователь немного знаком с PHP.

Для добавления нового BBCode-тэга, нужно совершить несколько шагов:

  1. Определиться с синтаксисом самого тэга: будет ли это пара [ tag]...[/ tag] или одиночный тэг, можно ли ему будет передать параметр [ tag=something]...[/ tag] или нет, и что этот параметр означает.

  2. Определиться, как будет выглядеть результирующая HTML-разметка.
    Здесь трудно дать какой-то конкретный совет.
    Помните, что если вы делаете что-то блочное, то в самом начале вашей разметки закройте параграф текста, а в конце - откройте: </p><div class="newfeature">....</div><p>. Так устроен парсер, что в любой момент обработки BBCode тэгов, вы всегда находитесь внутри некоторого параграфа <p>, который был открыт ранее.
    Для внутристрочных тэгов (типа [ b] или [ i]) скорее всего разметка будет на основе HTML-тэга <span>

  3. Можно ли ваш тэг использовать внутри подписи пользователя? От этого будет зависеть дальнейшая его настройка в парсере

  4. Заставляем парсер преобразовывать ваши тэги в HTML-разметку.
    Для этого находим класс Parser (protected/parser.php), в нем метод _doBBcode(). Этот метод формирует пары шаблон/замена и в конце при помощи метода preg_replace() выполняет преобразование текста сообщения в HTML-разметку.
    Шаблоны добавляются в массив $pattern, разметка на замену в массив $replace. Очень важен порядок, в котором в эти массивы помещаются элементы. Чтобы не запутаться, проще всего просто поместить эти строки рядом:

    $pattern[] = '...';
    $replace[] = '...';

    Если вы не хотите, чтобы ваш BBCode-тэг можно было использовать в подписях пользователей, заключите формирование пары шаблон/замена в условие:

    if (!$is_signature)
    {
        $pattern[] = '...';
        $replace[] = '...';
    }

    Для описания шаблона используется синтаксис регулярных выражений PCRE (Perl Compatible Regular Expressions). Описание регулярных выражений выходит за рамки данной статьи, приведу лишь пару примеров:

    $pattern[] = '#\[b\](.*?)\[/b\]#ms';
    $pattern[] = '#\[url\]([^\[]*?)\[/url\]#e';
    $pattern[] = '#\[colou?r=([a-zA-Z]{3,20}|\#[0-9a-fA-F]{6}|\#[0-9a-fA-F]{3})](.*?)\[/colou?r\]#ms';

    Если регулярное выражение получается сложным и медленным, или используется несколько вариантов для одного и того же тэга, иногда может оказаться эффективнее сначала убедиться что такой тэг присутствует в тексте сообщения, выполнив обычный текстовый поиск:

    if (strpos($text, '[mytag') !== false)
    {
        $pattern[] = '...';
        $replace[] = '...';
    }

    где "mytag" заменяем на название вашего тэга.
    Более-менее достоверно сказать, что такой предварительный поиск действительно увеличит производительность, можно только проведя сравнительное тестирование. (Да, это значит спарсить обоими способами несколько тысяч тестовых сообщений, как содержащих, так и не содержащих внутри себя ваш тэг, и сравнить среднее время обработки таких сообщений. Думаю для этого без навыков PHP не обойтись.) Как именно тестировать - выходит за рамки данной статьи, по сути это все эвристика.

    То, на что мы должны заменить текст, при совпадении с шаблоном, является специально сформированной строкой. Подробности можно прочитать здесь. Приведу примеры:

    $pattern[] = '#\[b\](.*?)\[/b\]#ms';
    $replace[] = '<strong>$1</strong>';
    
    $pattern[] = '#\[search\]([^\[]*?)\[/search\]#e';
    $replace[] = '$this->_handleSearchTag(\'$1\')';
    
    $pattern[] = '#\[colou?r=([a-zA-Z]{3,20}|\#[0-9a-fA-F]{6}|\#[0-9a-fA-F]{3})](.*?)\[/colou?r\]#ms';
    $replace[] = '<span style="color: $1">$2</span>';

    В некоторых сложных случаях, требующих дополнительной обработки или проверки каких-либо условий, рационально будет создать метод, обабатывающий ваш BBCode-тэг и возвращающий сформированную разметку в виде строки (пример сильно упрощен):

    private function _handleMytagTag($text)
    {
        return '</p><div class="newfeature">' . $text . '</div><p>';
    }

    Такие методы делаются скрытыми (private), т.к. они являются внутренними для парсера и нигде вне его не используются. Имя обычно начинается с "_handle", затем идет название вашего тэга, и завершается "Tag". Количество и имена параметров вы выбираете сами, в зависимости от ваших потребностей. Обычно их нужно не более 2.
    Для того, чтобы ваш метод использовался для формирования HTML-разметки, необходимо в конце шаблона указать модификатор "е", а вместо разметки для замены, указать имя функции (как в примере выше, для тэга [ search]).

  5. Настраеваем препарсер.
    Это нужно для того, чтобы парсер мог оценить правильность использования вашего тэга в сообщении.

    Находим в классе Parser метод preparseBBcode().
    Если ваш тэг нельзя использовать в подписи пользователя, добавьте его имя в разделенный вертикальной чертой | список тэгов в условии if (preg_match(...)), которое находится внутри условия if ($is_signature) в самом начале метода.

    Находим метод _preparseTags().
    Для начала помещаем имя вашего BBCode-тэга в массив $tags - список всех тэгов, обрабатываемых парсером.
    Затем, помещая или наоборот не помещая в другие массивы имени вашего тэга, настраиваем другие параметры:

    • $tags_selfclosed - тэги, которые не имеют закрывающего тэга. Например [ thumb] или [ cut]

    • $tags_nested - тэги, которые могут содержаться внутри себя. Формат массива: array('tag' => $val), где 'tag' - это имя тэга, а вместо $val может быть любое вырадение, формирующее число, и это число означает допустимый уровень вложенности тэга внутрь самого себя. Если вложить тэг большее число раз, препарсер выдаст пользователю сообщение об ошибке.

    • $tags_ignore - тэги, чье содержимое полностью игнорируется препарсером. На данный момент это только [ code]

    • $tags_block - блоковые тэги. Это как раз те, о которых я выше говорил, чтобы вы не забыли закрыть параграф </p> до них, и открыть новый после.

    • $tags_inline - не смотря на название, это не обязательно внутристрочные (inline) тэги, это скорее тэги, которые не допускают внутри себя символов перевода строки. Препарсер разбивает их следующим образом:

      [b]Первая строка.
      Вторая строка.[/b]

      превращается в:

      [b]Первая строка.[/b]
      [b]Вторая строка.[/b]
    • $tags_trim - в таких тэгах пробельные символы (пробелы, табуляция, переводы строки) между обычным текстом и BBCode тэгами удаляются. Нужно помнить, что такие символы удаляются не только после открывающего тэга и перед закрывающим, но и перед и после любого другого BBCode-тэга внутри вашего.

    • $tags_quotes - тэги, из аргументов которых мы удаляем кавычки. Т.е. тэг вида:

      [tag="something"]...[/tag]

      Превращается в:

      [tag=something]...[/tag]
    • $tags_limit_bbcode - все тэги, не включенные в этот список, могут содержать внутри себя любые другие ббкоды. Если мы включаем сюда какой-либо тэг, то мы указываем для него белый лист из других тэгов, разрешая только этим тэгам содержаться внутри него. Формат: 'mytag' => array('tag1', 'tag2', ...).

    • $tags_fix - для данных тэгов мы позволяем препарсеру самостоятельно исправить порядок открывающих и закрывающих тэгов, если пользователем они указаны неверно. Например:

      [b][i]something[/b][/i]

      превращается в

      [b][i]something[/i][/b]
  6. Обработка пустых тэгов.
    Обычно BBCode-тэги без содержимого оставляются пользователем случайно, и на самом деле не нужны.
    Если ваш тэг не самозакрывающийся (т.е. имеет помимо открывающего также закрывающий тэг), найдите метод _stripEmptyBBcode(), в нем первый из циклов while(). Если ваш тэг бесполезен пустым не зависимо от наличия у него параметра, поместите его в первый из preg_replace() внутри while. Если при наличии параметра, тэг остается функциональным даже при отсутствии внутри него текста, поместите его во второй preg_replace() внутри while.
    Например, [ url=http://example.org][/ url] обрабатывается также как и [ url]http://example.org[/ url], и поэтому помещен во второй preg_replace(). В то время как [ color=#a00][/ color] бесполезен, и поэтому помещен в первый preg_replace().

  7. Формирование поискового индекса.
    Для того, чтобы новое сообщение могло быть найдено средствами встроенного в движок поиска, это сообщение должно быть проиндексировано. Перед индексацией из сообщения удаляются все тэги BBCode и остается только полезная информация. Для этого код, занимающийся индексированием должен знать о существовании вашего тэга и о том, как его корректно удалить.

    Находим класс SearchIndex (protected/searchindex.php), в нем метод splitWords(). Добавляем имя вашего тэга в самый первый preg_replace() в список тэгов, разделенных вертикальной чертой |.

    В большинстве случаев этого достаточно. Но иногда к тэгу требуется особый подход. Например, может потребоваться оставить для индексации параметр тэга. В таком случае находим в этом же классе метод _stripBBcode() и добавляем в массив $patterns шаблон поиска (регулярное выражение) и строку замены. Пример:

    $patterns = array(
        '%\[img=([^\]]*+)\]([^[]*+)\[/img\]%'               => '$2 $1',    // Оставить URL и описание
        '%\[anchor\]([a-zA-Z_0-9\-/\.:~]++)\[/anchor\]%'    => ' ',        // Удалить ID якоря
    );
  8. Проверить, что все работает, как вы планировали.

 

Дизайн сайта отсутствует
оформление: Группа «САМОВАРчик»

[ Сгенерировано за 0.015 сек, 8 запросов выполнено - Использовано памяти: 1.92 MiB (Пик: 1.98 MiB) ]