Как сделать собственный шаблонизатор
Рано или поздно у каждого программиста возникает желание написать свой Smarty
.
Называются разные аргументы - более быстрый, гибкий, удобный и так далее.
После того, как я начал пользоваться CakePHP ко мне неожиданно пришло просветление - это было как раз то, что я хотел. Но иногда возникают задачи, когда действительно нужны простейшие шаблонизаторы и CakePHP становится слишком тяжёлой артиллерией.
И тут все сразу смотрят в сторону eval. Первое, что приходит в голову - написать eval(file_get_contents('template.htm')). И, конечно же, это не работает. Потом читается документация и пишется что-то вроде eval('echo("'.file_get_contents('template.htm').'");');. Но и это тоже не работает.
А потом наконец включается мозг и приходит понимание, что нужно учится у умных людей. Например, тех, кто написал CakePHP ![]()
Они устанавливают нужные переменные, отключают вывод на экран, инклюдят шаблон, берут вывод из буфера и обрабатывают. Обычно даже это сильно сложно, можно переменные не устанавливать.
Например, чтобы
превратилось в
можно написать такую простенькую функцию.
-
<?php
-
-
include($template);
-
-
foreach ($params as $key=>$value) {
-
}
-
-
return $html;
-
}
-
-
$html = parsePage($params);
-
-
?>
Преимущество в том, что работает очень быстро, кода мало, верстальщикам можно объяснить суть шаблонов за пару минут.
Если вы знаете, как сделать шаблонизатор ещё проще - расскажите.
Однако, велосипед
Вот, здесь, например.. Датировано 2003 годом
http://spectator.ru/technology/php/easy_templates
Да, ту статью помню. Кажется, пару лет назад её читал, когда делал свой фреймворк.
Но это не совсем то. Была задача сделать именно с параметрами {=$title}, а не < ?=$title?> (нужна совместимость с шаблонами определённой CMS).
В указанной статье нет упоминания об ob_*, а это важно, так как даёт определённую свободу, если надо делать более сложные шаблоны с вложениями и другой чепухой.
Я сам в большинстве случаев за шаблоны на PHP, но ведь даже в Хабре решили от них отказаться. Что-то знают...
А вот как Смарти прикрутили не подходит? http://bakery.cakephp.org/articles/view/how-to-use-smarty-with-cake-smartyview
Идея в том, чтобы создать простейший шаблонизатор, который будет настолько лёгким, насколько можно, потому что непонятно, где он будет работать.
Единственным требованием должно быть только наличие PHP.
А у CakePHP есть неплохие требования по памяти, да ещё и базу данных ему подавай
При этом бонусное требование - понятность для верстальщиков, которые не знают, что такое PHP; совместимость с какой-то странной CMS в которой они привыкли работать.
Поэтому Smarty тоже не подходит.
Smarty будет работать быстрее с его предкомпиляцией
Так, как на этом сайте принято замечать даже мелкие неточности, то код метода нужно написать так:
function parsePage($params=array(), $template='main.tpl') {
ob_start(); // echo to buffer, not screen
include($template);
$html = ob_get_clean(); // get buffer contents
foreach ($params as $key=>$value) {
$html = str_replace('{=$'.$key.'}', $value, $html);
}
return $html;
}
ничего себе мелочь - забыл return сделать
исправил, спасибо
Дан шаблон main.tpl:
{=$var1}
{=$var2}
'hack {=$var2} motherfuck',
'var2' => 'ha-ha'
);
parsePage($vars);
?>
Конечно же, то, что в include нельзя доверять менять пользователям, только обученым людям.
Тогда, это не баг, а фича
Ошибка:
str_replace('{=$'.$key.'}', $value, $html);
А нужно:
$html = str_replace('{=$'.$key.'}', $value, $html);
Спасибо. Видимо, нельзя писать в блог в конце рабочего дня - сплошные глюки.
Помоему ошибка:
str_replace('{=$'.$key.'}', $value, $html);
А надо:
$html = str_replace('{=$'.$key.'}', $value, $html);
Велосипед...
Так однозначно быстрее
//template bla.phtml
PHP и есть шаблонизатор, причем самый быстрый и самый гибкий из всех написанных .
Это холивар, кому как.
И тут все сразу смотрят в сторону eval. Первое, что приходит в голову - написать eval(file_get_contents('template.htm')). И, конечно же, это не работает. Потом читается документация и пишется что-то вроде eval('echo("'.file_get_contents('template.htm').'");');. Но и это тоже не работает.
Мы в своих шаблонах используем eval, не не та как вы описали:
например есть файл file.tpl:
{$main}
</html
И мы делаем так:
$main = "ou la la
";
$template = @file_get_contents('file.tpl');
$t = '';
@eval("\$t=<<<HTML\n{$template}\nHTML\n;");
Потом, в нужном месте делаем: echo $t;
Даже не нужно включать буфферизацию вывода ))
естественно, это упрощённый вариант. наш код оформлен в целый класс, где есть условия, циклы, компилирование шаблонов
признаюсь, я очень боюсь конструкций с @ и eval.
стараюсь их использовать только в самом-самом крайнем случае и то если уверен, что сам контролирую то, что эвалится.
.....я конечно извиняюсь...НО....как в этом шаблоне проделывать фокусы с использованием
do{}
While()
или
For()
куда я только не вписывл...и как только не пробывал....что-то не то получалось.
подскажите.....а
Заранее спасибо
Никак. Это не баг, а фича
Если нужно использовать управляющие конструкции PHP, то нужно пользоваться CakePHP и его шаблонами.
Ну зачем сразу чужие шаблоны? Использовать MVC можно и без таких вещей... Если нужен цикл - используем 1 шаблон (который описывает отображение элемента) и оборачиваем во враппер (тобишь - другой шаблон).
И код от шаблона отделен, и функционал реализован
А я пользуюсь своим и он мне нравится
http://maxreplace.zx6.ru/
я предпочитаю парсить через preg_replace с ключем /e в шаблоне:
вот пример кода:
Да, нормально. Я тоже так поступаю. Только немного попроще. В нужных местах шаблона ставлю типа %text%, а потом заменяю его на $text, которая подготовлена заранее и в которую можно вставлять все, что угодно, картинки, java и т. д.
Подробнее на http://acvarif.info/wbsphp.html
Только думаю код можно немного упростить.
Зачем это делать?
$html = str_replace('{=$'.$key.'}', $value, $html);
Можно ведь так
$html = str_replace('$'.$key, $value, $html);
И тогда в шаблоне просто
$title
$content
Хорошие верстальщики и так без проблем сделают шаблон под ваш код если код будет читаемым... А плохим и нечего делать. Не шаблонизаторами заниматься надо, а тексты писать красивые без экранирования.
Тупо, тупо и еще раз тупо. Так делают только первоклашки.
)
Вот вопрос. Как вы раелизуете меню (выводящееся на php) через такой "шаблонизатор"
например такого типа:
text 1
text 2
text 3
text 4
text 5
text 6
text 2
хотябы такого плана, причем может быть любым по счету, хоть первым, хоть последним
Вот мой вариант, возможно он чуточку посложнее, но тоже очень удобен:
vars = $_vars;
$this->file = $_file;
}
public static function SetViewPath($path)
{
self::$view_include_path = $path;
}
public function SetVars($_vars)
{
$this->vars += $_vars;
}
public function Get()
{
$return = '';
$include = self::$view_include_path . $this->file . '.php';
if( file_exists($include) )
{
ob_start();
foreach ( $this->vars as $key => $value )
{
$$key = $value;
}
include ($include);
$return = ob_get_contents();
ob_end_clean();
}
else
{
throw new Exception("No view file: $include");
}
return $return;
}
public static function Call($skin, $vars = array())
{
$include = self::$view_include_path . $skin . '.php';
if( file_exists($include) )
{
$view = new self($skin);
$view->SetVars($vars);
return $view->Get();
}
else
{
return '';
}
}
}
?>
Столкнулся с проблемой, data($content) выводит результат не там где нужно: {content}, а нужно {content}.
function data($c){
for($i = 0; $i $c[$i][date],
'name' => $c[$i][name],
'msg' => $c[$i][msg]), 'msg.tpl');
}
}
$s = mysql_query("
select `date`, `uid`, `text`
from `msg`") or die(mysql_error());
while($p = mysql_fetch_array($s)){
$content[] = array(
'date' => "$p[date]",
'name' => "$p[uid]",
'msg' => "$p[text]");
}
print tmpl(array(
'title'=>'title',
'content'=> data($content)), 'main.tpl');