我在为自己的模板引擎创建正则表达式时遇到问题。 我的代码:
$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。:抱歉我的英语不好。
答案 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);
通过这种方式可以避免嵌套问题,并且只解析字符串两次。