仿Smarty替换模板标签时遇到的问题
最近正在做一个微型的仿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替换模板标签时遇到的问题的更多相关文章
- linux下, 再次遇到使用thinkphp的模板标签时,报错used undefined function \Think\Template\simplexml_load_string() 是因为没有安装 php-xml包
linux下, 使用thinkphp的模板标签,如 eq, gt, volist defined, present , empty等 标签时, 报错: used undefined function ...
- django自定义过滤器及模板标签
创建一个模板库 不管是写自定义标签还是过滤器,第一件要做的事是创建模板库(Django能够导入的基本结构). 创建一个模板库分两步走: 第一,决定模板库应该放在哪个Django应用下. 如果你通过 m ...
- smarty 模板标签
smarty 模板标签 变量标签 数组变量标签 变量调节器 条件标签 循环标签 数组变量标签 模板加载标签 预定义变量标签 常量标签
- PHPSTORM+Thinkphp3.2模板标签替换Thinkphp5.1公式
FORMAT: purpose: find: replace 替换<php>标签 <php>(.*)</php> {php}$1{/php} 替换<if &g ...
- jQuery入门第二天&&&正则表达式完结篇——仿smarty引擎的制作
hi 周一完全的不在状态...中午还去观战,没有睡觉的我,晚上的smarty不知道能不能做完,加油吧 1.jQuery ---过滤性选择器(二)--- --[attribute=value]属性选择器 ...
- ECSHOP模板标签
模板制作修改经常用到ecshop模板标签: 页面关键字 {$keywords }页面标题 {$page_title}产品分类父分类列表 {foreach from=$categories item=c ...
- ECMall2.x模板制作入门系列之2(模板标签/语法)
ECMall2.x模板制作入门系列之2(模板标签/语法) 今天给大家带来一个模板语法的教程.希望能为ECMall模板制作者提供一份参考资料.如有问题.建议和意见,欢迎提出. 在ECMall模板中,用& ...
- Discuz!X/模板标签说明
Discuz 模板标签说明 Discuz! 的模板采用近似 PHP 表达式的语法,基本都是可识别的HTML,但涉及到变量和动态内容时,基本形式下: <!-{ 代码内容 }-> 逻辑元素包围 ...
- (转)DEDECMS模板原理、模板标签学习 - .Little Hann
本文,小瀚想和大家一起来学习一下DEDECMS中目前所使用的模板技术的原理: 什么是编译式模板.解释式模板,它们的区别是什么? 模板标签有哪些种类,它们的区别是什么,都应用在哪些场景? 学习模板的机制 ...
随机推荐
- Open vSwitch流表应用实战
本文参考:Open vSwitch流表应用实战 一个通过改变流表下发而实现的互相通信实验. 实验目的: 掌握Open vSwitch下发流表操作: 掌握添加.删除流表命令以及设备通信的原理. 原理:. ...
- mysq数据库再次理解
1.表中的一条记录就是一个object,object有很多属性,对应表中的字段.object的属性对应的值就是字段值 2.外键是关联表关系用的.表关系的确立只能通过外键 但更高效的策略是,在数据库中部 ...
- Irrlicht引擎I 配置
游戏是一个比较大的系统,包含了图形引擎.网络.AI.声音.UI等模块,模块的开发可能会分别进行或者采用开源项目,Irrlicht引擎基本包含了这些模块,不过在使用中也会陆续加入其它的模块.以前开发的程 ...
- zabbix 2.2.2在centos 6.3 x86_64上的安装
zabbix 2.2.2在centos 6.3 x86_64上的安装 更新五月 03, 2014 # 依赖环境 yum install -y php-mbstring mysql-deve ...
- 浏览器cookie数
<?php for ($w=0; $w < 200 ; $w++) { setcookie('name'.$w,'value'.$w, time()+3600*10 ); } var_du ...
- Peeking into Apache Flink's Engine Room
http://flink.apache.org/news/2015/03/13/peeking-into-Apache-Flinks-Engine-Room.html Join Processin ...
- 转:java.sql.SQLException: [Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序
在Win7 64位系统下,使用Java+Access数据库编程,用Java连数据库时,出现错误提示,如下: Java java.sql.SQLException: [Microsoft][ODBC 驱 ...
- java Reentrant Lock
//Listing 7-1. Achieving Synchronization in Terms of Reentrant Locks import java.util.concurrent.Exe ...
- 数据传输:JSON,XML
一.调用Ajax需要的JSON数据 1.url 处理页面 2.data 传递数据 3.datatype返回数据类型 4.type 提交数据方式 5.success成功之后 ...
- Lambda中的一些方法的总结
public List<UserInfoBaseModel> GetNameByIDList(List<int> UserID) { var UserList = LoadRe ...