Я боролся с этим некоторое время.Я знаю, что это большой код, но я понятия не имею, в чем проблема, и не могу ее сузить.Я щедро награждаю.
Я написал этот класс для разбора bbcodes.Он в основном использует strtok (), и класс работает отлично, если вы не поместите два тега рядом друг с другом, и я не могу понять, почему.
Например, [b] [i]test1[/i] [/b]
приводит к<strong> <em>test1</em> </strong>
.Все же [b][i]test1[/i][/b]
приводит к <strong>i]test1/b]</strong>
.Последний тег </strong>
находится только там, потому что анализатор автоматически закрывает теги, для которых не удалось найти закрывающий тег в строке.Каким-то образом он полностью пропускает теги [i]
и [/b]
.
Вот класс, а также тот подкласс, который он использует для настройки различных bb-кодов.Подкласс - это просто структура данных без поведения.
<?php
// beware images can contain any url/any get request. beware of csrf
class Lev_TextProcessor_Extension_BbCode {
protected $elements = array();
protected $openTags = array();
public function __construct() {
$this->elements['b'] = new Lev_TextProcessor_Extension_BbCode_Element('<strong>', '</strong>');
$this->elements['i'] = new Lev_TextProcessor_Extension_BbCode_Element('<em>', '</em>');
$this->elements['u'] = new Lev_TextProcessor_Extension_BbCode_Element('<span style="text-decoration: underline;">', '</span>');
$this->elements['s'] = new Lev_TextProcessor_Extension_BbCode_Element('<span style="text-decoration: line-through;">', '</span>');
$this->elements['size'] = new Lev_TextProcessor_Extension_BbCode_Element('<span style="font-size: ', '</span>', 'px;">');
$this->elements['color'] = new Lev_TextProcessor_Extension_BbCode_Element('<span style="color: ', '</span>', ';">');
$this->elements['center'] = new Lev_TextProcessor_Extension_BbCode_Element('<div style="text-align: center;">', '</div>', '', true, true, false);
$this->elements['url'] = new Lev_TextProcessor_Extension_BbCode_Element('<a href="', '</a>', '">');
$this->elements['email'] = new Lev_TextProcessor_Extension_BbCode_Element('<a href="mailto:', '</a>', '">');
$this->elements['img'] = new Lev_TextProcessor_Extension_BbCode_Element('<img src="', '" alt="" />', '', false, false, true);
$this->elements['youtube'] = new Lev_TextProcessor_Extension_BbCode_Element('<object width="400" height="325"><param name="movie" value="http://www.youtube.com/v/{param}"></param><embed src="http://www.youtube.com/v/', '" type="application/x-shockwave-flash" width="400" height="325"></embed></object>', '', false, false, false);
$this->elements['code'] = new Lev_TextProcessor_Extension_BbCode_Element('<pre><code>', '
',' ', true, false, false);} публичная функция processText ($ input) {// предварительная обработка $ input = htmlspecialchars ($ input, ENT_NOQUOTES);$ input = nl2br ($ input);$ input = str_replace (array ("\ n", "\ r"), '', $ input);// начать основную обработку $ output = '';$ allow_child_tags = true;$ allow_child_quotes = true;$ string_segment = strtok ($ input, '[');do {// проверить содержимое на наличие кавычек if ($ allow_child_quotes === false) {if (strpos ($ string_segment, '"') === false) {$ output. = $ string_segment;}} else {// добавить содержимоедля вывода $ output. = $ string_segment;} $ tag_contents = strtok (']'); if (strpos ($ tag_contents, '/') === 0) {// закрывающий тег $ tag = substr ($ tag_contents, 1); if (isset ($ this-> elements [$ tag]) === true && array_search ($ tag, $ this-> openTags)! == false) {// найденный тег do {// закрывает теги до совпадениянайден тег $ last_open_tag = array_pop ($ this-> openTags); $ output. = $ this-> elements [$ last_open_tag] -> htmlAfter;} while= true;}} else {// открывающий тег // отделяет имя тега от аргумента, если есть один $ equal_pos = strpos ($ tag_contents, '='); if ($ equal_pos === false) {$ tag_name = $ tag_contents;} else {$ tag_name = substr ($ tag_contents, 0, $ equal_pos); $ tag_argument = substr ($ tag_contents, $ equal_pos + 1);} if (isset ($ this-> elements [$ tag_name]) = ==True) {// тег найден, если (($ this-> elements [$ tag_name] -> allowParentTags === true ||count ($ this-> openTags) === 0) && $ allow_child_tags === true) {// добавить тег в список открытых тегов и установить флаги $ this-> openTags [] = $ tag_name;$ allow_child_tags = $ this-> elements [$ tag_name] -> allowChildTags;$ allow_child_quotes = $ this-> elements [$ tag_name] -> allowChildQuotes;$ output. = $ this-> elements [$ tag_name] -> htmlBefore;// если аргумент существует if ($ equal_pos! == false) {if (strpos ($ tag_argument, '"') === false) {$ output. = $ tag_argument;}$ output. = $ this-> elements [$ tag_name] -> htmlCenter;}}}} $ string_segment = strtok ('[');} while ($ string_segment! == false);// закрываем оставшиеся теги while ($ tag = array_pop ($ this-> openTags)) {$ output. = $ this-> elements [$ tag] -> htmlAfter;} вернуть $ output;}}?>htmlBefore = $ html_before;$ this-> htmlAfter = $ html_after;$ this-> htmlCenter = $ html_center;$ this-> allowChildQuotes = $ allow_child_quotes;$ this-> allowChildTags = $ allow_child_tags;$ this-> allowParentTags = $ allow_parent_tags;}}?>
edit
Исправлено путем создания следующего класса для маркировки.
<?php
// unlike PHP's strtok() function, this class will not skip over empty tokens.
class Lev_TextProcessor_Tokenizer {
protected $string;
public function __construct($string) {
$this->string = $string;
}
public function getToken($token) {
$segment_length = strcspn($this->string, $token);
$token = substr($this->string, 0, $segment_length);
$this->string = substr($this->string, $segment_length + 1);
return $token;
}
}
?>