regex — регулярное выражение для пользовательского шаблона — вложение (PHP)

У меня проблема с созданием регулярных выражений для моего собственного движка шаблонов.
Мой код:

       $re_foreachelse = '%
{foreach (.+?) as (.+?)}
(
[^{]*
(?:
{
(?!/?foreach(.*?)})
[^{]*
)*
)
{foreachelse}
(
[^{]*
(?:
{
(?!/?foreach(.*?)})
[^{]*
)*
)
{/foreach}
%x';

$re_foreach = '%
{foreach (.+?) as (.+?)}
(
[^{]*
(?:
{
(?!/?foreach(.*?)})
[^{]*
)*
)
{/foreach}
%x';

while(preg_match($re_foreachelse, $this->tpl)) $this->tpl = preg_replace($re_foreachelse, '[foreach $1 as $2]$3[foreachelse]$4[/foreach]', $this->tpl);
while(preg_match($re_foreach, $this->tpl)) $this->tpl = preg_replace($re_foreach, '[foreach $1 as $2]$3[/foreach]', $this->tpl);

Я буду генерировать php-код, но для тестирования он меняет только «{}» на «[]». Моя проблема в том, что это работает для большинства вложенных тегов:

 [foreach $items as $item] $item [foreach $item as $i] $i [foreachelse][/foreach] $item [foreachelse][/foreach]
{foreach $items as $item} [foreach $tests as $test] [foreach $test as $t] [/foreach] [/foreach] {foreachelse} {/foreach}
[foreach $othertests as $test] [foreach $item as $i] $i [foreachelse][/foreach] [/foreach]

Однако, как вы можете видеть, один не изменился и все еще имеет «{}». Похоже, что вложение foreach с foreachelse в foreach работает, но не наоборот.

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

П.С .: Извините за мой плохой английский.

1

Решение

Проблема в том, что «foreach foreachelse» может быть включен в простой «foreach» и наоборот. Поэтому, независимо от того, какой порядок вы выберете для замены двух типов, проблема остается неизменной.
Другое дело, preg_match тесты бесполезны. Если вы хотите остановить замены, когда больше нет замен, используйте параметр «count» preg_replace и использовать do...while цикл, чтобы проверить это.

Итак, есть несколько решений:

использовать только одну петлю:

$count1 = 0; $count2 = 0;
do {
$this->tpl = preg_replace($re_foreachelse, '[foreach $1 as $2]$3[foreachelse]$4[/foreach]', $this->tpl, -1, $count1);
$this->tpl = preg_replace($re_foreach, '[foreach $1 as $2]$3[/foreach]', $this->tpl, -1, $count2);
} while ($count1 || $count2);

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

использовать только один шаблон с preg_replace_callback:

$re_foreachcommon = '%
{foreach (.+?) as (.+?)}
(
[^{]*
(?:
{
(?!/?foreach(.*?)})
[^{]*
)*
)
(?:
{foreachelse}
(
[^{]*
(?:
{
(?!/?foreach(.*?)})
[^{]*
)*
)
)?
{/foreach}
%x';

$count=0;
do {
$this->tpl = preg_replace_callback($re_foreachcommon, function ($m) {
return '[foreach ' .$m[1] . ' as ' . $m[2] . ']' . $m[3]
. ($m[4] ? '[foreachelse]' . $m[4] : '') . '[/foreach]';
}, $this->tpl, -1, $count);
} while ($count);

Самый быстрый способ, если это возможно:

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

// you store in this array, all the tokens that only needs a simple replacement
$trans = array('{/foreach}' => '[/foreach]',
'{foreachelse}' => '[foreachelse]',
'{/foreachelse}' => '[/foreachelse]');

$this->tpl = strtr($this->tpl, $trans);

// after you only need to make more advanced replacements
$this->tpl = preg_replace('%{foreach (.+?) as (.+?)}%', '[foreach $1 as $2]', $this->tpl);

Таким образом вы избежите проблемы с вложенностью и проанализируете вашу строку только два раза.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]