Как сделать собственный шаблонизатор

Рано или поздно у каждого программиста возникает желание написать свой Smarty :) .
Называются разные аргументы - более быстрый, гибкий, удобный и так далее.

После того, как я начал пользоваться CakePHP ко мне неожиданно пришло просветление - это было как раз то, что я хотел. Но иногда возникают задачи, когда действительно нужны простейшие шаблонизаторы и CakePHP становится слишком тяжёлой артиллерией.

И тут все сразу смотрят в сторону eval. Первое, что приходит в голову - написать eval(file_get_contents('template.htm')). И, конечно же, это не работает. Потом читается документация и пишется что-то вроде eval('echo("'.file_get_contents('template.htm').'");');. Но и это тоже не работает.

А потом наконец включается мозг и приходит понимание, что нужно учится у умных людей. Например, тех, кто написал CakePHP :)
Они устанавливают нужные переменные, отключают вывод на экран, инклюдят шаблон, берут вывод из буфера и обрабатывают. Обычно даже это сильно сложно, можно переменные не устанавливать.

Например, чтобы

HTML:
  1. <head><title>{=$title}</title></head>
  2. <body>{=$content}</body>
  3. </html>

превратилось в

HTML:
  1. <head><title>My homepage</title></head>
  2. <body>Under construction</body>
  3. </html>

можно написать такую простенькую функцию.

PHP:
  1. <?php
  2.  
  3. function parsePage($params=array(), $template='main.tpl') {
  4.     ob_start(); // echo to buffer, not screen
  5.     include($template);
  6.     $html = ob_get_clean(); // get buffer contents
  7.    
  8.     foreach ($params as $key=>$value) {
  9.         $html = str_replace('{=$'.$key.'}', $value, $html);
  10.     }
  11.    
  12.     return $html;
  13. }
  14.  
  15. $params = array('title'=>'My homepage', 'content'=>'Under construction');
  16. $html = parsePage($params);
  17. echo($html);
  18.  
  19. ?>

Преимущество в том, что работает очень быстро, кода мало, верстальщикам можно объяснить суть шаблонов за пару минут.

Если вы знаете, как сделать шаблонизатор ещё проще - расскажите.


Понравилось?

  1. Подпишись через RSS
  2. Расскажи о http://php.southpark.com.ua друзьям.
    Все способы хороши: ICQ, E-mail, свой блог, комментарий в чужом блоге или сообщение на форуме
  3. Добавь статью на news2.ru, Хабрахабр или в закладки

Огромное спасибо!

И не стесняйтесь комментировать - у меня стоит плагин, который убирает rel="nofollow" у людей, которые написали больше 5 комментариев.

RSS feed | Trackback URI

26 комментариев »

Comment by Влад
2008-01-28 20:14:57

Однако, велосипед :)

Вот, здесь, например.. Датировано 2003 годом :)

http://spectator.ru/technology/php/easy_templates

2008-01-28 20:27:12

Да, ту статью помню. Кажется, пару лет назад её читал, когда делал свой фреймворк.

Но это не совсем то. Была задача сделать именно с параметрами {=$title}, а не < ?=$title?> (нужна совместимость с шаблонами определённой CMS).
В указанной статье нет упоминания об ob_*, а это важно, так как даёт определённую свободу, если надо делать более сложные шаблоны с вложениями и другой чепухой.
Я сам в большинстве случаев за шаблоны на PHP, но ведь даже в Хабре решили от них отказаться. Что-то знают... :)

 
 
Comment by Danaki
2008-01-28 23:51:02

А вот как Смарти прикрутили не подходит? http://bakery.cakephp.org/articles/view/how-to-use-smarty-with-cake-smartyview

2008-01-29 00:50:04

Идея в том, чтобы создать простейший шаблонизатор, который будет настолько лёгким, насколько можно, потому что непонятно, где он будет работать.
Единственным требованием должно быть только наличие PHP.
А у CakePHP есть неплохие требования по памяти, да ещё и базу данных ему подавай :)

При этом бонусное требование - понятность для верстальщиков, которые не знают, что такое PHP; совместимость с какой-то странной CMS в которой они привыкли работать.
Поэтому Smarty тоже не подходит.

 
 
Comment by Sam Subscribed to comments via email
2008-01-29 13:03:22

Smarty будет работать быстрее с его предкомпиляцией ;)

 
Comment by Revisor
2008-02-14 17:27:51

Так, как на этом сайте принято замечать даже мелкие неточности, то код метода нужно написать так:

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;
}

2008-02-15 17:30:19

ничего себе мелочь - забыл return сделать :shock:
исправил, спасибо

 
 
Comment by Виктор Subscribed to comments via email
2008-02-23 19:02:27

Дан шаблон main.tpl:

{=$var1}
{=$var2}

'hack {=$var2} motherfuck',
'var2' => 'ha-ha'
);
parsePage($vars);

?>

2008-02-23 23:33:44

Конечно же, то, что в include нельзя доверять менять пользователям, только обученым людям.
Тогда, это не баг, а фича :grin:

 
 
Comment by Hokage
2008-03-03 01:53:41

Ошибка:
str_replace('{=$'.$key.'}', $value, $html);
А нужно:
$html = str_replace('{=$'.$key.'}', $value, $html);

2008-03-06 10:08:56

Спасибо. Видимо, нельзя писать в блог в конце рабочего дня - сплошные глюки.

 
 
Comment by Hokage
2008-03-03 16:42:30

Помоему ошибка:
str_replace('{=$'.$key.'}', $value, $html);
А надо:
$html = str_replace('{=$'.$key.'}', $value, $html);

 
Comment by Yaroslav Vorozhko Subscribed to comments via email
2008-03-11 16:15:42

Велосипед... :???:
Так однозначно быстрее
//template bla.phtml

PHP и есть шаблонизатор, причем самый быстрый и самый гибкий из всех написанных . :mrgreen:

 
Comment by Виктор Subscribed to comments via email
2008-03-11 18:00:41

Это холивар, кому как.

 
Comment by Владимир Subscribed to comments via email
2008-04-18 00:40:06

И тут все сразу смотрят в сторону 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;
Даже не нужно включать буфферизацию вывода ))

естественно, это упрощённый вариант. наш код оформлен в целый класс, где есть условия, циклы, компилирование шаблонов :-)

2008-04-21 17:32:34

признаюсь, я очень боюсь конструкций с @ и eval.
стараюсь их использовать только в самом-самом крайнем случае и то если уверен, что сам контролирую то, что эвалится.

 
 
Comment by Сеня Subscribed to comments via email
2008-05-17 06:40:03

.....я конечно извиняюсь...НО....как в этом шаблоне проделывать фокусы с использованием

do{}
While()

или

For()
куда я только не вписывл...и как только не пробывал....что-то не то получалось.
подскажите.....а
Заранее спасибо

2008-05-19 09:53:14

Никак. Это не баг, а фича :)
Если нужно использовать управляющие конструкции PHP, то нужно пользоваться CakePHP и его шаблонами.

 
 
Comment by Jeurey
2008-06-17 03:20:37

Ну зачем сразу чужие шаблоны? Использовать MVC можно и без таких вещей... Если нужен цикл - используем 1 шаблон (который описывает отображение элемента) и оборачиваем во враппер (тобишь - другой шаблон).

И код от шаблона отделен, и функционал реализован ;)

 
Comment by ZuBAR Subscribed to comments via email
2008-11-04 08:49:35

А я пользуюсь своим и он мне нравится
http://maxreplace.zx6.ru/

 
Comment by Форум PHP Subscribed to comments via email
2008-11-27 00:37:27

я предпочитаю парсить через preg_replace с ключем /e в шаблоне:

вот пример кода:

 
Comment by Yury
2009-01-21 10:47:56

Да, нормально. Я тоже так поступаю. Только немного попроще. В нужных местах шаблона ставлю типа %text%, а потом заменяю его на $text, которая подготовлена заранее и в которую можно вставлять все, что угодно, картинки, java и т. д.
Подробнее на http://acvarif.info/wbsphp.html

Только думаю код можно немного упростить.
Зачем это делать?
$html = str_replace('{=$'.$key.'}', $value, $html);
Можно ведь так
$html = str_replace('$'.$key, $value, $html);
И тогда в шаблоне просто
$title
$content

 
Comment by stasik
2009-05-12 18:14:49

Хорошие верстальщики и так без проблем сделают шаблон под ваш код если код будет читаемым... А плохим и нечего делать. Не шаблонизаторами заниматься надо, а тексты писать красивые без экранирования.

 
Comment by Mogz
2009-08-13 10:14:02

Тупо, тупо и еще раз тупо. Так делают только первоклашки.
Вот вопрос. Как вы раелизуете меню (выводящееся на php) через такой "шаблонизатор" :) )
например такого типа:

text 1
text 2
text 3

text 4
text 5
text 6

text 2

хотябы такого плана, причем может быть любым по счету, хоть первым, хоть последним

 
Comment by Elfet Subscribed to comments via email
2009-09-02 21:46:39

Вот мой вариант, возможно он чуточку посложнее, но тоже очень удобен:
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 '';
}
}
}
?>

 
Comment by aisthetes Subscribed to comments via email
2009-09-04 14:18:43

Столкнулся с проблемой, 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');

 
Имя (required)
E-mail (required - never shown publicly)
URL
Текст комментария
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> in your comment.