PHP - 使用自定义标签进行模板化 - 这是对 eval 的合法使用吗?
Posted
技术标签:
【中文标题】PHP - 使用自定义标签进行模板化 - 这是对 eval 的合法使用吗?【英文标题】:PHP - templating with custom tags - is this a legit use of eval? 【发布时间】:2011-03-20 13:54:42 【问题描述】:概述
大约在 2009 年底,我为 php/html 编写了一个简单的模板系统,供我们的设计师在内部用于宣传册类网站。该系统的目标是允许通过 PHP 处理的自定义标签在纯 HTML 中进行模板化。例如,模板页面可能如下所示:
<tt:Page template="templates/main.html">
<tt:Content name="leftColumn">
<p> blah blah </p>
...
</tt:Content>
<tt:Content name="rightColumn">
<p> blah blah </p>
...
</tt:Content>
</tt:Page>
模板本身可能看起来像这样:
<html>
<head>...</head>
<body>
<div style="float:left; width:45%">
<tt:Container name="leftColumn" />
</div>
<div style="width:45%">
<tt:Container name="rightColumn" />
</div>
</body>
</html>
除了页面和内容/容器标签之外,核心中还包含一些其他标签,用于流控制、迭代集合、输出动态值等。该框架的设计使添加您的在另一个前缀和命名空间下注册的自己的一组标签。
自定义标签到 PHP
我们如何解析这些自定义标签?由于不能保证 HTML 文件是格式良好的 XML,因此 XSLT/XPATH 之类的解决方案将不可靠。相反,我们使用正则表达式来查找带有注册前缀的标签,并将其替换为 PHP 代码。 PHP 代码是基于堆栈的设计……在遇到开始标签时,会创建一个表示该标签的对象并将其推送到堆栈上,并运行其“初始化函数”(如果有)。每当遇到已注册的结束标记时,最新的对象就会从堆栈中弹出,并运行其“渲染功能”。
所以,在框架用 PHP 替换模板标签后,我们的示例页面可能看起来像这样(实际上它有点丑):
<?php $tags->push('tt', 'Page', array('template'=>'templates/main.html')); ?>
<?php $tags->push('tt', 'Content', array('name'=>'leftColumn')); ?>
<p> blah blah </p>
...
<?php $tags->pop(); ?>
<?php $tags->push('tt', 'Content', array('name'=>'rightColumn')); ?>
<p> blah blah </p>
...
<?php $tags->pop(); ?>
<?php $tags->pop(); ?>
好的、坏的和eval
现在,如何执行我们新生成的 PHP 代码?我可以在这里想到几个选项。最简单的方法是简单地eval
字符串,这样就足够了。然而,任何程序员都会告诉你“eval 是邪恶的,不要使用它......”所以问题是,我们可以在这里使用比 eval
更合适的东西吗?
我考虑过使用临时文件或缓存文件,使用php://
输出流等,但据我所知,这些与eval
相比并没有任何真正的优势。缓存可以加快速度,但实际上我们在这个东西上拥有的所有网站都已经非常快了,所以我认为目前没有必要进行速度优化。
问题
对于此列表中的每一项:这是个好主意吗?你能想出更好的选择吗?
总体思路(html / php 的自定义标签) 将标签转换为php代码而不是直接处理 基于堆栈的方法eval
(或类似名称)的使用
感谢您的阅读和 TIA 的任何建议。 :)
【问题讨论】:
+1 表示格式良好、编写良好且经过深思熟虑的问题。 谢谢。 OT:我喜欢你的头像。几年前,来自 Evol Intent 的 Gigantor 来到这里时,穿着一件设计完全相同的 T 恤。它是唱片公司的标志还是什么的,或者只是一个随机的设计?多年来一直想知道。 【参考方案1】:让我提倡一种不同的方法。与其动态生成 PHP 代码然后试图弄清楚如何安全地执行它,不如在遇到标签时直接执行它。您可以一次性处理整个 HTML 块,并在遇到每个标签时立即处理。
编写一个查找标签的循环。它的基本结构如下所示:
-
查找自定义标记,您可以在 n 位置找到该标记。
位置 n 之前的所有内容都必须是简单的 HTML,因此要么将其保存以进行处理,要么立即输出(如果您的
$tags
堆栈上没有标签,您可能不需要保存任何地方)。
为标签执行适当的代码。无需生成调用$tags->push
的代码,只需直接调用$tags->push
。
返回步骤 1。
使用这种方法,您只需要直接调用 PHP 函数,您永远不会在运行中构建 PHP 代码,然后再执行它。不再需要eval
。
第 3 步基本上有两种情况。当您遇到开始标签时,您将立即执行push
。然后稍后当您点击结束标签时,您可以执行pop
,然后以适当的方式处理标签,现在您已经处理了自定义元素的全部内容。
以这种方式处理 HTML 也更有效。对长 HTML 字符串进行多次搜索和替换效率低下,因为每次搜索和每次替换在字符串长度上都是 O(n)。这意味着您一遍又一遍地反复扫描字符串,每次进行替换时,您都必须生成长度相似的全新字符串。如果您有 20KB 的 HTML,那么每次替换都需要搜索这 20KB,然后创建一个新的 20KB 字符串。
【讨论】:
John,这听起来像是严格纯 HTML(没有 PHP)的正确方法,我会试一试。然而,当我设计模板系统时,我认为如果你可以混合 PHP 会很好,我认为现在一些网站已经混合了它。也许这是一个糟糕的设计理念,但我认为它现在需要使用eval
或类似的。此外,在设计时,我认为缓存生成的 PHP 是个好主意。您认为这可能与您在这里的方法相当或更快吗?
缓存会减轻性能问题,因此您只需要担心安全方面。
如果您允许用户混入 PHP 代码,那么这完全是“另一个蜡球”。一旦您执行任意代码,那么担心调用 eval 就毫无意义了。然后,您的安全问题将变为对 PHP 代码进行沙箱处理、使用 PHP 的 safe_mode、锁定用户权限以使他们无法进行随机 system() 调用等。
约翰:很好,谢谢。该系统是为内部使用而设计的,所以最初我并不担心设计师会对其进行恶意操作。但是,情况会发生变化,并且可能很快就会有必要为此设置一个安全模式。由于我在我的 OP 中没有提到混入 PHP,所以我将其标记为正确。
啊,如果是内部开发人员,eval
走开! :-)【参考方案2】:
我发布了另一个答案,因为它与第一个完全不同,这也可能很有价值。
本质上,这个问题是询问如何使用正则表达式执行 PHP 代码。看起来可能不是那么明显,但这就是 eval 打算完成的事情。
话虽如此,您可以使用 PHP 的 preg_replace_callback 函数在匹配时执行一段代码,而不是执行 preg_replace 传递然后执行 eval。
请参阅此处了解该功能的工作原理:http://us.php.net/manual/en/function.preg-replace-callback.php
【讨论】:
迈克,看看我对约翰的回应(你们说的和我想的差不多)。【参考方案3】:是的,代替eval,你可以做zend和其他主要框架做的事情,使用输出缓冲:
ob_start();
include($template_file); //has some HTML and output generating PHP
$result = ob_get_contents();
ob_end_clean();
【讨论】:
这意味着将中间 PHP 代码(通过解析标记生成)保存到文件中,然后包含它。这涉及很多磁盘访问,而简单地使用“eval”则不会。这样做有什么好处? 我明白你的意思。这个答案没有错,但在这里不现实。我会发布另一个更理智的答案 @no,我的新答案已经发布。以上是关于PHP - 使用自定义标签进行模板化 - 这是对 eval 的合法使用吗?的主要内容,如果未能解决你的问题,请参考以下文章
在PHP与HTML混合输入的页面或者模板中就需要对PHP代码进行闭合