对于一个组项目,我正在尝试为PHP创建一个模板引擎,对于那些语言不太熟练的人可以在他们的HTML中使用{name}之类的标签,而PHP将使用数组中的预定义变量替换该标签。以及支持循环。
这远远超出了项目的期望,但由于我有使用PHP的经验,我认为让我忙碌将是一个很好的挑战!
我的主要问题是,如何执行解析器的循环部分,这是实现此类系统的最佳方法。在您推荐现有的模板系统之前,我宁愿自己创建它以获得经验,因为我们项目中的所有内容都必须是我们自己的。
目前使用regex和preg_replace_callback进行基本解析时,它会检查$ data [name]是否存在以及是否替换它。
我试图以各种不同的方式进行循环,但我不确定我是否在正确的轨道上!
如果给出解析引擎的数据是一个例子:
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
)
它正在解析的页面是这样的:
<html>
<title>{title}</title>
<body>
<h1>{subtitle}</h1>
{LOOP:people}
<b>{name}</b> {surname}<br />
{ENDLOOP:people}
<br /><br />
<i>{footer}</i>
</body>
</html>
它会产生类似于:
的东西<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>
你的时间非常值得赞赏!
非常感谢,
P.S。我完全不同意这一点,因为我希望创建类似于已经存在的经验的东西,我的格式良好且易于理解的问题得到了投票。
P.p.s对于这个话题似乎有大量意见,请不要对人们投票,因为他们对你有不同的看法。每个人都有权获得自己的权利!
答案 0 :(得分:22)
一种简单的方法是将模板转换为PHP并运行它。
$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标记。
您会看到我引用了$this->showVariable()
,$this->data
,$this->wrap()
和$this->unwrap()
。这就是我要实施的目标。
showVariable
函数显示变量的内容。每次迭代都会调用wrap
和unwrap
来提供闭包。
这是我的实施:
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()
将数据从堆栈中恢复。
为了额外的安全性,我添加了这个额外的位来用PHP echos替换<
:
$template = str_replace('<', '<?php echo \'<\'; ?>', $template);
基本上是为了防止任何类型的PHP代码直接注入,<?
,<%
或<script language="php">
。
用法是这样的:
$engine = new TemplateEngine();
echo $engine->process($template, $data);
这不是最好的方法,但它是一种可行的方法。
答案 1 :(得分:5)
好的,首先让我解释一下,告诉你 PHP是模板参与者。
做你正在做的就像从模板解析器创建一个模板解析器一样毫无意义,并且坦率地说它让我认为模板解析器如smarty在无意义的任务中变得非常好。
你应该做的是创建一个模板助手,而不是一个多余的解析器,在编程方面,模板文件被称为视图,这是他们获得特定的原因之一名字是人们会在那里与模型,域逻辑等分开知道
您应该做的是找到一种方法将所有视图数据封装在您的视图中。
这方面的一个例子是使用2个类
模板类的功能是域逻辑将数据设置到视图并进行处理。
这是一个简单的例子:
class Template
{
private $_tpl_data = array();
public function __set($key,$data)
{
$this->_tpl_data[$key] = $data;
}
public function display($template,$display = true)
{
$Scope = new TemplateScope($template,$this->_tpl_data); //Inject into the view
if($display === true)
{
$Scope->Display();
exit;
}
return $Scope;
}
}
这是极端基本的东西,你可以扩展,oko所以关于Scope,这基本上是你的视图在解释器中编译的类,这将允许你访问TemplateScope类中的方法但不在范围之外class,即名称。
class TemplateScope
{
private $__data = array();
private $compiled;
public function __construct($template,$data)
{
$this->__data = $data;
if(file_exists($template))
{
ob_start();
require_once $template;
$this->compiled = ob_get_contents();
ob_end_clean();
}
}
public function __get($key)
{
return isset($this->__data[$key]) ? $this->__data[$key] : null;
}
public function _Display()
{
if($this->compiled !== null)
{
return $this->compiled;
}
}
public function bold($string)
{
return sprintf("<strong>%s</strong>",$string);
}
public function _include($file)
{
require_once $file; // :)
}
}
这只是基本的,不起作用,但概念就在那里,Heres是一个用法示例:
$Template = new Template();
$Template->number = 1;
$Template->strings = "Hello World";
$Template->arrays = array(1,2,3,4)
$Template->resource = mysql_query("SELECT 1");
$Template->objects = new stdClass();
$Template->objects->depth - new stdClass();
$Template->display("index.php");
在模板中你可以使用传统的php:
<?php $this->_include("header.php") ?>
<ul>
<?php foreach($this->arrays as $a): ?>
<li><?php echo $this->bold($a) ?></li>
<?php endforeach; ?>
</ul>
这也允许您在仍具有$this
关键字访问权限的模板中包含然后包含自己,包括递归(但不是)。
然后,不要以编程方式创建缓存,因为没有什么要缓存的,你应该使用memcached
在内存中存储前编译的源代码,跳过很大一部分编译/解释时间
答案 2 :(得分:1)
如果我不担心缓存或其他高级主题会让我进入像smarty这样的既定模板引擎,我发现PHP本身就是一个很棒的模板引擎。只需像正常一样在脚本中设置变量,然后包含模板文件
$name = 'Eric';
$locations = array('Germany', 'Panama', 'China');
include('templates/main.template.php');
main.tempate.php使用alternative php tag syntax非常容易让非php人使用,只是告诉他们忽略包含在php标签中的任何内容:)
<h2>Your name is <?php echo $name; ?></h2>
<?php if(!empty($locations)): ?>
<ol>
<?php foreach($locations as $location): ?>
<li><?php echo $location; ?></li>
<?php endforeach; ?>
</ol>
<?php endif; ?>
<p> ... page continues ... </p>
答案 3 :(得分:1)
在我开始使用DOMXPath之前,我对KINDA这样的东西有一个非常基本的答案。
课是这样的(不确定它是否像你想要的那样工作但是很有思想,因为它非常简单
<?php
class template{
private $template;
function __CONSTRUCT($template)
{
//load a template
$this->template = file_get_contents($template);
}
function __DESTRUCT()
{
//echo it on object destruction
echo $this->template;
}
function set($element,$data)
{
//replace the element formatted however you like with whatever data
$this->template = str_replace("[".$element."]",$data,$this->template);
}
}
?>
使用此类,您只需使用您想要的任何模板创建对象,并使用set函数放置所有数据。
创建对象后的简单循环可能会实现您的目标。
祝你好运答案 4 :(得分:-4)
Smarty :) ......
PHP:
$smarty->assign("people",$peopleArray)
smarty模板:
{foreach $people as $person}
<b>{$person.name}</b> {$person.surname}<br />
{/foreach}
要做其他事情要做,但这就是聪明的事情。
答案 5 :(得分:-11)
使用Smarty。