最近正在做一个微型的仿TP框架,当然以鄙人之技术只能略仿表层,于是遇到的问题层出不穷。今天做到View层替换模板部分,本以为一下子搞掂的事,果不其然又是败下阵来。

好了,来重点。

模板文件 test1.tpl

{foreach from=$phone key=k item=v name=phones}
    <tr>
        <td>{$smarty.foreach.phones.iteration}</td>
        <td>{$v.brand}</td>
        <td>{$v.ver}</td>
        <td>{$v.conf}</td><td>{$v.price|test:20}</td>
        <td>{$v.date}</td></tr>
{/foreach}

而Smarty将以上代码替换为:

<?php
$_from = $_smarty_tpl->tpl_vars['phone']->value;
if (!is_array($_from) && !is_object($_from))
{
    settype($_from, 'array');
}
$__foreach_phones_1_saved = isset($_smarty_tpl->tpl_vars['__smarty_foreach_phones']) ? $_smarty_tpl->tpl_vars['__smarty_foreach_phones'] : false;
$__foreach_phones_1_saved_item = isset($_smarty_tpl->tpl_vars['v']) ? $_smarty_tpl->tpl_vars['v'] : false;
$__foreach_phones_1_saved_key = isset($_smarty_tpl->tpl_vars['k']) ? $_smarty_tpl->tpl_vars['k'] : false;
$_smarty_tpl->tpl_vars['v'] = new Smarty_Variable();
$_smarty_tpl->tpl_vars['__smarty_foreach_phones'] = new Smarty_Variable(array('iteration' => 0));
$_smarty_tpl->tpl_vars['k'] = new Smarty_Variable();
$_smarty_tpl->tpl_vars['v']->_loop = false;
foreach ($_from as $_smarty_tpl->tpl_vars['k']->value => $_smarty_tpl->tpl_vars['v']->value) 
{
    $_smarty_tpl->tpl_vars['v']->_loop = true;
    $_smarty_tpl->tpl_vars['__smarty_foreach_phones']->value['iteration']++;
    $__foreach_phones_1_saved_local_item = $_smarty_tpl->tpl_vars['v'];
?>
<tr>
        <td><?php echo (isset($_smarty_tpl->tpl_vars['__smarty_foreach_phones']->value['iteration']) ? $_smarty_tpl->tpl_vars['__smarty_foreach_phones']->value['iteration'] : null);?></td>
        <td><?php echo $_smarty_tpl->tpl_vars['v']->value['brand'];?></td>
        <td><?php echo $_smarty_tpl->tpl_vars['v']->value['ver'];?></td>
        <td><?php echo $_smarty_tpl->tpl_vars['v']->value['conf'];?></td>
        <td><?php echo smarty_modifier_test($_smarty_tpl->tpl_vars['v']->value['price'],20);?></td>
        <td><?php echo $_smarty_tpl->tpl_vars['v']->value['date'];?></td>
<?php
    $_smarty_tpl->tpl_vars['vv'] = $__foreach_v_2_saved_local_item;
}
if ($__foreach_v_2_saved_item) 
{
    $_smarty_tpl->tpl_vars['vv'] = $__foreach_v_2_saved_item;
}
if ($__foreach_v_2_saved_key) 
{
    $_smarty_tpl->tpl_vars['vk'] = $__foreach_v_2_saved_key;
}
?>
</tr>

这替换的代码也怪吓人的,其实分析也就三个部分:

仿Smarty替换模板标签时遇到的问题

 

  好了,问题来了,由于foreach标签的特殊性,{/foreach}结束标签替换后第三部分不是单单的?>php脚本结束标签,而是有第一部分声明而来的变量代码,因此直接替换标签不可行。于是想到用正则解决。

  搞了一个下午,一直有个困扰问题,在很多情况下都不可能是单单一个foreach,其内部有可能有嵌套不止一个foreach循环,以我目前认知水平,用正则直接匹配替换显然不可行。

  于是想到先用正则匹配出所有的{foreach}{/foreach},【1】匹配替换过程中将匹配的标签加入到一个临时数组中,以该数组下标和一个MD5值组成的混合值替换模板中该标签所在的位置作标识占位。【2】排列好临时数组中的foreach闭合标签后,【3】将排列好的foreach内数值进行匹配替换成上图代码的样子,【4】再根据该标签所在的下标替换回编译文件中的标识占位即可。

  【1】这里用到PHP一个强大的(我认为)正则替换函数preg_replace_callback《执行一个正则表达式搜索并且使用一个回调进行替换》

//模拟Smarty内部处理模板替换
$l_delimiter
='{'; $r_delimiter='}'; $_foreach=md5('foreach'); $_foreach_count=0; $_foreach_arr=array(); $reg_foreach="/\\$l_delimiter\s*".'\/?\s*\bforeach\b[^\\'.$r_delimiter.']*\\'.$r_delimiter.'/i'; $content=preg_replace_callback($reg_foreach,'_replace_foreach', $content); function _replace_foreach($match) { global $_foreach_arr,$_foreach,$_foreach_count; $_foreach_arr[]=$match[0]; $rt=$_foreach.$_foreach_count.'_'; $foreach_count++; return $rt; } echo '<pre>'; print_r($foreach_arr); echo '</pre>';

原模板foreach部分代码:

 1 {foreach from=$phone key=k item=v name=phones}
 2     <tr>
 3         <td>{$smarty.foreach.phones.iteration}</td>
 4         <td>{$v.brand}</td>
 5         <td>{$v.ver}</td>
 6         <td>{$v.conf}</td>
 7         {foreach from=v key=vk item=vv name=v}//这是用于测试标签嵌套,实际不可能如此
 8         <td>{$v.price|test:20}</td>
 9         <td>{$v.date}</td>
10         {/foreach}//这是用于测试标签嵌套,实际不可能如此
11     </tr>
12     {/foreach}
13 
14     {foreach $phone as $v}
15     <tr>
16         <td>{$smarty.foreach.phones.iteration}</td>
17         <td>{$v.brand}</td>
18         <td>{$v.ver}</td>
19         <td>{$v.conf}</td>
20         <td>{$v.price|test:20}</td>
21         <td>{$v.date}</td>
22     </tr>
23     {/foreach}
24 
25     {foreach $phone as $k=>$v name=phonename}
26     <tr>
27         <td>{$smarty.foreach.phones.iteration}</td>
28         <td>{$v.brand}</td>
29         <td>{$v.ver}</td>
30         <td>{$v.conf}</td>
31         <td>{$v.price|test:20}</td>
32         <td>{$v.date}</td>
33     </tr>
34     {/foreach}
View Code

相关文章: