最近正在做一个微型的仿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部分代码:

 {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>
{foreach from=v key=vk item=vv name=v}//这是用于测试标签嵌套,实际不可能如此
<td>{$v.price|test:20}</td>
<td>{$v.date}</td>
{/foreach}//这是用于测试标签嵌套,实际不可能如此
</tr>
{/foreach} {foreach $phone as $v}
<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} {foreach $phone as $k=>$v name=phonename}
<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}

模板替换后的编译文件(只看替换foreach的部分)代码:

 1dfae1db802b03f8a8debd7a9a1cb34b0_
<tr>
<td><?php echo $_tpl_data["_smarty_sys_reserved_value"]["foreach"]["phones"]["iteration"]; ?></td>
<td><?php echo $_tpl_data["v"]["brand"]; ?></td>
<td><?php echo $_tpl_data["v"]["ver"]; ?></td>
<td><?php echo $_tpl_data["v"]["conf"]; ?></td>
1dfae1db802b03f8a8debd7a9a1cb34b1_
<td><?php echo test($_tpl_data["v"]["price"],20); ?></td>
<td><?php echo $_tpl_data["v"]["date"]; ?></td>
1dfae1db802b03f8a8debd7a9a1cb34b2_
</tr>
1dfae1db802b03f8a8debd7a9a1cb34b3_ 1dfae1db802b03f8a8debd7a9a1cb34b4_
<tr>
<td><?php echo $_tpl_data["_smarty_sys_reserved_value"]["foreach"]["phones"]["iteration"]; ?></td>
<td><?php echo $_tpl_data["v"]["brand"]; ?></td>
<td><?php echo $_tpl_data["v"]["ver"]; ?></td>
<td><?php echo $_tpl_data["v"]["conf"]; ?></td>
<td><?php echo test($_tpl_data["v"]["price"],20); ?></td>
<td><?php echo $_tpl_data["v"]["date"]; ?></td>
</tr>
1dfae1db802b03f8a8debd7a9a1cb34b5_ 1dfae1db802b03f8a8debd7a9a1cb34b6_
<tr>
<td><?php echo $_tpl_data["_smarty_sys_reserved_value"]["foreach"]["phones"]["iteration"]; ?></td>
<td><?php echo $_tpl_data["v"]["brand"]; ?></td>
<td><?php echo $_tpl_data["v"]["ver"]; ?></td>
<td><?php echo $_tpl_data["v"]["conf"]; ?></td>
<td><?php echo test($_tpl_data["v"]["price"],20); ?></td>
<td><?php echo $_tpl_data["v"]["date"]; ?></td>
</tr>
1dfae1db802b03f8a8debd7a9a1cb34b7_

替换后可见原本foreach标签所在的位置已经被一串md5码+数字+下划线替代。

打印显示:

Array
(
[0] => {foreach from=$phone key=k item=v name=phones}
[1] => {foreach from=v key=vk item=vv name=v}
[2] => {/foreach}
[3] => {/foreach}
[4] => {foreach $phone as $v}
[5] => {/foreach}
[6] => {foreach $phone as $k=>$v name=phonename}
[7] => {/foreach}
)

拿到了存储foreach的临时数组$foreach_arr,即可以【2】排列数组中的foreach闭合标签。

刚开始想到用递归,孰知递归及算法乃鄙人之短板,折腾了一晚,终究还是笨方法,使用foreach

以上面数组排列,正确应该是0->3;1->2;4->5;6->7;。思考良久,有一个思路。一个数组存头标签$a_start,每当遇到非{/foreach}的标签,则加入到$a_start数组中,一旦遇到{/foreach}标签,立刻将$a_start最后一个数据取出,加入到$a_result数组中。

具体实现如下:

 function foreach_sort($arr)
{
$a_start=array();
$a_result=array();
for($i=0;$i<count($arr);$i++)
{
preg_match('/\{\s*\/\bforeach\b\s*\}/i',$arr[$i],$match);
if(!$match)//如果是开头
{
$a_start[]=$i;
}
else
{
$a_result[]=array_pop($a_start).'->'.$i;
}
}
return $a_result;
} echo '<pre>';
print_r(foreach_sort($foreach_arr));
echo '</pre>';

打印显示:

Array
(
[0] => 1->2
[1] => 0->3
[2] => 4->5
[3] => 6->7
)

排列OK。

【3】将排列好的$foreach_arr内数值进行匹配替换。

未完,明天继续...

仿Smarty替换模板标签时遇到的问题的更多相关文章

  1. linux下, 再次遇到使用thinkphp的模板标签时,报错used undefined function \Think\Template\simplexml_load_string() 是因为没有安装 php-xml包

    linux下, 使用thinkphp的模板标签,如 eq, gt, volist defined, present , empty等 标签时, 报错: used undefined function ...

  2. django自定义过滤器及模板标签

    创建一个模板库 不管是写自定义标签还是过滤器,第一件要做的事是创建模板库(Django能够导入的基本结构). 创建一个模板库分两步走: 第一,决定模板库应该放在哪个Django应用下. 如果你通过 m ...

  3. smarty 模板标签

    smarty 模板标签 变量标签 数组变量标签 变量调节器 条件标签 循环标签 数组变量标签 模板加载标签 预定义变量标签 常量标签

  4. PHPSTORM+Thinkphp3.2模板标签替换Thinkphp5.1公式

    FORMAT: purpose: find: replace 替换<php>标签 <php>(.*)</php> {php}$1{/php} 替换<if &g ...

  5. jQuery入门第二天&&&正则表达式完结篇——仿smarty引擎的制作

    hi 周一完全的不在状态...中午还去观战,没有睡觉的我,晚上的smarty不知道能不能做完,加油吧 1.jQuery ---过滤性选择器(二)--- --[attribute=value]属性选择器 ...

  6. ECSHOP模板标签

    模板制作修改经常用到ecshop模板标签: 页面关键字 {$keywords }页面标题 {$page_title}产品分类父分类列表 {foreach from=$categories item=c ...

  7. ECMall2.x模板制作入门系列之2(模板标签/语法)

    ECMall2.x模板制作入门系列之2(模板标签/语法) 今天给大家带来一个模板语法的教程.希望能为ECMall模板制作者提供一份参考资料.如有问题.建议和意见,欢迎提出. 在ECMall模板中,用& ...

  8. Discuz!X/模板标签说明

    Discuz 模板标签说明 Discuz! 的模板采用近似 PHP 表达式的语法,基本都是可识别的HTML,但涉及到变量和动态内容时,基本形式下: <!-{ 代码内容 }-> 逻辑元素包围 ...

  9. (转)DEDECMS模板原理、模板标签学习 - .Little Hann

    本文,小瀚想和大家一起来学习一下DEDECMS中目前所使用的模板技术的原理: 什么是编译式模板.解释式模板,它们的区别是什么? 模板标签有哪些种类,它们的区别是什么,都应用在哪些场景? 学习模板的机制 ...

随机推荐

  1. FastDFS安装、配置、部署

    FastDFS是一个开源的,高性能的的分布式文件系统,他主要的功能包括:文件存储,同步和访问,设计基于高可用和负载均衡,FastDFS非常适用于基于文件服务的站点,例如图片分享和视频分享网站. Fas ...

  2. 基于 jQuery Jcrop 插件的功能模块:头像剪裁

    /** Jcrop version: 0.9.12 jQuery version: 1.9.0 Author: 小dee Date: 2014.10.30 */ 先看看他山之石:博客园的头像模块也是使 ...

  3. Write Cache-friendly Code

    Computer Systems A Programmer's Perspective Second Edition In Section 6.2, we introduced the idea of ...

  4. X5学习笔记—给单元格添加颜色

    设置grid某一个单元格的颜色 可以用dhtmlxgrid的原生态方法 setCellTextStyle (row_id, ind, styleString) 参数: rowid:行id cellin ...

  5. html轮播效果的实现

    要实现如下图的效果 点击可以选择图片:不点击的时候自动轮播:并且点击完后再次自动轮播. 思路:如同在房子里透过窗子看路过的火车一样,窗子是不动的,但火车是陆续经过窗子的,所以透过窗子可以看到依次看完所 ...

  6. js 键盘移动div、img对象

    js 键盘移动div.img对象 <html> <script type="text/javascript"> var EXtype="" ...

  7. phpexcel 读取数据

    最近公司做一个客户导入会员的功能,以前导入都是使用csv格式导入的,但是客户反应问题挺多的,普遍是乱码(由于各种系统各种环境可能引起编码问题).最近想着就把这个导入完全改成excel导入,就研究了下p ...

  8. ava.lang.NullPointerException的一般解决方法

    抛出异常后,一般会输出异常信息,, 从上往下找 ,第一次出现与"自己的代码"有关的部分,就是异常抛出的最近点,异常就是在那里开始的 然后再顺藤摸瓜 找问题去吧

  9. Delphi XE5 与其他版本共存

    来源于http://www.board4allcz.eu 作者是gateway 如果你想使用Delphi诸如XE4.XE3.XE2.XE之类的版本跟Delphi XE5共存的话,在cglm.ini中简 ...

  10. FW:使用weave管理docker网络

    Posted on 2014-11-12 22:20 feisky 阅读(1761) 评论(0) 编辑 收藏 weave简介 Weave creates a virtual network that ...