Шаблонизатор на PHP
Меня многократно просили дать код шаблонизатора на PHP, которым я пользуюсь. Но прежде чем перейти к этому коду, давайте немного расскажу о наиболее популярном шаблонизаторе - Smarty. Сразу скажу, что я всегда был против всех этих чужих и огромных библиотек. Но Smarty я стерпел и некоторое время использовал, а потом я подумал: "А зачем мне нужна эта помойка, если от шаблонизатора мне нужен минимум функциональности?".
Действительно, а что вообще нужно от шаблонизатора? От него нужно лишь подставлять в нужные места tpl-файлов определённые переменные из PHP. Все говорят, что PHP-код в tpl-файлах - это плохо. А чем лучше Smarty-код в tpl-файлах? Да абсолютно ничем, и это даже хуже, поскольку этот Smarty-код потом преобразуется в PHP-код и выполняется.
Smarty уже так сильно разошёлся, что стал вообще отдельным языком со своим синтаксисом, со своими циклами и условиями. Разве это требуется от шаблонизатора?
Задача шаблонизатора - только подставлять определённые значения в определённые места, а все циклы я могу написать и на PHP, и промежуточный язык для этого не требуется.
Итак, привожу код своего шаблонизатора, которым я настоятельно рекомендую Вам пользоваться:
<?php
class Template {
private $dir_tmpl; // Директория с tpl-файлами
private $data = array(); // Данные для вывода
public function __construct($dir_tmpl) {
$this->dir_tmpl = $dir_tmpl;
}
/* Метод для добавления новых значений в данные для вывода */
public function set($name, $value) {
$this->data[$name] = $value;
}
/* Метод для удаления значений из данных для вывода */
public function delete($name) {
unset($this->data[$name]);
}
/* При обращении, например, к $this->title будет выводиться $this->data["title"] */
public function __get($name) {
if (isset($this->data[$name])) return $this->data[$name];
return "";
}
/* Вывод tpl-файла, в который подставляются все данные для вывода */
public function display($template) {
$template = $this->dir_tmpl.$template.".tpl";
ob_start();
include ($template);
echo ob_get_clean();
}
}
?>
Всего 1 файл размером 0.5 КБ, вместо больше 100 файлов и примерно 1 МБ (в 2000 раз больше). Но при этом всё самое важное данный класс сделает.
Теперь создадим tpl-файл (пусть называется menu.tpl), который будет без проблем обработан этим шаблонизатором:
<ul>
<?php foreach ($this->menu as $link => $name) { ?>
<li>
<a href="<?=$link?>"><?=$name?></a>
</li>
<?php } ?>
</ul>
И, наконец, давайте напишем PHP-файл, который будет вызывать шаблонизатор:
<?php
$template = new Template("tmpl/");
$menu = array();
$menu["http://site.ru"] = "Главная";
$menu["http://site.ru/page-1.html"] = "Страница 1";
$menu["http://site.ru/page-2.html"] = "Страница 2";
$template->set("menu", $menu);
$template->display("menu");
?>
Если бы мы использовали Smarty, то у нас было бы всё ровно так же, но в tpl-файле мы бы написали код Smarty, который потом бы заменялся на PHP и выполнялся. А мы же сразу написали этот PHP-код. И какой смысл учить отдельный язык, который будет лишь тормозить работу, а также требует наличие огромной библиотеки?
Надеюсь, данная статья Вам покажет, что не нужно гнаться за бессмысленной функциональностью Smarty. Всё это в гораздо большем объёме есть в PHP, а задача шаблонизатора всего лишь одна - подставлять в tpl-файлы значения, полученные из PHP. Всё.
-
- Михаил Русаков
Комментарии (73):
Там в цикле foreach ($this->menu as $name => $link) ключ и значение поменять местами нужно)
Ответить
Спасибо, подправил!
Ответить
Не получается передать переменную из .php в .tpl файл. Обращался в вашу тех поддержку (давно уже) сказали подождать - все еще жду. Может объясните как это сделать и можно ли обойтись без массива ?
Ответить
Задайте свой вопрос снова в службу поддержки Возможно он затерялся
Ответить
А для чего вообще этот шаблонизатор? Можно чуточку поподробнее об этом для чайников? :)
Ответить
Для движков он нужен, чтобы не смешивать лишний раз HTML и PHP.
Ответить
Очень полезная штука. С помощью такой технологии вполне можно создать собственную cms, не чем не уступающую ныне известным, но она будет намного быстрее, намного меньше весить, к тому же вы сможете реализовать лишь те функции, которые вам нужны и сделать абсолютно любой уникальный дизайн. Спасибо Михаил за вашу помощь!
Ответить
А как сделать чтобы автоматом если есть в файле базовые переменные, например {address} заменялись на переменные из конфиг файла?
Ответить
В конструкторе Template сразу через метод set() добавить все эти всегда присутствующие конструкции.
Ответить
Но ведб в классе нету метода для замены чего-либо, а мне нужно просто заменить... Расскажите подробнее пожалуйста
Ответить
Метод set фактически и подготавливает набор данных для будущей подстановки в шаблоне.
Ответить
Ваш класс конечно хороший, но мне кажется что этот немного лучше http://malinichev.ru/lesson/php-html-template.html как вы считаете?
Ответить
Михаил, я всё таки решил испробовать ваш класс в деле, и попал на неожиданную проблему, как мне выводить переменные в шаблоне, если массив у меня двухмерный, при выводе вашего массива menu выходит нечнто такое: Array ( [http://site.ru] => Главная [http://site.ru/page-1.html] => Страница 1 [http://site.ru/page-2.html] => Страница 2 ), а при выводе моего массива выходит такое: Array ( [0] => Array ( [id] => 1 [username] => Владислав Малиничев ) ) Как поступить в такой ситуации, шаблонизатор использую в паре с вашим классом для бд.
Ответить
Всё разобрался, даже когда цикл по логике не нужен, через него всё равно нужно прогонять в шаблоне... Вы бы это сразу объяснили, а то голову ломал 4 часа. Ну вы сами подумайте, зачем нужен цикл, когда я вывожу только данные одной таблицы где поле равно числу, там ведь не нужен цикл, я прав? Или это у меня уже мозги плывут в нехорошую сторону?
Ответить
Для вывода массива нужен цикл, по-другому не бывает.
Ответить
Теперь буду знать
Ответить
Спасибо, отличный вариант. Но вот хотелось бы услышать, как автор в подобном варианте решил бы вопрос с расставлением позиций блоков
Ответить
Я бы тоже хотел услышать... Даже если взять пример блока только для авторизованных, вот как его сделать?
Ответить
Через if в tpl-файле. И если авторизован, то вывести, иначе нет.
Ответить
Подскажите, пжлста, у Вас в menu.tpl <?=$link?> и <?=$name?> - это синтаксис такой, или указатели для str_replace()? Просто еще не сталкивался с таким выражением
Ответить
<?=$link?> аналогично записи <?php echo $link;?>, просто первый вариант короче.
Ответить
Вот оно что! Там еще short_open_tag надо было включить, поэтому у меня и не получалось. Спасибо!
Ответить
Простите, но это такого как раз и был введен альтернативный синтаксис <ul> <?php foreach ($this->menu as $link => $name) { ?> <li> <a href="<?=$link?>"><?=$name?></a> </li> <?php } ?> </ul> if ($a == 5): echo "a равно 5"; echo "..."; elseif ($a == 6): echo "a равно 6"; echo "!!!"; else: echo "a не равно ни 5 ни 6"; endif;
Ответить
Ещё бы класс для валидации всех данных с GET и POST и был бы полный набор...
Ответить
Здравствуйте, Михаил! Подскажите пожалуйста, как с помощью Вашего шаблонизатора вывести многоуровневое горизонтальное меню из базы данных. --Структура таблицы `menu` CREATE TABLE IF NOT EXISTS `menu` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `parent_id` int(11) DEFAULT NULL, `title` varchar(255) DEFAULT NULL, `link` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; Буду очень признателен за помощь!
Ответить
Многоуровневое меню делается с помощью множества ul, внутри которых вложены другие ul. Нужно создать отдельный tpl-файл для одноуровневого меню, а внутри цикла в этом файле сделать подключение этого же файла, если требуется создать новый уровень, если не требуется, то просто вывести пункт меню. Таким образом, будет своего рода рекурсия, только тут не функция вызывается, а файл выводится. В основе это, но, конечно, придётся ещё поработать здесь над алгоритмом и над видом данных с меню.
Ответить
Я так понимаю в подобных шаблонизаторах нельзя использовать циклы и if,else? Чтоб вывести в цикле,то сам цикл помещаем в пхп в переменную, а потом уже выводим,эта переменная потом выглядит допустим так $content='цикл' , а потом $content заменяем на {content} или <?=$content?> в шаблоне? Т.е. частично html всеравно будет присутствовать в файле обработчике? По другому только smarty и т.п. или дорабатывать этот шаблонизатор?
Ответить
Здесь как раз и можно использовать if и циклы. <?php if () { ?>тут html-код<?php } ?>
Ответить
А как вызвать файл шаблона.Допустим у меня папка template,в ней фаил header.tpl У вас я так понимаю вызов тут $template = new Template("tmpl/"); я указал путь $template = new Template("template/header.tpl"); но так не работает
Ответить
И что за переменная $link в этом коде <?php foreach ($this->menu as $link => $name) { ?> ? в ней я так понял содержится эта часть ["http://site.ru"],а как быть если у меня несколько переменных? ....О вроде с директориями разобрался,и в цикле вывелось что нужно,но не до конца,данные из бд не вывелись,я так понимаю переменные пустые
Ответить
Можете подсказать что не так,почему не выводятся данные вот пхп $template = new Template("template/"); $top = array(); $top['/'.$categoryUrl.'/'.$myrow77['url']] = $url; $top[$myrow['title']] = $title; $template->set("header", $top); $template->display("header"); а вот header.tpl <?php foreach ($this->header as $top => $name) { ?> <li> <div class="top-text"><a href="<?=$url?>"><?=$title?></a></div></li> <?php } ?> Но все переменные пустые,не могу понять в чем причина.
Ответить
http://myrusakov.ru/php-finderror.html
Ответить
Файл шаблона надо подключить через <?php require_once "header.tpl"; ?> - и это непосредственно в main.tpl
Ответить
Да причем тут поиск ошибок.У вас в примере простейший пример foreach ($this->menu as $link => $name) Получается в цикле используется только одна переменная,а как мне вывести к примеру 4 переменных? У меня допустим есть переменные $img,$title,$url,$text. Как их вывести через цикл foreach? При этом в цикле переменные могут использоваться по несколько раз.
Ответить
Так и выводите: <?=$title?> И не важно где, хоть в цикле, хоть нет.
Ответить
В том то и дело,что так в цикле не получается вывести.Уже второй день пробую,читаю про циклы.А результата нет.Чтоб понятней было на форуме тогда покажу как делаю.
Ответить
Есть такой вопрос тогда) Есть допустим шаблон с именем index.tpl, а мне необходимо в этот шаблон вставить еще один - например шаблон с формой регистрации registration_form.tpl Как мне тогда поступить используя ваш шаблонизатор? Заранее спасибо!
Ответить
Сначала собираете целый шаблон, затем заменяете значения шаблонизатором.
Ответить
А что если файл registration_form.tpl прочитать в строку и записать ее в переменную, а затем эту переменную присоединить к выводимой информации в нужном месте?
Ответить
Можно и так.
Ответить
Объясните пожалуйста чем данный подход хуже ----- php-файл------ <?php $menu = array(); $menu["http://site.ru"] = "Главная"; include "menu.tpl"; ?> ----- файл menu.tpl------ <ul> <?php foreach ($menu as $link => $name) { ?> <li> <a href="<?=$link?>"><?=$name?></a> </li> <?php } ?> </ul> ---------------- заранее спасибо
Ответить
В такой структуре проще запутаться, если у Вас много всего выводится.
Ответить
Не поленился зарегистрироватся чтобы поинтересоватся, зачем вы вывод буферизируете?
Ответить
Решился Ваш шаблонизатор у себя использовать для организации вывода разных версий сайта, но так как и у многих других здесь возникли проблемы. Не могу понять как выводить переменные в шаблон. Допустим таким образом в php файле: $index = array('time' => $time); $tpl->set('index', $index); $tpl->display('index'); а в tpl получается выводится только таким образом: $this->index['time'] Это через чур много, можно как-то покороче? Таким образом <?=$time?> не работает
Ответить
В старых версиях PHP так и было, а в новых только через $this.
Ответить
Спасибо за ответ, жаль что так, а исправить это если только дополнительную функцию дописать для замены? или ещё какие-либо варианты возможны?
Ответить
Только лишь вначале tpl файла сделать из переменных класса обычные локальные переменные, перебрав через цикл всё, что передано в шаблон.
Ответить
Еще раз спасибо, думаю это уже будет излишняя и не обоснованная операция. Пока оставлю как есть
Ответить
Как можно очистить значения? Пробывал unset,не помогает,вроде в вашем классе есть функция для удаления значений. Но с ней тоже ничего не получилось, ну или я не так что то делал. Вообщем получается следующее, допустим вывожу картинку $template->set('img', ''.$row['img'].''); В шаблоне <?=$this->img?>, либо <?=$this->data['img']?>, без разницы(но первый варинт удобней,он короче). Вообщем больше одного раза я не могу использовать метку 'img'. Если я ее проставлю к примеру дважды в разных местах шаблона то тогда выведется одна и таже картинка. Чтоб все выводилось как нужно,надо в др. месте например делать метку img2 , $template->set('img2', ''.$row['img'].'');....Ну и так со всеми метками. В итоге у меня в шаблонах куча этих разных меток и как то неудобно получается, да и не красиво. С циклами тоже самое <?php foreach ($this->menu as $value){ ?> Далее <?=$value['url']?> и т.д. ,но тут попроще нужно только чтоб $this->menu было разное т.е. $this->menu2, $this->menu3 и .т.п. Думаю понтяно обьяснил? Сори если что то непонятно. Вообщем можно как то сделать чтоб можно было использовать одни и теже метки в шаблонах, а не присваивать постоянно разные имена?
Ответить
О что то получилось,только не так как хотел. Загнал в массив потом через foreach прогнал,и в шаблоне можно использовать <?=$this->info[img]?>. Хотя б так,правда метку info можно использовать только один раз. Но все же так по лучше будет.
Ответить
ob_start(); include ($template); echo ob_get_clean(); Посему не просто include ($template); ?
Ответить
Потому что не всегда требуется выводить, иногда может потребоваться вернуть строковую переменную с содержимым буфера.
Ответить
В том то и дело, что строка echo ob_get_clean(); ВЫВЕДЕТ буфер после чего ОЧИСТИТ его. Тогда какой смысл в этой буферизации, ведь если вдруг потребуется вернуть шаблон с осуществленной подстановкой, то только внеся изменения в сам метод display. Или я что-то не так понял?
Ответить
Если добавить параметр $ret (по умолчанию, можно сделать false) в метод, то тогда можно делать так: if ($ret) return ob_get_clean(); else echo ob_get_clean();
Ответить
А если, предположим мне нужно вывести имя пользователя, причем не используя php код в шаблоне,а используя метку {user}. Мои действие следующие: в функции public function display($template)я пишу foreach($this->data as $key => $value) { $this->template = str_replace($key, $value, $this->template); } echo $this->template; А потом в index.php $template->set('{name}', 'Sergey'); По логике так или нет? Просто я так попробовал, у меня ничего не вышло. Хотелось бы узнать решение.
Ответить
Покажите код.
Ответить
А что делать если у тебя в пхп коде есть такие методы while(), case, swich() и тд, как быть в этом случае с вашим шаблонизатором?
Ответить
Не понял если честно, в чем проблема?
Ответить
подскажите пожалуста ,как выводить результат данного кода на странице
Ответить
какой код выводить?
Ответить
я сайт с урока ООП русакова скопипастип,а толку нету,обратно перешел на упрощенный режим,с этого примера попробую внедрить ООП
Ответить
Оно конечно проще и эстетичней... чем смарти, а как же замечательная фишка - кеширование? То есть в любом случае данный код будет обрабатываться, а не брать уже готовый файл на отдачу из кэша. При чем, я в смарти настроил кеширование и компилирование шаблонов в ram-диск, благодаря чему - скорость вывода данных значительно возрасла! Вот рецепт http://yurecnt.ru/Pro_Smarty_-_zastavlyaem_sayt_letat_esche_byistree_.html
Ответить
можливо для виводу потрібно усі параметри прогнати через extract() у буфері, в мене усе працює, ось: public function display($template) { $template = $this->dir_tmpl.$template.".tpl"; ob_start(); extract($this->data); include ($template); echo ob_get_clean(); }
Ответить
В функции display($template) используется вызов ob_start(); что это и откуда???
Ответить
http://php.net/manual/ru/function.ob-start.php почитайте тут.
Ответить
В класс наверное стоит добавить еще один метод? /*для осуществления проверок типа isset($this->title) */ public function __isset($name) { if (isset($this->data[$name])) return $this->data[$name]; return ""; } тк в шаблоне не срабатывает isset($this->....)
Ответить
А как, в приведенном классе, сделать автоматический искейпинг данных. То есть, чтобы на выводимую переменную автоматически накатывалась htmlspecialchars?
Ответить
Здравствуйте, Михаил! Решил спросить на счёт шаблонизатора. Я не совсем понял, зачем здесь вот эти методы: /* Метод для добавления новых значений в данные для вывода */ public function set($name, $value) { $this->data[$name] = $value; } /* Метод для удаления значений из данных для вывода */ public function delete($name) { unset($this->data[$name]); } И ещё. В вашем шаблонизаторе показан пример где в html коде подставляются переменные, а не вот эти элементы: <ul> <?php foreach ($this->menu as $link => $name) { ?> <li> <a href="{link}">{name}</a> </li> <?php } ?> </ul> Как нужно изменить шаблонизатор, чтобы подставлялись только элементы: {name}, например?
Ответить
уже 8 дней занимаюсь над созданием своего движка. Нет денег чтобы купить платные курсы. А бесплатные вот такие вот... то есть я с вами полностю согласен
Ответить
из бесплатных где все "нормально" описано?
Ответить
Alex_daev, а вы почитайте эту статью: http://myrusakov.ru/php-use-tpl.html Кстати, я этот пример пробовал на практике и мне очень понравилось, настолько всё просто. И ещё... За 8 дней вряд ли вы создадите свой движок.
Ответить
Спасибо. На Вашем сайте много полезной информации. Что касается шаблонизатора, на мой взгляд лучше все действия с шаблоном и его наполнением совершать в буфере, тобиш в памяти и выдавать клиенту готовый результат как содержимое всего одной переменной, типа так http://acvarif.info/wbsphp/movephp/tempphp.html или так http://acvarif.info/progjava/progjava5.html Это уже на Java но суть та же. Хтелось-бы узнать Ваше мнение по этому поводу?
Ответить
Здравствуйте Михаил, но я видал такой же код на https://nabiullin.com/php/php-osnovy/shablonizator-na-php
Ответить
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.