问题描述
概述
大约在2009年底,我为PHP / HTML编写了一个简单的模板系统,小册子类型网站的设计师。该系统的目标是允许通过由PHP处理的自定义标签以其他纯HTML格式进行模板化。例如,模板页面可能如下所示:
< tt:Page template =templates / main.html> ;
< tt:内容名称=leftColumn>
< p>等等等等< / p>
...
< / tt:内容>
< tt:内容名称=rightColumn>
< p>等等等等< / p>
...
< / tt:内容>
< / tt:Page>
模板本身可能如下所示:
< HTML>
< head> ...< / head>
< body>
< div style =float:left; width:45%>
< tt:容器名称=leftColumn/>
< / div>
< div style =width:45%>
< tt:容器名称=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>等等等等< / p>
...
<?php $ tags-> pop(); ?>
<?php $ tags-> push('tt','Content',array('name'=>'rightColumn')); ?>
< p>等等等等< / p>
...
<?php $ tags-> pop(); ?>
<?php $ tags-> pop(); ?>
好的,坏的和 现在,如何执行我们新生成的PHP代码?我可以在这里想到几个选项。最简单的就是简单的 我曾考虑过使用临时或缓存文件,使用 问题 对于此列表中的每一件事情:这是一个好主意吗?你能想到一个更好的选择吗? 感谢阅读和TIA的任何建议。 :) 让我提倡一种不同的方法。不是动态生成PHP代码,然后试图找出如何安全地执行它,而是在遇到标记时直接执行它。您可以处理整个HTML块,并立即处理每个标记。 编写一个循环,查找标记。它的基本结构如下所示: eval
$ b eval
这个字符串,而且工作得很好。然而,任何程序员都会告诉你eval是邪恶的,不要使用它......所以问题是,有什么比我们可以使用的 eval
更合适这里?
php://
输出流等,但据我所见,这些不会比 eval
提供任何真正的优势。缓存可以加快速度,但实际上我们在这个网站上的所有网站已经非常快速,所以我认为在这一点上不需要进行速度优化。
$ tags
堆栈,您可能无需将其保存到任何地方)。 执行标记的相应代码。不要生成调用 $ tags-> push
的代码,只需直接调用 $ tags-> push
即可。 / li>
eval
的需求已经消失。
基本上有两种情况适用于步骤#3。当你遇到一个开标签时,你会立即执行 push
。然后,当您点击结束标记时,您可以执行 pop
,然后以适当的方式处理标记,现在您已经处理了自定义元素的全部内容。
以这种方式处理HTML也更高效。进行多次搜索并替换长HTML字符串效率不高,因为每个搜索和每个替换字符串的长度都是O( )。这意味着你一遍又一遍地重复扫描字符串,并且每次你做一个替换时,你都必须生成类似长度的全新字符串。如果您有20KB的HTML,那么每个替换包括搜索20KB,然后创建一个新的20KB字符串。
Overview
Around the end of 2009, I wrote a simple templating system for PHP/HTML to be used in-house by our designers for brochure-ware type websites. The goal of the system is to allow templating in otherwise pure HTML via custom tags that are processed by PHP. For example, a templated page might look like this:
<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>
The template itself might look something like this:
<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>
Besides the Page and Content/Container tags, there are a few other tags included in the core for stuff like flow control, iterating over a collection, outputting dynamic values, etc. The framework is designed so it's very easy to add your own set of tags registered under another prefix and namespace.
Custom Tags to PHP
How do we parse these custom tags? Since the're no guarantee that the HTML file is well-formed XML, solutions like XSLT/XPATH won't be reliable. Instead, we use a regex to look for tags with registered prefixes, and replace those with PHP code. The PHP code is a stack-based design... upon encountering an opening tag, an object representing the tag is created pushed onto the stack, and its "initialization function" (if any) runs. Whenever a registered closing tag is encountered, the most recent object is popped off the stack, and its "rendering function" runs.
So, after the framework replaces the templating tags with PHP, our example page might look something like this (in realty it's a bit uglier):
<?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(); ?>
The good, the bad, and eval
Now, how to execute our newly-generated PHP code? I can think of a few options here. The easiest is to simply eval
the string, and that works well enough. However, any programmer will tell you "eval is evil, don't use it..." so the question is, is there anything more appropriate than eval
that we can use here?
I've considered using a temporary or cached file, using php://
output streams, etc, but as far as I can see these don't offer any real advantage over eval
. Caching could speed things up, but in practice all the sites we have on this thing are already blazingly fast, so I see no need to make speed optimizations at this point.
Questions
For each of the things on this list: is it a good idea? Can you think of a better alternative?
- the whole idea in general (custom tags for html / php)
- converting tags to php code instead of processing directly
- the stack-based approach
- the use of
eval
(or similar)
Thanks for reading and TIA for any advice. :)
Let me advocate a different approach. Instead of generating PHP code dynamically and then trying to figure out how to execute it safely, execute it directly as you encounter the tags. You can process the entire block of HTML in one pass and handle each tag as you encounter it immediately.
Write a loop that looks for tags. Its basic structure will look like this:
- Look for a custom tag, which you find at position n.
- Everything before position n must be simple HTML, so either save it off for processing or output it immediately (if you have no tags on your
$tags
stack you probably don't need to save it anywhere). - Execute the appropriate code for the tag. Instead of generating code that calls
$tags->push
, just call$tags->push
directly. - Go back to step 1.
With this approach you only call PHP functions directly, you never build PHP code on the fly and then execute it later. The need for eval
is gone.
You'll basically have two cases for step #3. When you encounter an opening tag you will do an immediate push
. Then later when you hit the closing tag you can do a pop
and then handle the tag in the appropriate manner, now that you've processed the entire contents of the custom element.
It is also more efficient to process the HTML this way. Doing multiple search and replaces on a long HTML string is inefficient as each search and each replacement is O(n) on the length of the string. Meaning you're repeatedly scanning the string over and over, and each time you do a replacement you have to generate whole new strings of similar length. If you have 20KB of HTML then each replacement involves searching through that 20KB and then creating a new 20KB string afterwards.
这篇关于PHP - 使用自定义标签进行模板化 - 这是eval的合法用法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!