zoukankan      html  css  js  c++  java
  • 仿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_delimiters*".'/?s*foreach[^\'.$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部分代码:

     1 {foreach from=$phone key=k item=v name=phones}
     2     <tr>
     3         <td>{$smarty.foreach.phones.iteration}</td>
     4         <td>{$v.brand}</td>
     5         <td>{$v.ver}</td>
     6         <td>{$v.conf}</td>
     7         {foreach from=v key=vk item=vv name=v}//这是用于测试标签嵌套,实际不可能如此
     8         <td>{$v.price|test:20}</td>
     9         <td>{$v.date}</td>
    10         {/foreach}//这是用于测试标签嵌套,实际不可能如此
    11     </tr>
    12     {/foreach}
    13 
    14     {foreach $phone as $v}
    15     <tr>
    16         <td>{$smarty.foreach.phones.iteration}</td>
    17         <td>{$v.brand}</td>
    18         <td>{$v.ver}</td>
    19         <td>{$v.conf}</td>
    20         <td>{$v.price|test:20}</td>
    21         <td>{$v.date}</td>
    22     </tr>
    23     {/foreach}
    24 
    25     {foreach $phone as $k=>$v name=phonename}
    26     <tr>
    27         <td>{$smarty.foreach.phones.iteration}</td>
    28         <td>{$v.brand}</td>
    29         <td>{$v.ver}</td>
    30         <td>{$v.conf}</td>
    31         <td>{$v.price|test:20}</td>
    32         <td>{$v.date}</td>
    33     </tr>
    34     {/foreach}
    View Code

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

     1 1dfae1db802b03f8a8debd7a9a1cb34b0_
     2     <tr>
     3         <td><?php echo $_tpl_data["_smarty_sys_reserved_value"]["foreach"]["phones"]["iteration"]; ?></td>
     4         <td><?php echo $_tpl_data["v"]["brand"]; ?></td>
     5         <td><?php echo $_tpl_data["v"]["ver"]; ?></td>
     6         <td><?php echo $_tpl_data["v"]["conf"]; ?></td>
     7         1dfae1db802b03f8a8debd7a9a1cb34b1_
     8         <td><?php echo test($_tpl_data["v"]["price"],20); ?></td>
     9         <td><?php echo $_tpl_data["v"]["date"]; ?></td>
    10         1dfae1db802b03f8a8debd7a9a1cb34b2_
    11     </tr>
    12     1dfae1db802b03f8a8debd7a9a1cb34b3_
    13 
    14     1dfae1db802b03f8a8debd7a9a1cb34b4_
    15     <tr>
    16         <td><?php echo $_tpl_data["_smarty_sys_reserved_value"]["foreach"]["phones"]["iteration"]; ?></td>
    17         <td><?php echo $_tpl_data["v"]["brand"]; ?></td>
    18         <td><?php echo $_tpl_data["v"]["ver"]; ?></td>
    19         <td><?php echo $_tpl_data["v"]["conf"]; ?></td>
    20         <td><?php echo test($_tpl_data["v"]["price"],20); ?></td>
    21         <td><?php echo $_tpl_data["v"]["date"]; ?></td>
    22     </tr>
    23     1dfae1db802b03f8a8debd7a9a1cb34b5_
    24 
    25     1dfae1db802b03f8a8debd7a9a1cb34b6_
    26     <tr>
    27         <td><?php echo $_tpl_data["_smarty_sys_reserved_value"]["foreach"]["phones"]["iteration"]; ?></td>
    28         <td><?php echo $_tpl_data["v"]["brand"]; ?></td>
    29         <td><?php echo $_tpl_data["v"]["ver"]; ?></td>
    30         <td><?php echo $_tpl_data["v"]["conf"]; ?></td>
    31         <td><?php echo test($_tpl_data["v"]["price"],20); ?></td>
    32         <td><?php echo $_tpl_data["v"]["date"]; ?></td>
    33     </tr>
    34     1dfae1db802b03f8a8debd7a9a1cb34b7_
    View Code

    替换后可见原本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数组中。

    具体实现如下:

     1 function foreach_sort($arr)
     2 {
     3     $a_start=array();
     4     $a_result=array();
     5     for($i=0;$i<count($arr);$i++)
     6     {
     7         preg_match('/{s*/foreachs*}/i',$arr[$i],$match);
     8         if(!$match)//如果是开头
     9         {
    10             $a_start[]=$i;
    11         }
    12         else
    13         {    
    14             $a_result[]=array_pop($a_start).'->'.$i;
    15         }
    16     }
    17     return $a_result;
    18 }
    19 
    20 echo '<pre>';
    21 print_r(foreach_sort($foreach_arr));
    22 echo '</pre>';

    打印显示:

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

    排列OK。

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

    未完,明天继续...

  • 相关阅读:
    spring mvc velocity多视图
    ubuntu 的远程桌面
    nhibernate 3.3 linq扩展
    MongoDB资料汇总专题[转发]
    SQLServer 2008 删除、压缩日志
    VS2012和2010 设置framework版本
    引用的程序集 没有强名称
    Xamarin for OSX – SetUp
    Xamarin devexpress datagrid 样式
    Xamarin devexpress Grid
  • 原文地址:https://www.cnblogs.com/GaZeon/p/5290249.html
Copyright © 2011-2022 走看看