PHP: Как удалить вложенные теги и перенести их не вложенным образом?

Мне нужно удалить все вхождения тега стиля bb из строки. Теги могут быть вложенными, и это то, где я терпит неудачу. Мне также необходимо переместить каждый тег и содержимое в конец строки и заменить тег на элемент HTML. Я попытался сыграть с регулярным выражением и preg_replace_callback, но я до сих пор не увенчался успехом. Я также пытался изменить следующее, а также не повезло: удаление вложенных bbcode (кавычек) в PHP и как удалить элемент html и его содержимое с помощью RegEx. Я не думаю, что могу использовать парсер HTML, как это, потому что HTML неверен (дети в элементах, которые не могут иметь детей).

Вот как выглядит строка:

This is some [tag] attribute=1 attribute2=1 [tag] attribute=1 attribute2=1 [/tag] [tag] attribute=1 attribute2=1 [/tag] [/tag] text. 

Результат должен выглядеть так:

 This is some text. <br attribute=1 attribute2=1> <br attribute=1 attribute2=1> <br attribute=1 attribute2=1> 

Любая помощь будет оценена по достоинству.

Улица cred: я работал в Infopop (позже известный как Groupee, теперь Social Strata), создатели UBBCode, то, что было скопировано и преобразовано в просто старый обычный «BBCode».

tl; dr: время, чтобы написать собственный парсер без регулярных выражений.


Большинство парсеров BBCode используют регулярные выражения, и это работает в большинстве случаев, но вы делаете что-то обычное. Обычные старые регулярные выражения не помогут вам. Регуляторы имеют два режима работы, которые находятся на нашем пути: мы можем либо сопоставить все между двумя тегами в режиме «жадный», либо в режиме «не жадный».

В «жадном» режиме мы будем захватывать все между первой задачей открытия и самым последним закрывающим тегом. Это ужасно ломает ситуацию. Возьмите этот случай:

 [a][b][c]...[/c][/b][/a]...[a]...[/a] 

Жадное регулярное выражение, подобное \[a\].+\[/a\] A \[a\].+\[/a\] , будет захватывать все, начиная с этого первого тега открытия, до последнего закрывающего тега, игнорируя тот факт, что ближе не закрывается открыватель.

Другой вариант хуже. Возьмите этот случай:

 [a][b][a]...[/a][/b][/a] 

Ungredy regex like \[a\].+?\[/a\] A \[a\].+?\[/a\] (единственное изменение – знак вопроса) будет соответствовать первому открывающему тегу, но тогда он будет соответствовать первому закрывающему тегу, снова игнорируя это закрывающий тег не принадлежит открытому тегу.

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

В тот момент, когда вы разрешаете вложение одинаковых тегов, слепая, грубая сила больше не является подходящим инструментом.

Если ни один из механизмов синтаксического анализа BBCode не будет работать для вас, вам, возможно, придется написать свой собственный. Проверьте их все. Есть некоторые из PEAR, есть расширение PECL и т. Д. Также проверяйте другие языки для вдохновения, CPAN Perl имеет дюжину различных реализаций, некоторые из которых очень мощные и сложные (если в этом миксе нет правильного рекурсивного анализатора спуска , Я буду в депрессии). Это хороший вызов, но это не слишком сложно. Опять же, я написал как пять сейчас (ни один из которых я не могу выпустить), так что, может быть, я предвзятый?

Начните с взрыва строки на [ и ] . Пройдите через результирующий массив, отслеживая, когда индекс массива, следующий за открывающей скобкой, и перед следующей закрывающей скобкой окажется похожим на действительный тег и / или атрибуты. Вам нужно будет подумать о том, что происходит, когда атрибут может содержать скобки или, что еще хуже, URL-адреса, которые являются тяжелыми (например, синтаксис массива PHP). Вам также необходимо подумать об атрибутах в целом, в том числе о том, как (если?) Они цитируются, если разрешено несколько атрибутов для каждого тега (как в вашем примере) и что делать с недопустимыми атрибутами.

По мере продолжения обработки строки вам также нужно будет отслеживать, какие теги открыты и в каком порядке. Вам нужно будет подумать о том, какие теги разрешены внутри других тегов. Вам также придется иметь дело с неправильным вложением, например [a][b][/a][/b] . Ваши параметры будут либо повторно открывать внутренний тег после закрытия внешней, либо закрывать внутреннюю, как только наружу делает. Хуже того, другое поведение может иметь смысл в зависимости от ситуации. Хуже, хуже, такие дурацкие теги, как [*] внутри [list] , который традиционно не имеет закрывающего тега!

После того, как вы обработали строку и создали список открытых и закрывающих тегов (и, возможно, перебалансировали открывания и закрытие), вы можете преобразовать результат в HTML или что бы ни закончился ваш вывод. Это когда и как вы перемещаете выходные данные этих тегов в конец нового документа.

Как только вы закончите, напишите тысячу тестовых случаев. Попытайтесь сломать его, взорвать его в кусочки битты, произвести уязвимости XSS, а в противном случае сделать все возможное, чтобы сделать вашу жизнь ад. Это будет стоить того, потому что результатом будет движок BBCode, который будет делать то, что вы пытаетесь сделать.