У меня проблема с созданием регулярных выражений для моего собственного движка шаблонов.
Мой код:
$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 работает, но не наоборот.
Я просто новичок в регулярных выражениях, и я действительно не знаю, в чем проблема.
Заранее спасибо за любые хорошие идеи.
П.С .: Извините за мой плохой английский.
Проблема в том, что «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);
Таким образом вы избежите проблемы с вложенностью и проанализируете вашу строку только два раза.
Других решений пока нет …