Рекурсивное регулярное выражение в PHP с именами переменных

Я пытаюсь сделать движок на основе bbcode для своего веб-сайта. Но дело в том, что непонятно, какие коды доступны, потому что коды делают пользователи. И вдобавок ко всему, все это должно быть рекурсивным.

Например:

Hello my name is [name user-id="1"]
I [bold]really[/bold] like cheeseburgers

Это самые простые, и я добился того, чтобы они работали.

Теперь проблема в том, что происходит, когда два из этих кодов находятся друг за другом:

I [bold]really[/bold] like [bold]cheeseburgers[/bold]

Или внутри друг друга

I [bold]really like [italic]cheeseburgers[/italic][/bold]

Эти коды также могут иметь атрибуты

I [bold strengh="600"]really like [text font-size="24px"]cheeseburgers[/text][bold]

Следующий работал довольно хорошо, но ему не хватало рекурсивной части (?R)

(?P<code>\[(?P<code_open>\w+)\s?(?P<attributes>[a-zA-Z-0-1-_=" .]*?)](?:(?P<content>.*?)\[\/(?P<code_close>\w+)\])?)

Я просто не знаю, куда поставить рекурсивный тег (?R).

Также система должна знать, что в этой строке здесь

I [bold]really like [italic]cheeseburgers[/italic][/bold] and [bold]football[/bold]

являются 2 "кодовыми объектами":

1. [bold]really like [italic]cheeseburgers[/italic][/bold]

и

2. [bold]football[/bold]

... и содержание первого

really like [italic]cheeseburgers[/italic]

в котором снова есть код

[italic]cheeseburgers[/italic]

какой контент

cheeseburgers

Я искал в Интернете в течение двух дней, и я не могу понять это.

Я подумал о чем-то вроде этого:

  1. Найдите что-то вроде [**** attr="foo"] с необязательными атрибутами и сохраните их в группе захвата.
  2. Посмотрите, есть ли где-нибудь закрывающий тег (также может быть необязательным)
  3. Если существует закрывающий тег, все, что находится между двумя тегами, должно быть сохранено как группа захвата «контента», которая затем должна снова пройти ту же процедуру.

Я надеюсь, что есть специалисты по регулярным выражениям, которые готовы мне помочь. :(

Спасибо!

ИЗМЕНИТЬ

Поскольку это может быть трудно понять, вот ввод и ожидаемый вывод:

Вход:

[heading icon="rocket"]I'm a cool heading[/heading][textrow][text]<p>Hi!</p>[/text][/textrow]

Я хотел бы иметь массив, например

array[0][name] = heading
array[0][attributes][icon] = rocket
array[0][content] = I'm a cool heading
array[1][name] = textrow
array[1][content] = [text]<p>Hi!</p>[/text]
array[1][0][name] = text
array[1][0][content] = <p>Hi!</p>

person SunTastic    schedule 22.12.2015    source источник
comment
Я бы посмотрел на эту тему; stackoverflow.com/questions/6773192/recursive-bbcode-parsing.   -  person chris85    schedule 22.12.2015
comment
chris85, не слишком ли это просто? Я просто не могу использовать простую замену, потому что в некоторых кодах мне нужно вызывать классы, которые затем должны выполнять, например, некоторые функции базы данных. Мне нужны все данные, хранящиеся в массиве.   -  person SunTastic    schedule 22.12.2015
comment
anubhava, [heading icon=rocket]Я крутой заголовок[/heading][textrow][text]‹p›Привет!‹/p›[/text][/textrow] — здесь мне нужен массив, который говорит ок у нас есть два кода: заголовок и текст, в первом из них я классный заголовок в качестве содержимого (плюс значок атрибута, который является ракетой), во втором есть [текст]‹p›Hi!‹/p›[/text] внутри - который СНОВА имеет код внутри текста с содержимым ‹p›Hi!‹/p› – Таким образом, должно быть дерево-массив, которое представляет структуру   -  person SunTastic    schedule 22.12.2015
comment
Я добавил конкретный пример ввода и вывода в части вопроса EDIT.   -  person SunTastic    schedule 22.12.2015
comment
Я не знаю, как использовать (?R), но мне очень любопытно, как... вы можете попробовать что-то с этим шаблоном: (?s)\[(?!\/)([^\s\]]+)[^]]*\](.*?)\[\/\1\]   -  person    schedule 22.12.2015
comment
Проблема с использованием регулярных выражений для этого типа задач заключается в том, что это не то, в чем регулярные выражения хороши. Регулярные выражения предназначены для использования с обычным языком, у вас есть контекстно-свободная грамматика. CFG следует анализировать с помощью какого-либо конечного автомата с проталкиванием вниз вместо регулярного выражения. каждый обычный язык является контекстно-свободным. Обратное неверно: например, язык, состоящий из всех строк, имеющих то же количество букв a, что и b, является контекстно-свободным, но не регулярным. en.wikipedia.org/wiki/Regular_language   -  person Scott    schedule 22.12.2015
comment
Вот почему я рекомендую использовать мою библиотеку: github.com/thunderer/Shortcode с предоставленным RegularParser.   -  person Tomasz Kowalczyk    schedule 29.12.2015


Ответы (1)


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

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

Это имеет определенные преимущества, такие как возможность проверять каждый код и пропускать его, если он недействителен, а также обеспечивать соблюдение правильного порядка закрытия тегов ([bold][italic]Nesting![/bold][/italic] следует считать недействительным) и возможность предоставлять пользователю содержательные сообщения об ошибках, если что-то не так. (возможно, недопустимый параметр), потому что синтаксический анализатор точно знает, что происходит, тогда как регулярное выражение вывело бы что-то неожиданное и потенциально опасное.

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

person Niet the Dark Absol    schedule 22.12.2015
comment
Я уже думал об этом, но потом я полностью застрял с regex и regex101.com, чтобы поиграть с ними. Я действительно надеялся найти решение только с регулярным выражением. Но вы можете быть правы. Возможно, у вас есть предложение о том, как начать реализацию собственного парсера? - person SunTastic; 22.12.2015
comment
В качестве основной идеи вам понадобится $input = "..."; $pointer = 0; $output = "";, затем вы можете сделать что-то вроде while(is_int($bracket = strpos($input,'[',$pointer))) { $output .= substr($input,$pointer,$bracket-$pointer); /* do some regex to get the tag from substr($input,$bracket) and process stuff here - you will need to get the position of the ] in here */ $pointer = $closeBracketPosition; } — это очень простая идея, но, надеюсь, она поможет. - person Niet the Dark Absol; 22.12.2015