Шаблонизатор как правило применяется там, где требуется разделить работу программиста и верстальщика. Программист подготавливает данные для вывода, а верстальщик делает разметку в привычном HTML, без оглядки на программый код. Раньше, когда сайты были не такими интерактивными и «умными», подход неплохо работал. Со временем логика вывода усложнялась и это приводило к усложению самого шаблонизатора и добавлению новых команд. В итоге некоторые шаблонизаторы имеют настолько сложный и запутанный синтаксис, что проще выполнить логику на чистом PHP.
На текущий момент при создании PHP+HTML кода используется три основных подхода.
- PHP - сам по себе неплохой шаблонизатор. Поэтому за базу берется HTML-код с PHP-вставками в виде echo или короткого синтаксиса.
- Всё есть PHP. HTML выводится в виде строк череp echo. Как вариант - вывод через специальные php-функции.
- Отдельный шаблонизор - подготавливаются данные, а сам вывод делается в отдельном tpl-файле.
Есть ещё подход, где нет никакой системы, а вариант вывода определяется по ситуации. Мы его рассматривать не будем, хотя он, насколько я понимаю, и преобладает в вебстроительстве.
Возьмем для рассмотрения простой пример вывода div-блока с текстом.
HTML с PHP-вкраплениями.
<div class="text"><?= $text ?></div>
Код читается достаточно легко и нет большой сложности поменять html-вёрстку. Если же внутри div-блока будет располагаться объёмный и более сложный php-код, то обычно делают так:
<div class="text"><?php ... тут какой-то php-код ... ?></div>
Читабельность несколько ниже по той причине, что объем PHP превышает HTML. Логичней поступить наоборот - за основу взять PHP:
<?php echo '<div class="text">'; ... тут какой-то php-код ... echo '</div>'; ?>
Читабельность не идеальная, но явно лучше предыдущего варианта. Хотя бы уже потому что код написан в одном php-стиле и не требуется искать html-вкрапления.
Если же php-код в блоке оказывается очень большим, то визуально можно «потерять» парные div'ы в коде. Думаю, что это вообще основная проблема написания кода в этом варианте. Лично у меня вообше привычка вначале делать код для div-блока, а php уже между ними вставлять. Но всё-равно вероятность ошибки возрастает многократно, особенно, если требуется вывести вложенные html-блоки.
В этом случае наступает очередь шаблонизатора. Шаблонизатор может быть основан на HTML или PHP-синтаксисе.
В HTML-варианте вначале потребуется подготовить данные и подставить их в специальный шаблон вывода. В prelatest 0.755 MaxSite CMS уже реализован такой шаблонизатор-парсер и я покажу как его использовать.
За основу берется stock-файл page-out.php. Он находится в default-шаблоне, поэтому нужно его скопировать в свой шаблон и подключить в custom/my_functions.php
require_once(getinfo('template_dir') . 'stock/page-out/page-out.php');
Или, если вы не хотите копировать файл, то подключить сразу из default-шаблона:
require_once(getinfo('templates_dir') . 'default/stock/page-out/page-out.php');
Теперь Page_out будет доступен в любой части сайта. Использование парсера будет основано на двух файлах. Первый - основной, например top.php
<?php // новый объект Page_out $p = new Page_out; // данные для шаблона заносятся в массив $data = array( 'name_site' => getinfo('name_site'); 'description_site' => getinfo('description_site'), ); // сам шаблон вывода в файле top.tpl $p->parse_f('top.tpl', $data);
То есть здесь готовим массив $data с данными для шаблона вывода. Сам же html находится в файле top.tpl
<div class="top"><div class="wrap"> <div class="name_site">{name_site}</div> <div class="description_site">{description_site}</div> </div></div>
Здесь «переменные» заключены в фигурные скобки.
Данный шаблонизатор основан на стандартном парсере CodeIgniter и одним из его достоинств является высокое быстродействие. Правда и функциональность у него достаточно низкая.
Технически мы можем не разбивать вывод на два файла, а воспользоваться т.н. heredoc-синтаксисом PHP. В этом случае html-шаблон «загоняем» в отдельную строковую переменную.
<?php // новый объект Page_out $p = new Page_out; // данные для шаблона заносятся в массив $data = array( 'name_site' => getinfo('name_site'); 'description_site' => getinfo('description_site'), ); $tpl = <<<EOF <div class="top"><div class="wrap"> <div class="name_site">{name_site}</div> <div class="description_site">{description_site}</div> </div></div> EOF; // вывод $p->parse($tpl, $data);
Подход с парсером-шаблонизатором хорош там, где используется фиксированная html-структура, и более-менее стабильные «php-данные». На практике встречаются куда более сложные задачи, где нужно предусмотреть тесную связку HTML+PHP на уровне каждого подблока и различных html-тэгов.
Для решения таких задач больше подходит шаблонизатор в виде специальных php-функций, которые упрощают формирование html-кода.
В примере выше (3-й листинг) мы выводили html через echo и визуально html-код сильно «загрязняет» php. Чтобы от этого избавиться можно использовать чистый php, где формированием html будут заниматься отдельные функции.
О подобном подходе я уже рассказывал в статье Форматированный вывод записей в MaxSite CMS. В ней примеры основанные на выводе записей, но на самом деле возможности такие, что мы можем организовать вывод произвольных данных.
Все вышеприведенные примеры можно сделать так:
// новый объект Page_out $p = new Page_out; $p->div_start('top', 'wrap'); $p->div(getinfo('name_site'), 'name_site'); $p->div(getinfo('description_site'), 'description_site'); $p->div_end('top', 'wrap');
Здесь мы вообще не видим HTML-кода, хотя интуитивно он более чем понятен. Метод div_start открывет два div'а div.top и div.wrap. То есть в параметрах мы указываем css-классы. Метод div_end - закрывает div'ы.
Метод div выводит указанный текст (первый аргумент) в полном div-блоке с указанным во втором аргументе css-классом.
Теперь, если нужно поменять html-верстку, то меняем её через эти же php-функции. Если метод div «режет» глаз, то можно воспользоватья его аналогом - tag. Посмотрите на этот же вариант, только имя сайта и описание я загнал в H1 и H2.
// новый объект Page_out $p = new Page_out; $p->div_start('top', 'wrap'); $p->tag(getinfo('name_site'), 'name_site', 'h1'); $p->tag(getinfo('description_site'), 'description_site', 'h2'); $p->div_end('top', 'wrap');
Если стоит задача оформить блок в более сложном html-стиле, то можно воспользоваться методом block.
$p->block('<span style="color: red">', '</span>', $text);
В общем же случае, чтобы привести код к единому стилю с использованием $p без echo, для вывода произвольного html используется одноименный метод.
$p->html('<hr>');
Если требуется вывести div.clearfix, то можно сделать это так:
$p->clearfix();
Собственно, уже этих возможностей вполне достаточно, чтобы организовать практически любой HTML со сложной PHP-логикой. Кроме того, у функций Page_out есть дополнительный «интеллект». Например метод block выведет данные, только если они реально существуют. Если раньше приходилось делать так:
... откуда-то получили $text, которая может быть пустой ... if ($text) { echo $text; }
То теперь можно проще:
$p->block('<span>', '</span>', $text);
Span я поставил для примера. Метод block сам применит условие if ($text).
Ещё расскажу про метод link, который формирует произвольную html-ссылку. Например можно оформить ссылку в h1.name_site.
$p->tag( $p->link('ссылка', 'Название'), 'name_site', 'h1' );
Если делать «традиционно», то код мог бы быть таким:
<?php $link = 'ссылка'; $name = 'Название'; echo '<h1 class="name_site"><a href="' . $link . '">' . $name . '</a></h1>';
Вполне очевидно, что с помощью Page_out код получается чище, компактней и более понятный. Единственный минус использования Page_out - потребуется потратить 10 минут, чтобы узнать за что отвечают его методы. Впрочем, относящихся только к форматированному html-выводу совсем ничего: html, tag, div, div_start, div_end, block и link.
ps Ещё один небольшой пример, основанный на «синтаксическом сахаре» PHP. Код хоть и небольшой, но интересный.
$p -> div_start('top', 'wrap') -> tag( ( !is_type('home') ? $p->link(getinfo('siteurl'), getinfo('name_site')) : getinfo('name_site') ), 'name_site', 'h1') -> tag(getinfo('description_site'), 'description_site', 'h2') -> div_end('top', 'wrap') ;