2014-11-22 57 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] 

但是,正如你所看到的,一个没有改变,仍然有“{}”。它看起来像foreachlse foreachlse foreachl foreach作品,但不是另一种方式。

我只是正则表达式的初学者,我真的不知道问题出在哪里。 非常感谢您提供任何好的建议。

P.S .:对不起,我的英语不好。

+1

仅使用regexp做这种工作注定会失败,您应该使用词法分析器+解析器。我的建议:看看[Twig代码](https://github.com/twigphp/Twig)([lexer](https://github.com/twigphp/Twig/blob/master/lib/Twig/Lexer) .php),[parser](https://github.com/twigphp/Twig/blob/master/lib/Twig/Parser.php)) – mTorres 2014-11-22 11:41:38

回答

0

问题是,“foreach foreachelse”可以包含在一个简单的“foreach”中,反之亦然。因此,无论您选择进行两种替换的顺序,问题都保持不变。 另一件事情,preg_match测试是无用的,如果您想在没有更多替代方法时停止替换,请使用preg_replace的“count”参数并使用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); 

以这种方式避免了嵌套问题,并且只解析了两次字符串。