仿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中目前所使用的模板技术的原理: 什么是编译式模板.解释式模板,它们的区别是什么? 模板标签有哪些种类,它们的区别是什么,都应用在哪些场景? 学习模板的机制 ...
随机推荐
- [ZZ] Deferred Rendering and HDR
http://www.gamedev.net/topic/496785-deferred-rendering-and-hdr/ Quote: Original post by jstrohYeah I ...
- jQuery 实现带下拉提示且自动填充的邮箱
/* ** 本文例子所引用的jQuery版本为 jQuery-1.8.3.min.js ** Author:博客园小dee*/ 本文用 jQuery 实现一个用户输入字符时出现能够提示邮箱后缀名的下拉 ...
- 【Objective-C】Windows下Objective-C开发环境配置
[Objective-C]Windows下Objective-C开发环境配置 ftp://ftpmain.gnustep.org/pub/gnustep/binaries/windows/ 最近打 ...
- 推荐25款php中非常有用的类库
推荐25款php中非常有用的类库 投稿:hebedich 字体:[增加 减小] 类型:转载 时间:2014-09-29 作为一个PHP开发者,现在是一个令人激动的时刻.每天有许许多多有用的库分发出 ...
- 批量清除BOM头
批量清除BOM头 (2012-03-05 13:28:30) 转载▼ 标签: 杂谈 有些php文件由于不小心保存成了含bom头的格式而导致出现一系列的问题.以下是批量清除bom头的代码,复制代码, ...
- JavaScript 数组详解(转)
在程序语言中数组的重要性不言而喻,JavaScript中数组也是最常使用的对象之一,数组是值的有序集合,由于弱类型的原因,JavaScript中数组十分灵活.强大,不像是Java等强类型高级语言数组只 ...
- [转]为何TCP/IP协议栈设计成沙漏型的
http://m.blog.csdn.net/blog/dog250/18959371 前几天有人回复我的一篇文章问,为何TCP/IP协议栈设计成沙漏型的.这个问题问得好!我先不谈为何它如此设计,我一 ...
- MySQL服务器状态变量
一,最值得检查的状态变量使用show global status进行检测二.变量部分1.Aborted_clients如果这个变量持续增加,确定连接是否被关闭了.如果不是检查网络性能,并且检查max_ ...
- centos 6.X 安装scrapy-原创
特别注意[坑]: python版本一定大于 2.7.5,scrapy python 必须2.7以上centos 6.X系列 系统默认安装的python是2.6.6 ,本人折腾了很久2.7.3 在这里升 ...
- BLE-NRF51822教程19-Battery Service
Battery Service是有关电池特性方面的服务,如果需要它,在初始化时将它加入到蓝牙协议栈. 如果通过ble_bas_battery_level_update(),电池电量将会通知,Batte ...