Dag*_*bit 16 php templates eval heredoc
更新 -感谢所有回复.这个问题变得有些混乱,所以如果有人感兴趣,我就开始续集了.
我正在为朋友拼凑一个快速脚本,并偶然发现了一种在PHP中进行模板化的简单方法.
基本上,我们的想法是将html文档解析为heredoc字符串,因此PHP中的变量将被扩展.
passthrough函数允许在字符串中进行表达式求值和函数以及静态方法调用:
function passthrough($s){return $s;}
$_="passthrough";
在heredoc字符串中解析文档的代码非常简单:
$t=file_get_contents('my_template.html');
eval("\$r=<<<_END_OF_FILE_\n$t\_END_OF_FILE_;\n");
echo $r;
唯一的问题是,它使用eval.
任何人都可以想到一种方法来做这种模板而不使用eval,但没有添加解析器或大量的正则表达式疯狂?
没有编写完整的解析器,任何逃避不属于PHP变量的流浪美元符号的建议?流浪的美元符号问题是否会使这种方法不适合"严重"使用?
这是一些模板化的HTML代码示例.
<script>var _lang = {$_(json_encode($lang))};</script>
<script src='/blah.js'></script>
<link href='/blah.css' type='text/css' rel='stylesheet'>
<form class="inquiry" method="post" action="process.php" onsubmit="return validate(this)">
  <div class="filter">
    <h2> 
      {$lang['T_FILTER_TITLE']}
    </h2>
    <a href='#{$lang['T_FILTER_ALL']}' onclick='applyFilter();'>
      {$lang['T_FILTER_ALL']}
    </a>
    {$filter_html}
  </div>
  <table class="inventory" id="inventory_table">
    {$table_rows}
    <tr class="static"><th colspan="{$_($cols+1)}">
      {$lang['T_FORM_HELP']}
    </th></tr>
    {$form_fields}
    <tr class="static">
      <td id="validation" class="send" colspan="{$cols}"> </td>
      <td colspan="1" class="send"><input type="submit" value="{$lang['T_SEND']}" /></td>
    </tr>
  </table>
</form>
关于在PHP中是否需要创建模板层有一些讨论,诚然,它已经非常擅长模板化.
模板的一些快速原因很有用:
你可以控制它
如果在文件进入解释器之前对文件进行预处理,则可以对文件进行更多控制.你可以注入东西,锁定权限,抓取恶意php/javascript,缓存它,通过xsl模板运行它,等等.
良好的MVC设计
模板化促进了视图与模型和控制器的分离.
当<?php ?>您在视图中跳入和跳出标记时,很容易变得懒惰并执行一些数据库查询或执行其他一些服务器操作.使用上面这样的方法,每个'block'只能使用一个语句(没有分号),因此陷入陷阱要困难得多.<?= ... ?>有相同的好处,但......
短标签并非始终启用
...我们希望我们的应用程序可以运行各种配置.
当我最初一起破解一个概念时,它开始作为一个php文件.但是在它增长之前,我并不高兴,除非所有的php文件<?php在开头只有一个,最后一个?>,最好都是类,除了控制器,设置,图像服务器等等.
我根本不想在我的视图中使用PHP,因为当看到类似这样的东西时,设计师会因为Dreamweaver或其他任何事情而感到困惑:
<a href="<?php $img="$img_server/$row['pic'].png"; echo $img; ?>">
  <img src="<?php echo $img; ?>" /></a>
这对程序员来说很难看.普通的平面设计师不会靠近它.这样的事情更容易应对:
<a href="{$img}"><img src="{$img}" /></a>
程序员将他讨厌的代码保留在html中,现在设计师可以完成他的设计魔术.好极了!
考虑到每个人的建议,我认为预处理文件是要走的路,中间文件应该尽可能接近正常的"php模板",模板是语法糖.当我玩它时,Eval现在仍然存在.heredoc的东西改变了它的作用.我稍后会写更多,并尝试回答一些答案,但现在......
<?php
class HereTemplate {
  static $loops;
  public function __construct () {
    $loops=array();
  }
  public function passthrough ($v) { return $v; }
  public function parse_markup ($markup, $no_escape=null, $vars=array()) {
    extract($vars);
    $eot='_EOT_'.rand(1,999999).'_EOT_';
    $do='passthrough';
    if (!$no_escape) $markup=preg_replace(
      array(
        '#{?{each.*(\$\w*).*(\$\w*).*(\$\w*).*}}?#', 
        '#{?{each.*(\$\w*).*(\$\w*).*}}?#', 
        '#{?{each}}?#',
        '#{{#', '#}}#',
        '#{_#', '#_}#',
        ),
      array(
        "<?php foreach (\\1 as \\2=>\\3) { ?>", 
        "<?php foreach (\\1 as \\2) { ?>", 
        "<?php } ?>",
        "<?php echo <<<$eot\n{\$this->passthrough(", ")}\n$eot\n ?>",
        "<?php ", " ?>",
        ), 
      $markup);
    ob_start(); 
    eval(" ?>$markup<?php ");
    echo $markup;
    return ob_get_clean();
  }
  public function parse_file ($file) {
    // include $file;
    return $this->parse_markup(file_get_contents($file));
  }
}
// test stuff
$ht = new HereTemplate();
echo $ht->parse_file($argv[1]);
?>
...
<html>
{{each $_SERVER $key $value}
<div id="{{$key}}">
{{!print_r($value)}}
</div>
{each}}
</html>
Spu*_*ley 24
PHP本身最初是用作模板语言(即允许您在HTML中嵌入代码的简单方法).
正如您从自己的示例中看到的那样,它过于复杂,无法证明在大多数情况下以这种方式使用它,因此良好的实践从此转而使用它作为传统语言,并且只打破<?php ?>标签可能.
麻烦的是,人们仍然想要一种模板语言,所以像Smarty这样的平台被发明了.但是如果你现在看看它们,Smarty支持像自己的变量和foreach循环这样的东西......不久之后,Smarty模板开始出现与PHP模板一样的问题; 你也可以在最初使用本机PHP.
我在这里要说的是,简单模板语言的理想实际上并不容易.实际上几乎不可能使它既不够简单,也不会吓跑设计师,同时给予它足够的灵活性来实际完成你需要它做的事情.
我会做一些愚蠢的事情并建议一些根本不需要模板引擎的东西,每个变量/调用只需要最多5个字符而不是你所拥有的 - 替换{$foo}为<?=$foo?>然后你可以用来include满足你所有的模板需求
如果你需要的只是变量替换,虽然这是一个模板函数,我实际上使用:
function fillTemplate($tplName,$tplVars){
  $tpl=file_get_contents("tplDir/".$tplName);
  foreach($tplVars as $k=>$v){
    $tpl = preg_replace('/{'.preg_quote($k).'}/',$v,$tpl);
  }
  return $tpl;
}
如果你想能够调用函数或有循环,基本上没有办法调用eval而不是预处理.
如果您不习惯使用像Twig这样的大型模板引擎(我诚挚地推荐),您仍然可以使用很少的代码获得良好的结果.
所有模板引擎共享的基本思想是使用友好,易于理解的语法编译模板,以快速和可缓存的PHP代码.通常,他们会通过解析您的源代码然后编译它来实现这一目标.但即使你不想使用复杂的东西,你也可以使用正则表达式获得好的结果.
所以,基本思路:
function renderTemplate($templateName, $templateVars) {
    $templateLocation = 'tpl/'      . $templateName . '.php';
    $cacheLocation    = 'tplCache/' . $templateName . '.php';
    if (!file_exists($cacheLocation) || filemtime($cacheLocation) < filemtime($templateLocation)) {
        // compile template and save to cache location
    }
    // extract template variables ($templateVars['a'] => $a)
    extract($templateVars);
    // run template
    include 'tplCache/' . $templateName . '.php';
}
所以基本上我们首先编译模板然后执行它.仅当缓存的模板尚不存在或模板的版本高于缓存中的模板时,才会进行编译.
那么,我们来谈谈编译.我们将定义两种语法:输出和控制结构.默认情况下,输出始终是转义的.如果您不想逃避它,您必须将其标记为"安全".这提供了额外的安全性 所以,这里是我们语法的一个例子:
{% foreach ($posts as $post): }
    <h1>{ $post->name }</h1>
    <p>{ $post->body }</p>
    {!! $post->link }
{% endforeach; }
所以,你{ something }用来逃避和回应一些东西.你{!! something}用来直接回应一些东西,而不是逃避它.而且你用{% command }执行一些PHP代码而不回显它(例如用于控制结构).
所以,这是编译代码:
$code = file_get_contents($templateLocation);
$code = preg_replace('~\{\s*(.+?)\s*\}~', '<?php echo htmlspecialchars($1, ENT_QUOTES) ?>', $code);
$code = preg_replace('~\{!!\s*(.+?)\s*\}~', '<?php echo $1 ?>', $code);
$code = preg_replace('~\{%\s*(.+?)\s*\}~', '<?php $1 ?>', $code);
file_put_contents($cacheLocation, $code);
就是这样.您必须注意,这比真正的模板引擎更容易出错.但它适用于大多数情况.此外请注意,这允许模板的编写者执行任意代码.这既是赞成也是骗局.
所以,这是整个代码:
function renderTemplate($templateName, $templateVars) {
    $templateLocation = 'tpl/'      . $templateName . '.php';
    $cacheLocation    = 'tplCache/' . $templateName . '.php';
    if (!file_exists($cacheLocation) || filemtime($cacheLocation) < filemtime($templateLocation)) {
        $code = file_get_contents($templateLocation);
        $code = preg_replace('~\{\s*(.+?)\s*\}~', '<?php echo htmlspecialchars($1, ENT_QUOTES) ?>', $code);
        $code = preg_replace('~\{!!\s*(.+?)\s*\}~', '<?php echo $1 ?>', $code);
        $code = preg_replace('~\{%\s*(.+?)\s*\}~', '<?php $1 ?>', $code);
        file_put_contents($cacheLocation, $code);
    }
    // extract template variables ($templateVars['a'] => $a)
    extract($templateVars, EXTR_SKIP);
    // run template
    include 'tplCache/' . $templateName . '.php';
}
我没有测试过上面的代码;)这只是基本的想法.
| 归档时间: | 
 | 
| 查看次数: | 6069 次 | 
| 最近记录: |