PHP-使用自定义标签模板-这是eval的合法使用吗?
-
29-09-2020 - |
题
概览
大约在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替换模板标签之后,我们的示例页面可能看起来像这样(在realty中它有点丑陋):
<?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的任何建议。:)
解决方案
让我主张采取不同的做法。而不是动态生成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字符串。
其他提示
我发布了另一个答案,因为它与第一个完全不同,这也可能是有价值的。
本质上,这个问题是询问如何使用正则表达式执行PHP代码。它可能看起来不那么明显,但这是eval打算完成的。
也就是说,您可以使用PHP的preg_replace_callback函数在匹配时执行一段代码,而不是执行preg_replace然后执行eval。
请参阅此处以了解该功能的工作原理: http://us.php.net/manual/en/function.preg-replace-callback.php
是的,而不是eval,你可以做zend和其他主要框架所做的事情,使用输出缓冲:
ob_start();
include($template_file); //has some HTML and output generating PHP
$result = ob_get_contents();
ob_end_clean();