最近正在做一个微型的仿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. snowflake

    snowflake在分布式系统中生成全局id

  2. Json工具类,实现了反射将整个Object转换为Json对象的功能,支持Hibernate的延迟加

    package com.aherp.framework.util; import java.lang.reflect.Array;import java.lang.reflect.Method;imp ...

  3. mongodb 3.2 分片部署步骤

    #linux 网络优化1. 文件中/etc/sysctl.conf, 加入net.core.somaxconn = 2048fs.file-max = 2000000fs.nr_open = 2000 ...

  4. AMD GPU spec (public)

    http://www.x.org/docs/AMD/old/ Index of /docs/AMD/old Name Last modified Size Description Parent Dir ...

  5. emacs+ensime+sbt打造spark源码阅读环境

    欢迎转载,转载请注明出处,徽沪一郎. 概述 Scala越来越流行, Spark也愈来愈红火, 对spark的代码进行走读也成了一个很普遍的行为.不巧的是,当前java社区中很流行的ide如eclips ...

  6. Javascript 笔记与总结(1-4)this

    js 中函数的 4 种调用方式: ① 作为普通函数来调用,this 的值指向 window,准确地说,this 为 null,被解释成为 window.在 ECMAScript5 标准中,如果 thi ...

  7. Linux 执行ThinkPHP 文件的计划任务

    执行例如 http://192.168.1.32/index.php?s=/Home/Cron/yue 这样的 url 的计划任务 方式① */ * * * * curl http://192.168 ...

  8. Windows上python开发--2安装django框架

    Windows上python开发--2安装django框架 分类: 服务器后台开发2014-05-17 21:22 2310人阅读 评论(2) 收藏 举报 python django 上一篇文章中讲了 ...

  9. Solr 连接数据库

    实际工程应用中,从数据库导出数据创建索引再常见不过了,现在实验一下从数据库导入数据创建索引. 一.版本说明 Solr版本:4.7.0 数据库:sqlserver2005 二.配置步骤 1.  准备的j ...

  10. PHP实例练习--投票和租房子

    一,调查问卷 效果图: