ФорумПрограммированиеПыхнуть хотите?F.A.Q. → О шаблонных механизмах. XTemplate.

О шаблонных механизмах. XTemplate.

  • AlexB

    Сообщения: 4290 Репутация: N Группа: в ухо

    Spritz 19 марта 2008 г. 15:20

    В общем, начинаю цикл статей, посвященный шаблонизаторам.

    О наболевшем, или не Smarty единым

    Последнее время мне приходилось проводить собеседования с претендентами на должность php-программистов. Беседуя с кандидатами,  заметил, что когда речь заходит о шаблонизаторах (термин, ИМХО, несколько коряв, но поскольку прочно прижился в веб-сообществах позвольте его использовать) все неизменно называют одно единственное слово – Smarty. Причем мнения о Smarty бывают диаметрально противоположные от: “Знаю, активно использую”, до: “Ненавижу эту гадость”. Аналогичная картина наблюдается на, посещаемых мною, интернет-форумах. В умах подавляющего большинства разработчиков слова Smarty и шаблонизатор – синонимы. Надо ли говорить, что на самом деле это не так? В добавок к сложившемуся в головах стереотипу “шаблонизатор это Smarty”, у меня еще создалось впечатление, что многие начинающие разработчики вообще не понимают зачем именно им, именно в конкретном проекте нужен шаблонный парсер и их выбор архитектуры базируется на одной из двух, витающих по интернету, мантр: “настоящий профессионал всегда пользуется шаблонизатором для отделения логики от представления” или “php сам по себе шаблонизатор и незачем изобретать велосипед”. На самом деле и то и другое верно только отчасти и не в коем случае, не следует данные мантры принимать как истину в последней инстанции. А вот как точку, отсчета мы их можем принять и постараться рассмотреть по возможности отстраненно. Данный цикл статей преследует следующие цели:

    • Постараться разобраться во всех тех неоднозначностях и непониманиях, что царят вокруг шаблонизаторов. В т.ч. развенчать “догмы” и “стереотипы”. :)

    • Предложить конкретные решения и подходы и сделать введение в два продукта – XTemplate
      и Blitz, про которые практически нет никакой информации, кроме официальной документации (увы, практика показала, что начинающий программист уже на этапе продирания сквозь документацию отказывается от идеи использовать данный продукт, из-за банального непонимания).

  • AlexB

    Сообщения: 4290 Репутация: N Группа: в ухо

    Spritz 19 марта 2008 г. 15:21, спустя 53 секунды

    XTemplate – шаблонный движок для “маленьких”

    Прежде чем, пускаться в теоретические дебри и рассуждения, рассмотрим готовый продукт – шаблонизатор XTemplate, который можно прямо сейчас скачать (http://www.phpxtemplate.org/XTemplateDownloads) и прямо сейчас использовать. Это одно из крайне простых решений, которое, тем не менее, содержит все необходимое для создания более-менее стройной архитектуры средне-статистического веб–сайта. Основные его приемущества заключаются в следующем:

    • Маленький размер. Для работы требуется всего один небольшой php-файл.

    • Крайне простой синтаксис шаблонов, на изучение которого уйдут считанные минуты.

    • Написан на PHP и не использует никаких дополнительных расширений, т.е. вы 100% сможете его использовать везде, где будет работать или отлаживаться ваш проект. (Лично меня всегда удивляло, что для огромного количество разработчиков именно этот пункт является решающим фактором, но факт есть факт).

    • Имеет “блочную идеологию” (В чем именно ее преимущество мы подробнее рассмотрим позднее).


    Для работы с XTemplate надо скопировать файл xtemplate.class.php, в удобное место в файловой структуре вашего проекта (ну например, в директорию libs), после чего подключить движок и создать объект парсера (php скрипт в котором производим все манипуляции над шаблоном, в дальнейшем будем называть контроллером):

    include('libs/xtemplate.class.php'); // Подключаем файл
    $xtpl = new XTemplate('templates/test.xtpl'); // Создаем объект

    test.xtpl – собственно файл шаблона (их для удобства мы будем хранить в директории templates). Синтаксис шаблонов XTemplate представлен  всего двумя основными конструкциями. Первой и основной является блок. Блоки имеют следующий вид:

    <!– BEGIN:block –>
    Содержимое блока
    <!– END:block –>

    Каждой инструкции открывающей блок <!– BEGIN:block –> должна обязательно соответствовать закрывающая инструкция <!– END:block –>, где “block” имя блока, которое вы можете давать по своему усмотрению исходя из контекста содержимого блока, коим может быть любой HTML код. Про блоки надо понять три  вещи:

    • Содержимое блока никаким образом не будет отображено, пока в контролере явно не будет указано это сделать. Данная операция называется парсинг блока и выполняется командой
      $xtpl->parse(‘имя блока’); (В принципе, есть возможность задать вывод по умолчанию для не пропарсенных блоков с помощью функции set_null_block)

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

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


    Пришло время, проиллюстрировать вышесказанное примером. Итак, пусть шаблон имеет вид:

    Шаблон:

    <!– BEGIN:index –>

    <!– BEGIN:block –>
    <p>Это содержимое блока</p>
    <!– END:block –>

    <!– END:index –>

    Контроллер:

    include('libs/xtemplate.class.php'); // Подключаем файл
    $xtpl = new XTemplate('templates/test.xtpl'); // Создаем объект

    for($i=0; $i<3; $i++)
    {
    $xtpl->parse('index.block'); // Парсим блок "block" три раза в цикле
    }
    $xtpl->parse('index'); // Парсим блок "index"

    $xtpl->out('index');


    Результат вывода:

    <p>Это содержимое блока</p>

    <p>Это содержимое блока</p>

    <p>Это содержимое блока</p>

    Обратите внимание, блок “block” вложен в блок “index”. Для доступа к внутренним блокам в качестве разделителя используется точка. Таким образом, парсинг внутреннего блока осуществляется командой $xtpl->parse('index.block');

    Команда out выводит результат распарсенного блока на печать. Также есть команда text, ее отличие в том, что результат парсинга просто возвращается. Т.е. можно писать:


    $result = $xtpl->text('index');
    Делаем что-то с  $result
    echo $result;

    Это бывает необходимо, когда с результатом парсинга надо проделать те или иные действия, например, сохранить пропарсенный шаблон в кэше.

    Второй основной конструкцией синтаксиса XTemplate является тег, имеющий следующий синтаксис:

    {value}

    Аналогично блоку,  пока тегу не будет явно присвоено значение с помощью функции assign, никакого вывода в шаблон осуществляться не будет (В принципе, есть возможность задать вывод по умолчанию для не присвоенных блоков с помощью функции set_null_string). Фактически тег – это переменная, значение которой устанавливается в котроллере командой assign. Для простоты понимания, представьте, что {value} это примерно тоже самое, что <?php echo $value; ?>
    Поправим наш пример так, чтоб в парсинге блока участвовала переменная – счетчик пропарсенных блоков:

    Шаблон:

    <!– BEGIN:index –>

    <!– BEGIN:block –>
    <p>{value}. Это содержимое блока </p>
    <!– END:block –>

    <!– END:index –>

    Контроллер (инициализация и финальный вывод опущены, аналогично будет и в дальнейшем):

    for($i=1; $i<=3; $i++)
    {
    $xtpl->assign('value', $i); // Устанавливаем тегу value значение $i
    $xtpl->parse('index.block');
    }
    $xtpl->parse('index');

    Результат вывода:

    <p>1. Это содержимое блока</p>

    <p>2. Это содержимое блока</p>

    <p>3. Это содержимое блока</p>

    С помощью команды assign можно устанавливать сразу целый массив, причем элементы массивов могут быть в свою очередь опять массивами. Цепочка вложенных ключей массива в теге отделяется точками. Например:

    Шаблон:

    <!– BEGIN:index –>
    <p>{value.key1}</p>
    <p>{value.key2.key2_1}</p>
    <p>{value.key2.key2_2}</p>
    <!– END:index –>


    Контроллер:

    $arr = array // Инициализируем массив сложной структуры
    (
    'key1' => 'Значение 1',
    'key2' => array
    (
    'key2_1' => 'Значение 2_1',
    'key2_2' => 'Значение 2_2'
    )
    );
    $xtpl->assign('value', $arr); // Одним присваиванием передаем массив в шаблон
    $xtpl->parse('index');

    Результат вывода:

    <p>Значение 1</p>
    <p>Значение 2_1</p>
    <p>Значение 2_2</p>

    Что касается синтаксиса, то XTemplate позволяет переопределить основные его элементы. В начале файла xtemplate.class.php' легко найти следующие параметры:


    public $block_start_delim = '<!– ';
    public $block_end_delim = '–>';
    public $block_start_word = 'BEGIN:';
    public $block_end_word = 'END:';
    public $tag_start_delim = '{';
    public $tag_end_delim = '}';

    Надо ли переопределять данные параметры – решайте сами. Смысла особого в этом нет. Сомнения вызывают лишь фигурные скобки, которые могут встретится внутри тегов <script> или <style>, однако XTemplate обрабатывает такие ситуации корректно. По все видимости именем тега, заключенного в скобки, может служить лишь простой набор символов, соответствующий валидным именам переменных php.

    Реальный проект, как правило, имеет сложною систему шаблонов, части которых могут повторятся. Для включения шаблона в шаблон используется конструкция {FILE "inside.xtpl"}. Имя включаемого файла может быть динамическим и подключаться с помощью функции assign_file следующим образом:

    Шаблон:

    <!– BEGIN:index –>
    <p>Это тект во внешнем шаблоне
    {FILE {inside}}
    <p>Это тект тоже во внешнем шаблоне
    <!– END:index

    Второй шаблон (inside.xtpl):

    <p>Это тект во внутреннем шаблоне

    Контроллер:

    $xtpl->assign_file('inside', 'templates/inside.xtpl');

    Результат вывода:

    <p>Это тект во внешнем шаблоне
    <p>Это тект во внутреннем шаблоне
    <p>Это тект тоже во внешнем шаблоне

    Ну и напоследок упомяну еще об одной фишке. XTemplate также предоставляет возможность “рекурсивного” парсинга с помощью функции $xtpl->rparse(‘index’); Рекурсивного в кавычках, потому что реальной рекурсии здесь нет, просто при вызове данной функции будут один раз распарсены все внутренние блоки блока index. Честно говоря, реального применить этот функционал мне нигде не удалось – за не надобностью.

    Вот вкратце, основные возможности XTemplate. Не смотря на простоту и лаконичность, на самом деле, присутствует все необходимое для построения HTML-ля любой сложности. Также можно порекомендовать посмотреть и погонять примеры кода, которые включены в скачиваемый дистрибутив.
  • AlexB

    Сообщения: 4290 Репутация: N Группа: в ухо

    Spritz 19 марта 2008 г. 15:21, спустя 25 секунд

    Блоки наше все!

    Когда мы на примере XTemplate рассмотрели принципы работы блочного шаблонизатора, давайте вернемся к тому, с чего начали. Собственно, к вопросу: “Какую вообще роль играет в нашем проекте шаблонизатор?”. Типовой ответ начинающего программиста: “Отделение логики от представления” или “Отделение программного кода от HTML”. А вот теперь стоп! Давайте разбираться и с тем и другим. Сначала разбираемся с логикой. Проблема в том, что логику нашего приложения можно разделить в свою очередь на две части:

    • Бизнес-логика т.е. извлечение данных и БД, их специфическая обработка, математически расчеты и.т.п.

    • Вью-логика т.е. собственно оборачивание в HTML теги


    Так какую логику мы хотим отделить от представления? Обе? Вряд ли т.к. тогда, возникает законный вопрос: “А что есть представление?”. Ага, видимо под словом “представление” и имеется ввиду Вью-логика. Итак, более корректно говорить, что с помощью шаблонизатора мы хотим отделить Бизнес-логику от Вью-логики или проще говоря логики вывода. Но здесь мы натыкаемся на противоречие с вторым посылом, а именно попыткой отделить программный код от HTML. Ибо логика она и в Африке логика и может быть реализована только с помощью алгоритмических языков, а никак не с помощью HTML, который как известно является языком разметки. Забегая вперед, скажу что конечно отделить чистый HTML невозможно, но можно постараться сделать шаблон максимально к нему приближенным.

    Второй проблемой при разделении логик, является то, что прежде чем перейти к вью, все данные должны быть подготовлены в бизнес части приложения и внесены в отдельные структуры. Но, черт возьми, это же идет в разрез с тем принципом, благодаря которому PHP стал самой популярной технологией в вебе – внедрение извлечения данных непосредственно в нужное место HTML. Хрестоматийным случаем является перебор результатов выборки из базы данных:


    while ($row = mysql_fetch_assoc($recordset))
    {
    ?> <td><?= $row['field'] ?></td> <?php
    }


    Как только внутри while мы написали ?> или echo – все. Никакого разделения логик нет. Значит надо выгружать все в промежуточный массив $rows:


    while ($row = mysql_fetch_assoc($recordset))
    {
    $rows[] = $row;
    }


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


    {foreach key=key item=item  from=$rows}
           <td>{$item.field}</td>
    {/foreach}


    Его шаблоны – это инструкции на алгоритмическом макроязыке написанном на PHP. Своего рода PHP на PHP.

    Осознав данный факт, многие программисты ударяются в другую крайность. Зачем, говорят они, нам нужен шаблонизатор, когда PHP - сам прекрасно выполняет данные функции. Действительно, переписав шаблон на чистый PHP, мы будем иметь не многим более сложную конструкцию:


    foreach($rows as $key => $item)
    {
    ?><td><?=  $item['field'] ?></td><?php
    }


    Отличием и преимуществом Smarty будет то, что данные могут быть переданы в шаблон непосредственно после сборки в промежуточный массив, причем в любое место шаблона, а в случае чистого PHP вся бизнес-логика должна быть завершена, прежде чем начнется непосредственно вывод. Да и вывод будет происходить строго сверху вниз, как и полагается алгоритмическому языку.

    Также надо заметить, что Smarty предлагает ряд удобных для построения хорошего вью вещей – признаки первой и последней итерации, четная и нечетная итерации и.т.п. (кстати, почему подобных псевдопеременных нет в PHP?).

    Но все эти бонусы по сравнению с необходимостью нудного изучения синтаксиса Smarty да и учитывая его тяжеловесность часто заставляет делать выбор в пользу чистого PHP. Данный подход получил название “нативных шаблонов”, и в различных статьях ратуют за него уже достаточно давно. Желающие могут ознакомится:

    http://spectator.ru/technology/php/easy_templates
    http://larin.in/archives/16

    Как видите, идея живет и процветает в программистских умах. Автор первой статьи, которая получила широкую известность в рунете, предложил противникам данного подхода “побриться бритвой Оккама”. Побреемся, не заросшими же ходить :).  Первый недостаток уже указан выше – по многим данным, как и в случае со Smarty, нам придется пробежаться циклом два раза, что согласитесь не есть гут. Но есть и второй. В данных статьях рассматриваются крайне простые примеры – вывод переменной в нужное место шаблона или простой цикл. Это как доказывать теоремы, рассматривая только предельные случаи. А при усложнении верстки, “нативные шаблоны” превращаются в достаточно навороченный код. Не верите? Давайте рассмотрим пример:

    Допустим нам надо вывести список футбольных команд (кстати, Спартак – чемпион!) с городами приписки. В реальной жизни, скорее всего, данные будут извлекаться из базы данных, но для простоты отладки и запуска разместим их в массиве:


    $teams = Array(
    Array('name' => 'Спартак', 'city' => 'Москва'),
    Array('name' => 'Зенит', 'city' => 'Санкт-Петербург'),
    Array('name' => 'Шинник', 'city' => 'Ярославль'),
    Array('name' => 'Сатурн', 'city' => 'Раменское'),
    Array('name' => 'Луч', 'city' => 'Владивосток')
    );


    А теперь предположим, что злой дизайнер-верстальщик запихнул их в таблицу, так чтоб город был под названием команды, да еще все это счастье в несколько столбцов. Т.е. требуемый HTML должен выглядеть следующим образом:


    <table cellpadding="3">
    <tr>
    <td><b>Спартак</b></td>
    <td><b>Зенит</b></td>
    <td><b>Шинник</b></td>
    </tr>
    <tr>
    <td>Москва<br><br></td>
    <td>Санкт-Петербург<br><br></td>
    <td>Ярославль<br><br></td>
    </tr>
    <tr>
    <td><b>Сатурн</b></td>
    <td><b>Луч</b></td>
    </tr>
    <tr>
    <td>Раменское<br><br></td>
    <td>Владивосток<br><br></td>
    </tr>
    </table>


    И в браузере это выглядит как:

    1. teams.gif (2361)
  • AlexB

    Сообщения: 4290 Репутация: N Группа: в ухо

    Spritz 19 марта 2008 г. 15:22, спустя 23 секунды

    Blitz – шаблонный движок для “взрослых”

    На правах анонса: Здесь мы подробно рассмотрим очень мощный шаблонизатор – Blitz.
  • Lirck

    Сообщения: 1658 Репутация: N Группа: Джедаи

    Spritz 23 марта 2008 г. 7:46, спустя 3 дня 16 часов 24 минуты

    А как с помощью XTemplate узнать «параметры» блока?
    Например:
    <!– BEGIN:block param=test –>
    The Text
    <!– END:block –>
  • AlexB

    Сообщения: 4290 Репутация: N Группа: в ухо

    Spritz 23 марта 2008 г. 10:28, спустя 2 часа 42 минуты 18 секунд

    Никак. А зачем собственно, когда можно внутри завести сколь угодно много разных вложенных блоков на все варианты?
    Какая исходная задача?
  • Lirck

    Сообщения: 1658 Репутация: N Группа: Джедаи

    Spritz 23 марта 2008 г. 10:52, спустя 24 минуты 15 секунд

    Ну например в блоке разместить текст, который будет виден лишь определенной группе людей…
  • AlexB

    Сообщения: 4290 Репутация: N Группа: в ухо

    Spritz 23 марта 2008 г. 10:56, спустя 3 минуты 12 секунд


    <!– BEGIN:block –>
    Этот текст видят все
    <!– BEGIN:private –>
    А этот, только члены элитного клуба. :)
    <!– END:private –>
    <!– END:block –>



    if (член элитного клуба)
    {
    $xtpl->parse('block.private');
    }
    $xtpl->parse('block');
  • Lirck

    Сообщения: 1658 Репутация: N Группа: Джедаи

    Spritz 23 марта 2008 г. 10:59, спустя 3 минуты 43 секунды

    Убедил =)
  • AlexB

    Сообщения: 4290 Репутация: N Группа: в ухо

    Spritz 25 марта 2008 г. 15:45, спустя 2 дня 4 часа 45 минут

    Добавил вторую статью про шаблонизаторы вообще:
    http://pyha.ru/forum/topic/659.msg7887#msg7887
    Уж не знаю, продирется ли кто через всю эту философию. :)
  • md5

    Сообщения: 11949 Репутация: N Группа: в ухо

    Spritz 25 марта 2008 г. 23:22, спустя 7 часов 37 минут 23 секунды

    чет кавычки какие-то… вордовские чтоли?
    все умрут, а я изумруд
  • AlexB

    Сообщения: 4290 Репутация: N Группа: в ухо

    Spritz 25 марта 2008 г. 23:26, спустя 3 минуты 48 секунд


    чет кавычки какие-то… вордовские чтоли?
    А да, ночью из ворда копировал, он сволочь сам поменял без спроса. Поправил. Кавычки везде простые одинарные.
  • md5

    Сообщения: 11949 Репутация: N Группа: в ухо

    Spritz 26 марта 2008 г. 0:06, спустя 40 минут 30 секунд

    с Х-темплейтс на выходных поэкспериментирую.
    а можно заказать как добавок к статье (раз уж тут обзор нескольких шаблонизаторов), рассмотр XSLT шаблонов? (если конечно, автор не против) или могу попросить человека дополнить…
    все умрут, а я изумруд
  • mechanic

    Сообщения: 125 Репутация: N Группа: Кто попало

    Spritz 26 марта 2008 г. 0:24, спустя 17 минут 16 секунд

    +1
    про XSLT хочется почитать!
  • AlexB

    Сообщения: 4290 Репутация: N Группа: в ухо

    Spritz 26 марта 2008 г. 0:30, спустя 6 минут 49 секунд

    Ну честно говоря отдельно писать про XSLT не хочется т.к. технология достаточно старая и известная, писано переписано про нее не мало. Есть доки, статьи, спецификации. Возможно имеет смысл какой-нибудь FAQ краткий набросать.

    Пока хочу подробно Blitz рассмотреть. Там есть о чем поговорить. Потому что по сравнению с XTemplate - это супер мощь, но с похожей идеологией. И статей по нему я не видел.

Пожалуйста, авторизуйтесь, чтобы написать комментарий!