自定义模板的正则表达式 - 嵌套(PHP)

时间:2014-11-22 10:55:30

标签: php regex nested

我在为自己的模板引擎创建正则表达式时遇到问题。 我的代码:

       $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 个答案:

答案 0 :(得分: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);

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