2014-11-22 5 views
1

У меня проблема с созданием регулярных выражений для моего собственного механизма шаблонов. Мой код:Регулярное выражение для настраиваемого шаблона - вложенность (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 работает, но не наоборот.

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

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

+1

Выполнение такого рода работы с использованием только регулярного выражения обречено на провал, вы должны использовать синтаксический анализатор lexer +. Мой совет: взгляните на [Twig code] (https://github.com/twigphp/Twig) ([lexer] (https://github.com/twigphp/Twig/blob/master/lib/Twig/Lexer) .php), [парсер] (https://github.com/twigphp/Twig/blob/master/lib/Twig/Parser.php)) – mTorres

ответ

0

Проблема заключается в том, что «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); 

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

Смежные вопросы