问题描述
对于一个小组项目,我正在尝试为PHP的用户创建模板引擎,这些人对该语言缺乏经验的人可以在HTML中使用{name}之类的标签,而PHP会使用数组中的预定义变量替换该标签.以及支持循环.
For a group project I am trying to create a template engine for PHP for the people less experienced with the language can use tags like {name} in their HTML and the PHP will replace that tag with a predefined variable from an array. As well as supporting loops.
这远远超出了项目的期望,但是根据我对PHP的经验,我认为让我保持忙碌是一个很好的挑战!
This is well beyond the expectations of the project, but as I have experience with PHP I thought it would be a good challenge to keep me busy!
我的主要问题是,如何执行解析器的循环部分,这是实现这种系统的最佳方法.在您推荐现有模板系统之前,我宁愿自己为经验创建它,因为我们项目中的所有内容都必须是我们自己的.
My main questions are, how do I do the loop part of the parser and is this the best way to implement such a system. Before you just recommend an existing template system, I would prefer to create it myself for experience and because everything in our project has to be our own.
目前,使用regex和preg_replace_callback完成基本解析时,它会检查$ data [name]是否存在,并替换它.
At the moment the basic parsing is done with regex and preg_replace_callback, it checks if $data[name] exists and if it does replaces it.
我尝试过各种不同的循环方式,但是不确定我是否走在正确的轨道上!
I have tried to do the loop a variety of different ways but am not sure if I am on the correct track!
如果给出了解析引擎的数据,则示例为:
An example if the data the parsing engine was given is:
Array
(
[title] => The Title
[subtitle] => Subtitle
[footer] => Foot
[people] => Array
(
[0] => Array
(
[name] => Steve
[surname] => Johnson
)
[1] => Array
(
[name] => James
[surname] => Johnson
)
[2] => Array
(
[name] => josh
[surname] => Smith
)
)
[page] => Home
)
它正在解析的页面是这样的:
And the page it was parsing was something like:
<html>
<title>{title}</title>
<body>
<h1>{subtitle}</h1>
{LOOP:people}
<b>{name}</b> {surname}<br />
{ENDLOOP:people}
<br /><br />
<i>{footer}</i>
</body>
</html>
它会产生类似于以下内容的东西:
It would produce something similar to:
<html>
<title>The Title</title>
<body>
<h1>Subtitle</h1>
<b>Steve</b> Johnson<br />
<b>James</b> Johnson<br />
<b>Josh</b> Smith<br />
<br /><br />
<i>Foot</i>
</body>
</html>
与此相伴,您的时间令人难以置信!
Your time is incredibly appreciated with this!
非常感谢,
P.s.我完全不同意这一点,因为我希望创建与经验已经存在的东西类似的东西,所以我格式合理且易于理解的问题会被否决.
P.s. I completely disagree that because I am looking to create something similar to what already exists for experience, my well formatted and easy to understand question gets down voted.
P.p.s似乎该主题的观点广为传播,请不要压倒人们,因为他们对您有不同的观点.每个人都有自己的权利!
P.p.s It seems there is a massive spread of opinions for this topic, please don't down vote people because they have a different opinion to you. Everyone is entitled to their own!
推荐答案
一种简单的方法是将模板转换为PHP并运行它.
A simple approach is to convert the template into PHP and run it.
$template = preg_replace('~\{(\w+)\}~', '<?php $this->showVariable(\'$1\'); ?>', $template);
$template = preg_replace('~\{LOOP:(\w+)\}~', '<?php foreach ($this->data[\'$1\'] as $ELEMENT): $this->wrap($ELEMENT); ?>', $template);
$template = preg_replace('~\{ENDLOOP:(\w+)\}~', '<?php $this->unwrap(); endforeach; ?>', $template);
例如,这会将模板标签转换为嵌入式PHP标签.
For example, this converts the template tags to embedded PHP tags.
您将看到我引用了$this->showVariable()
,$this->data
,$this->wrap()
和$this->unwrap()
.这就是我要实现的.
You'll see that I made references to $this->showVariable()
, $this->data
, $this->wrap()
and $this->unwrap()
. That's what I'm going to implement.
showVariable
函数显示变量的内容.每次迭代都会调用wrap
和unwrap
来提供闭包.
The showVariable
function shows the variable's content. wrap
and unwrap
is called on each iteration to provide closure.
这是我的实现方式
class TemplateEngine {
function showVariable($name) {
if (isset($this->data[$name])) {
echo $this->data[$name];
} else {
echo '{' . $name . '}';
}
}
function wrap($element) {
$this->stack[] = $this->data;
foreach ($element as $k => $v) {
$this->data[$k] = $v;
}
}
function unwrap() {
$this->data = array_pop($this->stack);
}
function run() {
ob_start ();
eval (func_get_arg(0));
return ob_get_clean();
}
function process($template, $data) {
$this->data = $data;
$this->stack = array();
$template = str_replace('<', '<?php echo \'<\'; ?>', $template);
$template = preg_replace('~\{(\w+)\}~', '<?php $this->showVariable(\'$1\'); ?>', $template);
$template = preg_replace('~\{LOOP:(\w+)\}~', '<?php foreach ($this->data[\'$1\'] as $ELEMENT): $this->wrap($ELEMENT); ?>', $template);
$template = preg_replace('~\{ENDLOOP:(\w+)\}~', '<?php $this->unwrap(); endforeach; ?>', $template);
$template = '?>' . $template;
return $this->run($template);
}
}
在wrap()
和unwrap()
函数中,我使用堆栈来跟踪变量的当前状态.精确地,wrap($ELEMENT)
将当前数据保存到堆栈中,然后将$ELEMENT
中的变量添加到当前数据中,然后unwrap()
从堆栈中恢复数据.
In wrap()
and unwrap()
function, I use a stack to keep track of current state of variables. Precisely, wrap($ELEMENT)
saves the current data to the stack, and then add the variables inside $ELEMENT
into current data, and unwrap()
restores the data from the stack back.
为了提高安全性,我添加了以下额外内容以用PHP echos替换<
:
For extra security, I added this extra bit to replace <
with PHP echos:
$template = str_replace('<', '<?php echo \'<\'; ?>', $template);
基本上是为了防止直接注入<?
,<%
或<script language="php">
的任何PHP代码.
Basically to prevent any kind of injecting PHP codes directly, either <?
, <%
, or <script language="php">
.
用法是这样的:
$engine = new TemplateEngine();
echo $engine->process($template, $data);
这不是最好的方法,但这是可以完成的一种方法.
This isn't the best method, but it is one way it could be done.
这篇关于PHP循环模板引擎-从头开始的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!