За последние 24 часа нас посетили 20033 программиста и 1169 роботов. Сейчас ищут 522 программиста ...

BBcode`s - Патерны (рег. выр.), функции, теория.

Тема в разделе "Решения, алгоритмы", создана пользователем Hight, 29 июн 2007.

  1. Hight

    Hight Старожил
    Команда форума Модератор

    С нами с:
    5 мар 2006
    Сообщения:
    7.153
    Симпатии:
    0
    Адрес:
    из злой параллельной вселенной
    без него тоже можно
     
  2. Vladson

    Vladson Старожил

    С нами с:
    4 фев 2006
    Сообщения:
    4.040
    Симпатии:
    26
    Адрес:
    Estonia, Tallinn
    Нужно, иначе будет возможность сделать пустой тэг (например "<b></b>") а это нарушение HTML стандарта
     
  3. Hight

    Hight Старожил
    Команда форума Модератор

    С нами с:
    5 мар 2006
    Сообщения:
    7.153
    Симпатии:
    0
    Адрес:
    из злой параллельной вселенной
    а я давно на xHTML =) или тут тоже нарушаю? Ну ладно, согласен ;)
     
  4. dark-demon

    dark-demon Активный пользователь

    С нами с:
    16 фев 2007
    Сообщения:
    1.920
    Симпатии:
    1
    Адрес:
    леноград
  5. ONK

    ONK Активный пользователь

    С нами с:
    4 фев 2006
    Сообщения:
    281
    Симпатии:
    0
    Адрес:
    СПб
    И конечно же я тоже вставлю своё слово ;)

    Я использую парсёр специальных маркеров (ББ кодов) основанный на конечном автомате.
    Это позволяет контролировать содержимое каждого атрибута каждого маркера, причём атрибутов может быть сколько угодно и их порядок следования не важен. Отслеживается уровень взаимной вложенности маркеров и вообще огромный простор для деятельности. Также это позволяет легко создавать новые правила парсировки новых специальных маркеров. Фактически у меня сейчас половина HTML тэгов преобразуется в специальные маркеры, всё остальное обрабатывается htmlspecialchars() а затем всё обратно преобразуется в HTML и сохраняет в базе данных для пказа пользователям. Я не вижу смысла экономить на длине сообщений, тем более, что экономия мизерная. Сделано это для того, чтобы пользователи без права использования HTML кода в сообщениях могли пользоваться визуальным редактором.

    Всё выше перечисленное относиться к пользователями не имеющим право использовать HTML код.
    Если у пользователя есть это право, то сообщение проходит обработку фильтром HTML кода. Который также реализован на основе конечного автомата, проверяет значения атрибутов HTML тэгов (процедура проверки конфигурируется индивидуально для каждого тэга и атрибута), контролирует взаимную вложенность тэгов их допустимость (в том числе структуру тэгов таблицы). Есть отдельный парсёр тэга style.

    В данный момент я тестирую безопасность данного фильтра, в моих планах сделать режим разрешённого HTML кода доступным по умолчанию всем пользователям форума и чата.
     
  6. Sergey89

    Sergey89 Активный пользователь

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
    BBCode list, списки
    Код (Text):
    1. $text = '
    2. Список:
    3. [list]
    4. [*] Привет МИР!
    5. [*] Да здавствует МИР!
    6. [*] Пока МИР!
    7. [/list]
    8. ';
    PHP:
    1. <?php
    2. $text = preg_replace_callback('#\[list\](.*)\[/list\]#Usi',
    3.     create_function('$matches', '
    4.        $html = "<ul>\n";
    5.        $list = explode("[*]", $matches[1]);
    6.        array_shift($list);
    7.        foreach ($list as $li) {
    8.            $html .= "<li>" . trim($li) . "</li>\n";
    9.        }
    10.        return $html . "</ul>";
    11.    ')
    12. , $text);
    13.        
    14. print $text;
    15. ?>
    Вариант с поддержкой нумерованных списков
    Код (Text):
    1. [list=order]
    PHP:
    1. <?php
    2. $text = preg_replace_callback('#\[list(.*)\](.*)\[/list\]#Usi',
    3.     create_function('$matches', '
    4.        $html = ($matches[1] == "=order" ? "<ol>\n" : "<ul>\n");
    5.        $list = explode("[*]", $matches[2]);
    6.        array_shift($list);
    7.        foreach ($list as $li) {
    8.            $html .= "<li>" . trim($li) . "</li>\n";
    9.        }
    10.        return $html . ($matches[1] == "=order" ? "</ol>\n" : "</ul>\n");
    11.    ')
    12. , $text);
    13. ?>
    Код (Text):
    1. Список:
    2. <ul>
    3. <li>Привет МИР!</li>
    4. <li>Да здавствует МИР!</li>
    5. <li>Пока МИР!</li>
    6. </ul>
    PS надо бы на форуме исправить функцию подстветки BBCode php, а то в внутри этих BB тэгов успешно обрабатываются и другие ;)
     
  7. Sergey89

    Sergey89 Активный пользователь

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
    И очень полезный, но нигде не используемый BBCode :(
    PHP:
    1. <?
    2. $text = preg_replace(
    3.     '#\[man\]([a-z0-9_]+)\[/man\]#Usi',
    4.     '<a href="http://php.net/$1">$1</a>',
    5. $text);
    6. ?>
     
  8. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Между прочим о парсинге с помощью регулярок.
    Непосредственно сталкивался с одной проблемой, которая вызывает "500 - Internal Server Error" - в трекере там регулярки типа preg_replace('/[tag](.*)?[/tag]/', .. , ...); так вот. переодически бывают моменты, когда входишь в описание торрента и выскакивает 500-я ошибка. Оказываеться, иногда товарищи умудряються весь текст (а это порой пара килобайт) загнать в [/size ] или какой-нить другой тег. В итоге обработать такое кол-во текста PHP просто не может и страница отваливаеться с 500-й ошибкой. И это не смотря на то, что сервачёк то у меня не какой-нить захудалый, а вполне мощьная машина, да и оперативки там не 200-300 мегов, а 2 гига.

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

    Вот щас в данный момент в бумтайм проэкте мы используем парсер, который использует регулярки только для [ a][/a ], [a=url][/a], [ img][/img ], всё остальное заменяеться обычным str_replace (что кстати весьма и весьма шустро), однако к примеру при парсинге BBCode приходиться вырезать теги цитат, что-бы они в визивике были в виде BBcode, а не HTML - уже пришлось писать исключение руками. И таких вот задачь много. ДА хотя-бы взять тут на форуме - впишите в [php ] [/php ] любой практичесчки из тегов в исходниках - получите невъебенные глюки. А всё потому, что при обработке [php ] [/php ] всё что между ними надо пропускать ТОЛЬКО через обработчик [php ][/php ] и не давать другим обработчикам вообще притрагиваться к тому, что между [php ] и [/php ] :) А ещё веселее если у вас в коде будет так:
    $str = 'bla bla [/php ]';
    :D
    Будет глюк.

    Вообщем лично я пришел к выводу, что нужен класс, который работает по теории конечных автоматов и который при своей работе рокуводствуеться конфигурационным масивом, где можно настроить к примеру что в теге TAG вообще ничего не парсить, пока он не закроеться. Или что в тегк TAG2 не применять обработчик тега TAG3
     
  9. Sergey89

    Sergey89 Активный пользователь

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
    http://www.pc.uz/documents/text/732.html
    Нашёл у себя в закладках. Там именно класс и работает именно на теории конечных атвоматов ;)
    ЗЫ Тока сейчас чё-то не открывается, но потом глянь ;)
     
  10. Anonymous

    Anonymous Guest

    +3
     
  11. dark-demon

    dark-demon Активный пользователь

    С нами с:
    16 фев 2007
    Сообщения:
    1.920
    Симпатии:
    1
    Адрес:
    леноград
    а почему три? :)-.
     
  12. Anonymous

    Anonymous Guest

    +1 - класс нужен
    +1 - конечные автоматы
    +1 - потоковый парсинг
     
  13. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Вот человек понимает суть проблемы и явно сталкивался с геммороем на этой основе :)
    Вообщем в обозримом будущем у меня на работе задача доработки нашего парсера сообщений до нормального состояния будет, я и здесь выложу то что получиться, вместе может ещё и доработаем ;)
     
  14. Hight

    Hight Старожил
    Команда форума Модератор

    С нами с:
    5 мар 2006
    Сообщения:
    7.153
    Симпатии:
    0
    Адрес:
    из злой параллельной вселенной
    это давай давай, ждём ;)
     
  15. Gorns

    Gorns Активный пользователь

    С нами с:
    14 сен 2008
    Сообщения:
    19
    Симпатии:
    0

    и как избежать (.*?)

    ?
     
  16. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    очень полезный функция
    PHP:
    1.  
    2. <?
    3. function bbcontrol($text,$thisbb) {
    4.     for($i=0,$c=count($thisbb);$i<$c;$i++) {
    5.         preg_match_all("#\[(".$thisbb[$i].")\]#isU",$text,$openbb);
    6.         preg_match_all("#\[\/(".$thisbb[$i].")\]#isU",$text,$closebb);
    7.         if (count($openbb[1])>count($closebb[1])) {
    8.             if (preg_match("#\[\/".$thisbb[$i]."\]#is",$text)) {
    9.                 $exp=explode("[/".$thisbb[$i]."]",$text);
    10.              for($z=0,$k=count($openbb[1])-count($closebb[1]);$z<$k;$z++) {
    11.                 $exp[0].="[/".$thisbb[$i]."]";
    12.              }
    13.              $text=join("[/".$thisbb[$i]."]",$exp);
    14.            } else {
    15.              $text=str_replace("[".$thisbb[$i]."]","[".$thisbb[$i]."&#093",$text);
    16.            }
    17.         }else if (count($openbb[1])<count($closebb[1])) {
    18.             if (preg_match("#\[".$thisbb[$i]."\]#is",$text)) {
    19.                 $exp=explode("[".$thisbb[$i]."]",$text);
    20.                 for($z=0,$k=count($closebb[1])-count($openbb[1]);$z<$k;$z++) {
    21.                     $exp[0].="[".$thisbb[$i]."]";
    22.                 }
    23.                 $text=join("[".$thisbb[$i]."]",$exp);
    24.             }else {
    25.                 $text=str_replace("[/".$thisbb[$i]."]","[/".$thisbb[$i]."&#093",$text);
    26.             }
    27.         }
    28.     }
    29.     return $text;
    30. }
    31. ?>
     
  17. Mae Stro

    Mae Stro Активный пользователь

    С нами с:
    21 ноя 2008
    Сообщения:
    14
    Симпатии:
    0
    Адрес:
    Киров
    Привет всем.

    Лично я столкнулся с BB-кодами совсем недавно, поэтому тема еще свежа. Когда потребовался такой фильтр, я изучал многие решения CMS, и метод конечных автоматов отдельно.

    Ежу понятно, что strtr и str_replace - самое оно, пока не приходишь к мысли ввести таблицы, списки и теги с параметрами. Метод конечных - дает все, но Боже мой, это фильтр для сайта, или сайт для демонстрации этого фильтра? :) Там же прорва килобайт, и все это - ручками на PHP, как будто он не интерпретатор. В результате - жалобы на быстродействие и необходимость в обязаловку кешировать готовые страницы.

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

    Вот типа того и получилось. Недостатки - пока не брался за теги, блокирующие или модифицирующие парзинг типа [ PHP ] или [ NOBB ]

    Если угодно, можно юзать с учетом, что доработки будут выложены здесь же - мне самому интересно:)
    PHP:
    1.  
    2. <?php
    3. if (eregi("bbsm",$_SERVER['PHP_SELF'])) {
    4. Header("Location: http://{$_SERVER['SERVER_NAME']}/index.php");
    5. die();
    6. }
    7. $DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT'];
    8.  
    9. require_once "$DOCUMENT_ROOT/config.php";   // Берем с конфга путь к тематическим смайликам
    10. $dir_smiles = "/$dir_theme/img/smiles";
    11.  
    12. // СМАЙЛИКИ
    13.  
    14. $smiles = array(
    15. 's' => array( ':)', ':-)', ';)', ';-)', ':(', ':-(', ':D', ':-D', ':o', ':-o', ':p', ':-p',
    16.               ':confused:', ':rolleyes:', ':cool:', ':eek:', ':mad:'
    17. ),
    18. 'r' => array(
    19. '<img class="smiles" alt=":)" title=":)" src="'.$dir_smiles.'/smile.gif" />',
    20. '<img class="smiles" alt=":)" title=":)" src="'.$dir_smiles.'/smile.gif" />',
    21. '<img class="smiles" alt=";)" title=";)" src="'.$dir_smiles.'/wink.gif" />',
    22. '<img class="smiles" alt=";)" title=";)" src="'.$dir_smiles.'/wink.gif" />',
    23. '<img class="smiles" alt=":(" title=":(" src="'.$dir_smiles.'/frown.gif" />',
    24. '<img class="smiles" alt=":(" title=":(" src="'.$dir_smiles.'/frown.gif" />',
    25. '<img class="smiles" alt=":D" title=":D" src="'.$dir_smiles.'/biggrin.gif" />',
    26. '<img class="smiles" alt=":D" title=":D" src="'.$dir_smiles.'/biggrin.gif" />',
    27. '<img class="smiles" alt=":o" title=":o" src="'.$dir_smiles.'/redface.gif" />',
    28. '<img class="smiles" alt=":o" title=":o" src="'.$dir_smiles.'/redface.gif" />',
    29. '<img class="smiles" alt=":p" title=":p" src="'.$dir_smiles.'/tongue.gif" />',
    30. '<img class="smiles" alt=":p" title=":p" src="'.$dir_smiles.'/tongue.gif" />',
    31. '<img class="smiles" alt=":confused:" title=":confused:" src="'.$dir_smiles.'/confused.gif" />',
    32. '<img class="smiles" alt=":rolleyes:" title=":rolleyes:" src="'.$dir_smiles.'/rolleyes.gif" />',
    33. '<img class="smiles" alt=":cool:" title=":cool:" src="'.$dir_smiles.'/cool.gif" />',
    34. '<img class="smiles" alt=":eek:" title=":eek:" src="'.$dir_smiles.'/eek.gif" />',
    35. '<img class="smiles" alt=":mad:" title=":mad:" src="'.$dir_smiles.'/mad.gif" />',
    36. ),
    37. );
    38.  
    39. // ОДИНОЧНЫЕ ТЕГИ
    40.  
    41. $bb_list1 = array(
    42.  
    43. 'sl'      => '[',
    44. 'sr'      => ']',
    45. '_'       => '&nbsp;',
    46. 'br'      => '<br />',
    47. 'hr'      => '<hr />',
    48. 'hrns'    => '<hr noshade/>',
    49. );
    50.  
    51. // ДВОЙНЫЕ ТЕГИ
    52.  
    53. $bb_list2 = array(
    54.  
    55. // array( начинающие теги,
    56. //        функция отработки параметра (корректирует цифру после тега, проверяет параметр),
    57. //        конец заголовка,
    58. //        завершающие теги,
    59. //        после каких тегов может идти,
    60. //        какие теги закрывает - если элемент по определению является вложенным, то - в начале,
    61. //        а если нет - в конце
    62. // )
    63. // Знак $ обозначает вставку номера тега (например H1, H2 имеют номера 1, 2)
    64. // Номер тега при разборе отделяется от имени тега и может быть скорректирован
    65. // функцией-валидатором
    66.  
    67.  
    68. 'b'       => array( '<b',    '', '>', '</b>'),
    69. 'u'       => array( '<u',    '', '>', '</u>'),
    70. 'i'       => array( '<i',    '', '>', '</i> '),         // пробел в конце из-за глюка IE7 с float и курсивом
    71. 'em'      => array( '<em',   '', '>', '</em> '),
    72. 'tt'      => array( '<tt',   '', '>', '</tt>'),
    73. 's'       => array( '<s',    '', '>', '</s>'),
    74. 'sup'     => array( '<sup',  '', '>', '</sup>'),
    75. 'sub'     => array( '<sub',  '', '>', '</sub>'),
    76. 'pre'     => array( '<pre',  '', '>', '</pre>'),
    77. 'big'     => array( '<big',  '', '>', '</big>'),
    78.  
    79. 'p'       => array( '<p',        '',          '>', '</p>'),
    80. 'p='      => array( '<p align=', 'bbp_align', '>', '</p>'),
    81.  
    82. 'h'       => array( '<h$',           'bbp_h', '>', '</h$>'),
    83. 'h='      => array( '<h$ align="',   'bbp_h', '">', '</h$>'),
    84.  
    85. 'center'  => array( '<div style="text-align:center',  '',          '">', '</div>'),
    86. 'left'    => array( '<div style="text-align:left',    '',          '">', '</div>'),
    87. 'right'   => array( '<div style="text-align:right',   '',          '">', '</div>'),
    88. 'justify' => array( '<div style="text-align:justify', '',          '">', '</div>'),
    89. 'align'   => array( '<div style="text-align:',        'bbp_align', '">', '</div>'),
    90.  
    91. 'table'   => array( '<table', '',          '>', '</table>', '',                 '|tr|th|td|'),
    92. 'table='  => array( '<table', 'bbp_table', '>', '</table>', '',                 '|tr|th|td|'),
    93. 'tr'      => array( '<tr',    '',          '>','</tr>',     '|table|tr|th|td|', '|tr|th|td|'),
    94. 'tr='     => array( '<tr',    'bbp_tr',    '>','</tr>',     '|table|tr|th|td|', '|tr|th|td|'),
    95. 'th'      => array( '<th',    '',          '>','</th>',     '|tr|th|td|',       '|th|td|'),
    96. 'th='     => array( '<th',    'bbp_thtd',  '>','</th>',     '|tr|th|td|',       '|th|td|'),
    97. 'td'      => array( '<td',    '',          '>','</td>',     '|tr|th|td|',       '|th|td|'),
    98. 'td='     => array( '<td',    'bbp_thtd',  '>','</td>',     '|tr|th|td|',       '|th|td|'),
    99.  
    100. 'list'    => array( '<ul', '', '>','</ul>', '', '|*|'),
    101. 'list='   => array( '<ol style="list-style-type:', 'bbp_list', '">','</ol>', '', '|*|'),
    102. '*'       => array( '<li', '', '>','</li>', '|list|*|', '|*|'),
    103.  
    104. 'color='  => array( '<span style="color:', 'bbp_color', '">', '</span>'),
    105. 'url='    => array( '<a href="', 'bbp_url', '">', '</a>'),
    106. );
    107.  
    108.  
    109.  
    110. // ФУНКЦИИ-ПАРЗЕРЫ
    111.  
    112. // 1. разобрать параметр
    113. // 2. проверить на валидность части параметра, вернуть FALSE, если проверка не пройдена
    114. //    Как вариант, вернуть значение по умолчанию.
    115. // 3. преобразовать параметр в код HTML, c учетом возможных подстановок констант
    116.  
    117. function bbp_h(&$n, &$prm)
    118. {  $n = max(min($n, 6), 1);
    119.    return preg_match('/^"?(center|left|right|)"?/is', $prm,  $pat) ? $pat[1] : FALSE;
    120. }
    121. function bbp_align(&$n, &$prm)
    122. {  return preg_match('/^"?(center|left|right|justify)"?/is', $prm,  $pat) ? $pat[1] : FALSE;
    123. }
    124.  
    125. function bbp_url(&$n, &$prm)
    126. {  return preg_match('/"?([a-z]?[:\/\/]?[a-z0-9\-_]?\.?[a-z0-9\.\/%&#=\?\-_]+)"?/is', $prm,  $pat) ? $pat[1] : FALSE;
    127. }
    128.  
    129. function bbp_list(&$n, &$prm)
    130. {  static $subset =
    131.                 array( 'none', 'disc', 'circle', 'square', 'decimal', 'lower-roman',
    132.                        'upper-roman', 'lower-alpha', 'upper-alpha'
    133.                 );
    134.    if (! preg_match(
    135. //        '/^([0-8]{1}|none|disc|circle|square|decimal|lower-roman|upper-roman|lower-alpha|upper-alpha)/',
    136.         '/^([0-8]{1}|\w*)/',
    137.         $prm,  $pat)) return FALSE;
    138.  
    139.    return isset($subset[$pat[1]]) ? $subset[$pat[1]]: $pat[1];
    140. }
    141.  
    142. function bbp_color(&$n, &$prm)
    143. {  static $subset =
    144.                 array( 'black'   => '#000000', 'blue'     => '#0000FF', 'brown'   => '#A52A2A',
    145.                        'cyan'    => '#00FFFF', 'darkblue' => '#00008B', 'darkred' => '#8B0000',
    146.                        'green'   => '#008000', 'indigo'   => '#4B0082', 'olive'   => '#808000',
    147.                        'orange'  => '#FFA500', 'red'      => '#FF0000', 'violet'  => '#EE82EE',
    148.                        'white'   => '#FFFFFF', 'yellow'   => '#FFFF00', 'aqua'    => '#00FFFF',
    149.                        'fuchsia' => '#FF00FF', 'grey'     => '#808080', 'lime'    => '#00FF00',
    150.                        'maroon'  => '#800000', 'navy'     => '#000080', 'purple'  => '#800080',
    151.                        'silver'  => '#C0C0C0', 'teal'     => '#008080'
    152.                 );
    153.  
    154.    if (! preg_match(
    155. //        '/^"?(#[0-9]{6}|black|blue|brown|cyan|darkblue|darkred|green|indigo|olive|orange|red|violet|white|yellow|aqua|fuchsia|grey|lime|maroon|navy|purple|silver|teal)"?/is',
    156.         '/^"?(#[0-9A-F]{6}|\w*)"?/is',
    157.         $prm,  $pat)) return FALSE;
    158.  
    159.    return isset($subset[$pat[1]]) ? $subset[$pat[1]]: $pat[1];
    160. }
    161.  
    162. function bbp_thtd(&$n, &$prm)
    163. {  static $lprm = array(
    164.       'cs' => array('/^([0-9]+)$/is',           ' colspan='),
    165.       'rs' => array('/^([0-9]+)$/is',           ' rowspan='),
    166.       'h'  => array('/^([0-9]+%?)$/is',         ' height='),
    167.       'w'  => array('/^([0-9]+%?)$/is',         ' width='),
    168.       'ga' => array('/^(l|c|r)$/is',            ' align='),
    169.       'va' => array('/^(t|m|b)$/is',            ' valign='),
    170.       'nw' => array('',                         ' nowrap'),
    171.       'bc' => array('/^(#[0-9A-F]{6}|\w*)$/is', ' bgcolor='),
    172.    );
    173.    static $sub = array(
    174.       'l' => 'left',   'r' => 'right', 'c' => 'center',
    175.       'm' => 'middle', 't' => 'top',   'b' => 'bottom',
    176.    );
    177.    return bbp_short_args( $prm, $lprm, $sub);
    178. }
    179.  
    180. function bbp_tr(&$n, &$prm)
    181. {  static $lprm = array(
    182.       'ga' => array('/^(l|c|r)$/is',            ' align='),
    183.       'va' => array('/^(t|m|b)$/is',            ' valign='),
    184.       'bc' => array('/^(#[0-9A-F]{6}|\w*)$/is', ' bgcolor='),
    185.    );
    186.    static $sub = array(
    187.       'l' => 'left',   'r' => 'right', 'c' => 'center',
    188.       'm' => 'middle', 't' => 'top',   'b' => 'bottom',
    189.    );
    190.    return bbp_short_args( $prm, $lprm, $sub);
    191. }
    192.  
    193. function bbp_table(&$n, &$prm)
    194. {  static $lprm = array(
    195.       'b'  => array('/^([0-9]+?)$/is',          ' border='),
    196.       'cp' => array('/^([0-9]+?)$/is',          ' cellpadding='),
    197.       'cs' => array('/^([0-9]+?)$/is',          ' cellspacing='),
    198.       'h'  => array('/^([0-9]+%?)$/is',         ' height='),
    199.       'w'  => array('/^([0-9]+%?)$/is',         ' width='),
    200.       'ga' => array('/^(l|c|r)$/is',            ' align='),
    201.       'va' => array('/^(t|m|b)$/is',            ' valign='),
    202.       'bc' => array('/^(#[0-9A-F]{6}|\w*)$/is', ' bgcolor='),
    203.    );
    204.    static $sub = array(
    205.       'l' => 'left',   'r' => 'right', 'c' => 'center',
    206.       'm' => 'middle', 't' => 'top',   'b' => 'bottom',
    207.    );
    208.    return bbp_short_args( $prm, $lprm, $sub);
    209. }
    210.  
    211. function bbp_short_args(& $prm, & $lprm, & $sub)
    212. {  $args = explode(';', str_replace('"', '', $prm));
    213.    $out = '';
    214.    foreach ( $args as $arg)
    215.    {  $a = explode(':', $arg);
    216.       if (isset($lprm[$a[0]]))
    217.       {   if (isset($a[1]) )
    218.           {  if (preg_match($lprm[$a[0]][0], $a[1], $pat) > 0)
    219.                 $out .= $lprm[$a[0]][1].'"'.(isset($sub[$pat[1]])?$sub[$pat[1]]:$pat[1]).'"';
    220. //echo "Для маски {$lprm[$a[0]][0]} в строке поиска {$a[1]} найдено ".preg_match($lprm[$a[0]][0], $a[1], $pat)."совпадений<br>";
    221. //echo 'Контент: '.nl2br(print_r($pat, true)).'<br>';
    222.           }
    223.           else $out .= $lprm[$a[0]][1];
    224.       }
    225.    }
    226.    return $out;
    227. }
    228.  
    229.  
    230.  
    231. // ТОЧКА ВХОДА В ПАРЗЕР BB-кодов
    232.  
    233. function filter_bb($str, $smiles = false, $content = NULL)
    234. {
    235.    global $bb_list1a, $bb_list1b;
    236.  
    237.    // Преформат: выкидываем лишние \n из сложных выражений
    238.    // Фактически мы выкидываем br после закрывающих
    239.    // тегов
    240.  
    241.    $str = preg_replace('/[\r|\n]{2}+\[(\*|\/)/', "[$1", $str);
    242. //   $str = preg_replace('#(\[(?:\w|/\w|/|\*).*?\])[\r|\n]{2}#', "$1", $str);
    243.  
    244.    // разобьем строку на составляющие подстроки и теги
    245.    if ($content == NULL)
    246.       $content = preg_split('#(\[(?:\w|/\w|/|\*).*?\])#mis', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
    247.  
    248. // echo 'Контент: '.nl2br(print_r($content, true)).'<br>';
    249.  
    250.    // вызовем рекурсивную функцию обработки c с последющей заменой
    251.    // проигнорированных спецтегов
    252.    if ($smiles)
    253.    {  global $smiles;
    254.       return str_replace($smiles['s'], $smiles['r'], bb_rcd($content, 'start'));
    255.    }
    256.    else
    257.       return bb_rcd($content, 'start');
    258. }
    259.  
    260. // РЕКУРСИВНАЯ ФУНКЦИЯ ДЕКОДИРОВАНИЯ С ИСПОЛЬЗОВНИЕМ МАССИВА ПОДСТРОК
    261.  
    262. function bb_rcd(& $content, $openkey='')
    263. {
    264.    global $bb_list1, $bb_list2, $bb_const;
    265.    $out = "";
    266.    $opentag = str_replace('=', '', $openkey);
    267.  
    268. while (true)
    269. {
    270.    if (empty($content)) return $out;
    271.    $elm = array_shift( $content);
    272.  
    273. // echo "<br>Вход: $openkey; элемент: $elm<br>";
    274.  
    275.    if ($elm[0] == '[') // && $elm[1] !='*')                 // найден bb-код (игнорим списочный [*])
    276.    {
    277.       $match_count = preg_match( '#^\[(/?)((?:[A-Za-z]+|[\*_]||))(\d*)([=:]?)(.*?)]$#i', $elm, $matches);
    278.  
    279.       // $matches[0] - копия входящей строки
    280.       // $matches[1] - '/' если закрывающий тег, иначе ''
    281.       // $matches[2] - слово кода
    282.       // $matches[3] - цифра сразу за кодом
    283.       // $matches[4] - разделители '=' или ':'
    284.       // $matches[5] - параметр
    285.  
    286. // echo 'Массив разбора тега:<br>'.nl2br(print_r($matches, true)).'<br>';
    287.  
    288.       if (! $match_count)
    289.       {
    290.          $out .= $elm;
    291.          continue;
    292.       }
    293.  
    294.       $bb_prm = $matches[5];
    295.       $bb_wrd = strtolower(trim($matches[2]));
    296.       $bb_dlm = $matches[4] == '' ? '' : '=';
    297.       $bb_key = $bb_wrd.$bb_dlm;                    // ключ в массиве
    298.  
    299.       $vl =& $bb_list2[$bb_key];
    300.  
    301.       if ($matches[1] != '/')                       // тег не закрывающийся
    302.       {  if ( empty($bb_wrd)) continue;                 // тег пуст, игнорим
    303.  
    304.          // Проверим наличие тега в массиве допустимых
    305.  
    306. //echo '<br>Проверка тега: '. $bb_wrd.$bb_dlm . '<br>';
    307. //echo "Ключ: ".$bb_key.'<br>';
    308.  
    309.          if (! isset($bb_list2[$bb_key]) )              // тег не описан, смотрим в массив одиночных тегов
    310.          {  $out .= isset($bb_list1[$bb_key]) ? $bb_list1[$bb_key]: $elm;
    311.             continue;
    312.          }
    313.  
    314.          // Проверим ограничения на позицию элемента
    315.  
    316. //echo "Текущий открытый тег: ". $opentag.'<br>';
    317.  
    318.          if ( !empty($vl[4]))
    319.          {  if ( strpos($vl[4], "|$opentag|") === FALSE )       // если вложенный элемент вложен неправильно
    320.             {
    321. //echo "|$opentag| нет в {$vl[4]}, выход!<br>";
    322.                $out .= bb_invalid($elm);
    323.                continue;
    324.             }
    325.  
    326.             if ( !empty($vl[5]) && strpos($vl[5], "|$opentag|") !== FALSE)
    327.             {  array_unshift($content, $elm);
    328. //echo "Автозакрытие: $opentag и выход<br>";
    329.                 return $out;                        // закрываем текущий уровень вложенности
    330.             }
    331. //            else echo "Автозакрытие невозможно: |$opentag| нет в {$vl[5]}<br>";
    332.          }
    333.  
    334.          // Проверим соответствие формата ожидаемому
    335.  
    336.          if ( @function_exists($vl[1]) === TRUE )           // если присутствует функция-валидатор
    337.          {
    338. //echo "Есть функция-валидатор: ". $vl[1].'<br>';
    339.             $bb_prm = $vl[1]($matches[3], $bb_prm);
    340.             if ($bb_prm === FALSE)
    341.             { $out .= bb_invalid($elm); continue;
    342.             }
    343.          }
    344.  
    345.          $out .= str_replace('$', $matches[3],
    346.                                   $bb_list2[$bb_key][0].$bb_prm.$bb_list2[$bb_key][2].
    347.                                   bb_rcd($content, $bb_key).
    348.                                   $bb_list2[$bb_key][3]
    349.                  );
    350. //echo "Out: $out<br>";
    351.       }
    352.       else                              // закрывающий тег
    353.       {
    354. //echo "Слово закрывающегося тега: $bb_wrd, слово открытого тега: $opentag.<br>";
    355.          if ($bb_wrd == $opentag) return $out;              // полное совпадение, выходим
    356.          else
    357.          {  if ( empty($bb_wrd))                    // тег автозакрытия [/]
    358.             {  if ( ! empty($bb_list2[$opentag][4]))            // открытый тег вложенный
    359.                   array_unshift($content, $elm);            // сохраняем тег автозакрытия
    360.                   // - это вызовет последовательное закрытие вложенных тегов до закрытия контейнера
    361. // echo "Автозакрытие тега: $opentag<br>";
    362.                return $out;
    363.             }
    364.  
    365.             if ( empty($vl[4]) && strpos(isset($vl[5]) ? $vl[5]:'', "|$opentag|") !== FALSE )   // тег не является вложенным, возможно автозакрытие
    366.             {
    367.                array_unshift($content, $elm);
    368. // echo "Автозакрытие тега: $opentag<br>";
    369.                return $out;
    370.             }
    371. //            else echo "Автозакрытие невозможно: |$opentag| нет в {$vl[5]}<br>";
    372.  
    373.             $out.= bb_invalid($elm);
    374.          }
    375.  
    376. //echo "Закрывающий html для $openkey: ".htmlspecialchars($bb_list2[$openkey][3]).'<br>';
    377.  
    378.       }
    379.    }
    380.    else
    381.         $out .= nl2br($elm);
    382. }
    383. }
    384.  
    385. function bb_invalid($str)
    386. { return '<font color="red">' .$str. '</font>';
    387. }
    388.  
    389. ?>
    390.  

    Тестовое файло:


    PHP:
    1. <?php
    2.  
    3. include 'bbsm.php';
    4.  
    5. $str = '[list][*]Во-первых:[url="/main?l=auth#art"]ссылка 1[/url][*]Во вторых: [b]:ЖЫРНЫЙ ШРИФТ[/b][*]в третьих - вложенный список[list=4][*]первый пункт[*]второй пункт[*]третий[/list][/list]';
    6. echo $str.'<br>';
    7. //echo nl2br(htmlspecialchars(my_decode_bb($str)));
    8. echo "\n";
    9. echo '<br>Превью<br>';
    10. echo "\n";
    11. echo filter_bb($str);
    12. echo "\n";
    13.  
    14. $str = '[table=b:1;cp:0;cs:0;bc:#EEEEEE][tr][th="cs:2;va:m;ga:c;bc:lightblue;w:100%;h:100"]Заголовок[tr][td=rs]строка 1, ячейка 1[td]строка 1, ячейка 2[tr=bc][td]строка 2, ячейка 2[/table]';
    15. echo "\n";
    16. echo $str.'<br>';
    17. echo "\n";
    18. echo '<br>Превью<br>';
    19. echo "\n";
    20. echo filter_bb($str);
    21. echo "\n";
    22.  
    23. $str = '[h]Заголовок 1[/h][h2]Заголовок 2[/h2][h3=center]Заголовок 3[/h3]';
    24. echo "\n";
    25. echo $str.'<br>';
    26. echo "\n";
    27. echo '<br>Превью<br>';
    28. echo "\n";
    29. echo filter_bb($str);
    30. echo "\n";
    31.  
    32. $str = '[sl]текст в квадратных скобках[sr]';
    33. echo "\n";
    34. echo $str.'<br>';
    35. echo "\n";
    36. echo '<br>Превью<br>';
    37. echo "\n";
    38. echo filter_bb($str);
    39. echo "\n";
    40.  
    41. echo '<br><br>';
    42. $str =
    43. '[p=center]'.
    44. '[table=b:1;w][tr][th=cs:4;bc:#EEEEEE][b]Смайлики[/b]'.
    45. '[tr][td]:)[td=ga]:)[td]:-)[td=ga]:-)'.
    46. '[tr][td];)[td=ga];)[td];-)[td=ga];-)'.
    47. '[tr][td];([td=ga]:([td];-([td=ga]:-('.
    48. '[tr][td]:D[td=ga]:D[td]:-D[td=ga]:-D'.
    49. '[tr][td]:o[td=ga]:o[td]:-o[td=ga]:-o'.
    50. '[tr][td]:p[td=ga]:p[td]:-p[td=ga]:-p'.
    51. '[tr][td=cs]:confused:[td=ga]:confused:'.
    52. '[tr][td=cs]:cool:[td=ga]:cool:'.
    53. '[tr][td=cs]:rolleyes:[td=ga]:rolleyes:'.
    54. '[tr][td=cs]:eek:[td=ga]:eek:'.
    55. '[tr][td=cs]:mad:[td=ga]:mad:'.
    56. '[/table]'.
    57. '[/p]'.
    58. '';
    59. echo "\n";
    60. echo $str.'<br>';
    61. echo "\n";
    62. echo '<br>Превью<br>';
    63. echo "\n";
    64. echo filter_bb($str, true);
    65. echo "\n";
    66.  
    67. ?>
    68.  
    69.  
     
  18. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    ёпта, ктож для создания таблиц юзает ббкоды??
    для таких дел обычно вводят теги типа
    HTML:
    1.  
    2. содержимое обработывают регулярками или домом...
    3.  
    4. [quote]$str =
    5.  '[p=center]'.
    6.  '[table=b:1;w][tr][th=cs:4;bc:#EEEEEE]Смайлики'.
    7.  '[tr][td]:)[td=ga]:)[td]:-)[td=ga]:-)'.
    8.  '[tr][td];)[td=ga];)[td];-)[td=ga];-)'.
    9.  '[tr][td];([td=ga]:([td];-([td=ga]:-('.
    10.  '[tr][td]:D[td=ga]:D[td]:-D[td=ga]:-D'.
    11.  '[tr][td]:o[td=ga]:o[td]:-o[td=ga]:-o'.
    12.  '[tr][td]:p[td=ga]:p[td]:-p[td=ga]:-p'.
    13.  '[tr][td=cs]:confused:[td=ga]:confused:'.
    14.  '[tr][td=cs]:cool:[td=ga]:cool:'.
    15.  '[tr][td=cs]:rolleyes:[td=ga]:rolleyes:'.
    16.  '[tr][td=cs]:eek:[td=ga]:eek:'.
    17.  '[tr][td=cs]:mad:[td=ga]:mad:'.
    18.  '[/table]'.
    19.  '[/p]'.
    20.  '';[/quote]
    21.  
    22. убил ..
     
  19. Kreker

    Kreker Старожил

    С нами с:
    8 апр 2007
    Сообщения:
    5.433
    Симпатии:
    0
    Юзает, юзает...
     
  20. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    Kreker
    далой html, BBcodes спасут мир!
    доалой php натив, smarty фореве!
     
  21. Gorns

    Gorns Активный пользователь

    С нами с:
    14 сен 2008
    Сообщения:
    19
    Симпатии:
    0
    Вопрос в другом. как сделать этот код безопасным, без применения автоматов, пулеметов и т.п.

    PHP:
    1.  
    2.  
    3. function parseBBcode($var)
    4.     {
    5.        
    6.        
    7.         $search = array(
    8.                 '/\[b\](.*?)\[\/b\]/is',
    9.                 '/\[i\](.*?)\[\/i\]/is',
    10.                 '/\[u\](.*?)\[\/u\]/is',
    11.                 '/\[img\](.*?)\[\/img\]/is',
    12.                 '/\[quote\](.*?)\[\/quote\]/is',
    13.                 '/\[url\](.*?)\[\/url\]/is',
    14.                 '/\[url\=(.*?)\](.*?)\[\/url\]/is',
    15.                 '/\[youtube\](.*?)\[\/youtube\]/is'
    16.                 );
    17.  
    18.         $replace = array(
    19.                 '<strong>$1</strong>',
    20.                 '<em>$1</em>',
    21.                 '<u>$1</u>',
    22.                 '<img src="$1" />',
    23.                 '<div class=quote>$1</div>',
    24.                 '<noindex><a target=_blank rel="nofollow" href="$1">$1</a></noindex>',
    25.                 '<noindex><a target=_blank rel="nofollow" href="$1">$2</a></noindex>',
    26.                 '<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/$1&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="http://www.youtube.com/v/$1&hl=en&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"></embed></object>'
    27.                 );
    28.  
    29.         $var = preg_replace ($search, $replace, $var);
    30.  
    31.         return $var;
    32.     }
    33.  
     
  22. Gorns

    Gorns Активный пользователь

    С нами с:
    14 сен 2008
    Сообщения:
    19
    Симпатии:
    0
    я так понимаю, что туда можно внедрить

    Код (Text):
    1.  system \(сюрприз\) )/e %00
    ?
     
  23. Mae Stro

    Mae Stro Активный пользователь

    С нами с:
    21 ноя 2008
    Сообщения:
    14
    Симпатии:
    0
    Адрес:
    Киров
     
  24. Mae Stro

    Mae Stro Активный пользователь

    С нами с:
    21 ноя 2008
    Сообщения:
    14
    Симпатии:
    0
    Адрес:
    Киров
    в подготовке статей и текстов юзерами - очень может быть.
    Чушь :)
    Имхо, лучший шаблонизатор - strtr. Достаточно для упихания контента в тематический шаблон. Все остальное - хня.
     
  25. Mae Stro

    Mae Stro Активный пользователь

    С нами с:
    21 ноя 2008
    Сообщения:
    14
    Симпатии:
    0
    Адрес:
    Киров
    Читал многа статей, где многа букофф. Долго думал. Об стену убеватся не пришлось.

    Ладно, все это лирика, но с чего вы решили, что ваш скрипт подвержен подобным атакам? Давайте разберемся. Чтоб получить ключ /e, нужно смодифицировать паттерны поиска, не более и не менее. У вас они статические, так какого дьявола беспокоиться? Беспокоиться надо имхо исключительно в том случае, если паттерн поиска является продуктом вычислений с использованием переменных, которые в теории могут содержать начинку от GET, POST и COOKIE.

    Мнение экспертов весьма интересно будет увидеть.