5

概述

大约在 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>

除了 Page 和 Content/Container 标签之外,核心中还包含一些其他标签,用于流控制、迭代集合、输出动态值等。该框架的设计使得添加您自己的一组非常容易在另一个前缀和命名空间下注册的标签。

自定义标签到 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 的任何建议。:)

4

3 回答 3

2

我发布了另一个答案,因为它与第一个完全不同,这也可能很有价值。

本质上,这个问题是询问如何使用正则表达式执行 PHP 代码。看起来可能不是那么明显,但这就是 eval 打算完成的事情。

话虽如此,您可以使用 PHP 的 preg_replace_callback 函数在匹配时执行一段代码,而不是执行 preg_replace 传递然后执行 eval。

请参阅此处了解该功能的工作原理:http ://us.php.net/manual/en/function.preg-replace-callback.php

于 2010-07-24T19:36:10.313 回答
2

让我提倡一种不同的方法。与其动态生成 PHP 代码然后试图弄清楚如何安全地执行它,不如在遇到标签时直接执行它。您可以一次处理整个 HTML 块,并在遇到每个标签时立即处理它。

编写一个查找标签的循环。它的基本结构如下所示:

  1. 查找您在位置n找到的自定义标签。
  2. 位置n之前的所有内容都必须是简单的 HTML,因此要么将其保存以供处理,要么立即输出(如果您的$tags堆栈上没有标签,您可能不需要将其保存在任何地方)。
  3. 为标签执行适当的代码。无需生成调用 的代码$tags->push,只需$tags->push直接调用即可。
  4. 回到步骤 1。

使用这种方法,您只需直接调用 PHP 函数,您永远不会在运行中构建 PHP 代码,然后再执行它。的需要eval已经消失了。

对于第 3 步,您基本上会有两种情况。当您遇到开始标签时,您将立即执行push. 然后稍后当您点击结束标签时,您可以执行 apop然后以适当的方式处理标签,因为您已经处理了自定义元素的全部内容。

以这种方式处理 HTML 也更有效。对长 HTML 字符串进行多次搜索和替换是低效的,因为每次搜索和每次替换在字符串长度上都是 O( n )。这意味着您一遍又一遍地反复扫描字符串,每次进行替换时,您都必须生成长度相似的全新字符串。如果您有 20KB 的 HTML,那么每次替换都需要搜索 20KB,然后创建一个新的 20KB 字符串。

于 2010-07-24T19:38:22.963 回答
0

是的,除了 eval,您可以做 zend 和其他主要框架所做的事情,使用输出缓冲:

ob_start();
include($template_file); //has some HTML and output generating PHP
$result = ob_get_contents();
ob_end_clean();
于 2010-07-24T19:15:15.067 回答