Циклический движок Шаблонов PHP - С Нуля
Для группового проекта я пытаюсь создать механизм шаблонов для PHP, чтобы люди, менее опытные в языке, могли использовать теги, такие как {name}, в своем HTML, и PHP заменит этот тег предопределенной переменной из массива. А также поддерживающие петли.
Это намного превосходит ожидания проекта, но, поскольку у меня есть опыт работы с PHP, я подумал, что это будет хорошей задачей, чтобы занять меня!
Мои основные вопросы заключаются в том, как мне выполнить часть цикла синтаксический анализатор и является ли это лучшим способом реализации такой системы. Прежде чем вы просто порекомендуете существующую систему шаблонов, я бы предпочел создать ее сам для получения опыта и потому, что все в нашем проекте должно быть нашим собственным.
На данный момент основной синтаксический анализ выполняется с помощью регулярного выражения и preg_replace_callback, он проверяет, существует ли $data[имя], и если да, заменяет его.
Я пытался выполнить цикл различными способами, но не уверен, что нахожусь на правильном пути!
Ан пример, если были предоставлены данные для механизма синтаксического анализа:
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 Похоже, что есть огромное распространение мнений по этой теме, пожалуйста, не голосуйте против людей, потому что у них другое мнение по отношению к вам. Каждый имеет право на свое собственное!
6 answers
Простой подход состоит в том, чтобы преобразовать шаблон в 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);
Это не лучший метод, но это один из способов, которым это можно было бы сделать.
Хорошо, сначала позвольте мне кое-что объяснить, скажу вам, что 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;
}
}
Это чрезвычайно простой материал, который вы могли бы расширить, так что о области, Это в основном класс, в котором ваши представления компилируются в интерпретаторе, это позволит вам иметь доступ к методам в классе TemplateScope, но не за пределами класса области, т.Е. имени.
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; // :)
}
}
Это только основное и не работает, но концепция есть, вот пример использования:
$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
, который хранит предварительно скомпилированный исходный код в памяти, пропуская большую часть времени компиляции/интерпретации
Если я не беспокоюсь о кэшировании или других продвинутых темах, которые подтолкнули бы меня к такому устоявшемуся движку шаблонов, как smarty, я нахожу, что PHP сам по себе является отличным движком шаблонов. Просто установите переменные в сценарии, как обычно, а затем включите файл шаблона
$name = 'Eric';
$locations = array('Germany', 'Panama', 'China');
include('templates/main.template.php');
Main.tempate.php использует альтернативный синтаксис тегов php, который довольно легко использовать людям, не работающим на 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>
У меня был очень простой ответ на что-то вроде этого еще до того, как я начал использовать DOMXPath.
Класс что-то вроде этого (не уверен, что он работает так, как вы хотите, но пища для размышлений, так как он работает очень просто
<?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 для размещения всех ваших данных.
Простые циклы после создания объекта, вероятно, могут достичь вашей цели.
Хорошо удача
Умный:)...
Php:
$smarty->assign("people",$peopleArray)
Умный шаблон:
{foreach $people as $person}
<b>{$person.name}</b> {$person.surname}<br />
{/foreach}
Пара других вещей, которые нужно сделать, но это то, на что по сути будет похож smarty.