最近正在做一个微型的仿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>
这替换的代码也怪吓人的,其实分析也就三个部分:
好了,问题来了,由于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}