最近正在做一个微型的仿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. Memcached 笔记与总结(1)Linux(CentOS 6.6) 和 Windows(7)下安装与配置 Memcached (1.4.24)与 Memcached 基础命令

    Memcached 官方网站:http://memcached.org/ 官网对其的描述是: What is Memcached? Free & open source, high-perfo ...

  2. IE6 — 你若安好,便是晴天霹雳 [ 乱弹 ]

    为什么还有人在用IE6?估计和中国的盗版业有很大关系吧.小白的电脑启不来了,请人重装系统,一张古老的Ghost搞定,IE6便落地生根.今天碰到一例报告说某网站在IE6下丑陋吓人,无心无力去解决,于是来 ...

  3. 送给和我一样曾经浮躁过的PHP程序猿

    送给和我一样曾经浮躁过的PHP程序猿   2012年偶决定开始写博客了,不为别的,就希望可以通过博客记录我的成长历程,同时也希望可以帮助一些刚毕业,刚入行业的兄弟姐们 们.我们是一群充满浮躁.抱怨.迷 ...

  4. HttpStatusCodeResult

    HttpStatusCodeResult:让mvc回传特定的http状态代码与消息给客户端,对于一些特殊的http响应,可利用httpStatusCodeResult帮助我们响应适当的状态代码: 1X ...

  5. Bit-Value Type

    https://dev.mysql.com/doc/refman/5.7/en/bit-type.html MySQL 5.7 Reference Manual  /  ...  /  Bit-Val ...

  6. cursor:pointer

    .cursor_hand{ cursor:pointer; }

  7. Tomcat7 安装StartSSL证书笔记

    1.Tomcat-Native安装 使用StartSSL,Tomcat必须用apr方式启动(apr方式对于静态的内容,比默认的bio效率要高很多倍) Windows下tomcat-native安装 直 ...

  8. 抓包工具Charles 【转】

      今天就来看一下Mac上如何进行抓包,之前有一篇文章介绍了使用Fidder进行抓包 http://blog.csdn.net/jiangwei0910410003/article/details/1 ...

  9. 应用github pages创建自己的个人博客

    首先你需要注册自己的github账号 1.登录或者注册github,登录之后点击右上角的“+”号,选择“New repository”菜单,创建仓库,用于存储和博客相关的源文件. 2.跳转页面将填写域 ...

  10. 用正则验证字符串格式,形如:A)XXX B)XXXX C)XXX

    今天遇到个小功能,要验证某个英文选项是否正确,例如:A)accumulate B)circling C)communities  D)competition  E)domestic F)financi ...