更新 -感谢所有回复。这个Q有点乱,所以如果有人感兴趣,我开始了续集。
我正在为朋友编写一个快速脚本,偶然发现了一种在 PHP 中进行模板的非常简单的方法。
基本上,这个想法是将html文档解析为heredoc字符串,因此其中的变量将由PHP扩展。
传递函数允许在字符串中进行表达式评估以及函数和静态方法调用:
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 已经非常擅长模板化。
模板有用的一些快速原因:
你可以控制它
如果您在文件进入解释器之前对其进行预处理,您就可以更好地控制它。你可以注入东西,锁定权限,抓取恶意 php / javascript,缓存它,通过 xsl 模板运行它,等等。
良好的 MVC 设计
模板促进了视图与模型和控制器的分离。
在视图中进出
<?php ?>
标签时,很容易变得懒惰并执行一些数据库查询或执行一些其他服务器操作。使用像上面这样的方法,每个“块”只能使用一个语句(没有分号),所以要陷入这个陷阱要困难得多。<?= ... ?>
有几乎相同的好处,但是...短标签并不总是启用
...并且我们希望我们的应用程序能够在各种配置上运行。
当我最初将一个概念组合在一起时,它从一个 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>