Объявление

Хотите приглашение на сайт? Пишите: niikto@samovarchik.info


 

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

В FluxBB.PE используется шаблонизатор, предоставленный artoodetoo.
Я дополнил его, сохранив первоначальную задумку нетронутой.

Шаблонизатор заточен под наш движок, поэтому он не такой универсальный, как, например, Smarty или Twig, но зато очень быстрый и простой в освоении.

Возможности шаблонизатора:

  • Собственный язык разметки (нет необходимости использовать PHP в шаблонах)

  • Условный вывод разметки

  • Циклы для вывода однотипных данных

  • Наследование шаблонов

  • Блоки разметки

  • Импорт данных (неявная передача информации шаблонам)


Язык шаблонизатора не окончательный, и возможно будет еще меняться. В таком случае, я изменю/дополню и данное руководство.

Шаблон представляет собой HTML-код, дополненный особой разметкой. Каждый тэг такой разметки заключен в фигурные скобки { } (в противовес HTML-тэгам, которые заключены в угловые скобки < >).
Открывающая и закрывающая фигурные скобки должны находиться на одной строке. Единственным тэгом, который может занимать несколько строк, является комментирующий тэг. Между открывающей фигурной скобкой и первым ключевым словом не должно быть никаких пробельных символов (пробел, табуляция и т.д.). После ключевого слова и между элементами внутри тэга может находиться любое количество пробельных символов.

Файл шаблона имеет расширение *.tpl
Шаблоны в движке хранятся в директории protected/template/
На данный момент в движке используется XHTML 1.0 Strict

Cодержание:

  1. Вывод информации

  2. Комментарии

  3. Условный вывод разметки

  4. Циклы

  5. Подшаблоны и мастершаблоны

  6. HTTP-заголовки

  7. Блоки разметки

  8. Импортирование переменных

  9. Инициализация переменных

  10. Javascript в шаблонах

Вывод информации

Самый простой тэг вывода выглядит следующим образом: {'текст'} Вместо этого тэга браузеру будет отправлена строка, заключенная в кавычки. Т.е. следующий код

<p>{'Hello, World!'}</p>

будет передан браузеру как

<p>Hello, World!</p>

Чтобы вывести данные, хранящиеся в переменной с именем $variable, используется тэг {$variable}. Часто переменные имеют сложную структуру, поэтому внутри шаблонов можно встретить {$array['index']} (обращение к элементу массива $array с индексом 'index'), {$array[$index]} (в качестве индекса используется значение переменной $index) и более сложные варианты.
Например, если переменная $str имеет значение 'Hello, World!', то

<p>{$str}</p>

будет передан браузеру как

<p>Hello, World!</p>

Данный тэг имеет дополнительные возможности. Форма {?$variable} сначала проверяет существование переменной $variable, и в случае ее отсутствия ничего не выводит. Форма {@$variable} перед выводом переменной делает ее преобразование функцией htmlspecialchars() с некоторыми настройками. Комбинированная форма {?@$variable} сначала проверяет существование переменной, а затем выполняет преобразование. Обе эти формы нужны для специфических случаев вывода информации.
Пример (взят из register.tpl):

<input type="text" name="req_user" value="{?@$_POST['req_user']}" size="25" maxlength="25" />

В данном случае если пользователь только перешел на страницу регистрации, $_POST['req_user'] не существует, и поэтому мы получим атрибут value="". Если пользователь заполнил анкету и нажал кнопку "Регистрация", но не заполнил какое-то из обязательных полей (для определенности, Имя Пользователя (req_user) он заполнил), то мы вынуждены отобразить страницу регистрации заново, вставив туда уже введенные данные. В этом случае мы получим атрибут value="username", где username - это пример того, что мог ввести пользователь. При этом введенные пользователем данные были преобразованы с помощью функции htmlspecialchars(), чтобы заменить все спецсимовлы HTML на их аналоги (сущности HTML).

Вывод информации может принимать более сложные формы. Например: {(условие) ? 'строка1' : 'строка2'} проверяет условие в круглых скобках (написанное на PHP) на истинность, и в случае, если оно истинно выводит строку 1, и если ложно - строку 2. Такие строки могут содержать HTML-разметку, но не могут содержать внутри себя тэги шаблонизатора или даже просто фигурные скобки. Пример использования:

<li class="{($item_number % 2 == 0) ? 'roweven' : 'rowodd'}">...</li>

В данном случае для четных и нечетных элементов списка используются соответствующие классы стилей.

Часто используется и такая форма: {sprintf($str1, $str2)} или чуть более сложные ее варианты. Результатом будет строка, полученная из $str1, путем вставки в нужное место строки $str2. Подробности можно узнать здесь.

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

Для любознательных

На самом деле тэги

{'Hello, World!'}
{$variable}
{?$variable}
{@$variable}
{?@$variable}

эквивалентны следующему PHP-коду:

<?php echo 'Hello, World!' ?>
<?php echo $variable ?>
<?php echo isset($variable) ? $variable : '' ?>
<?php echo $this->_e($variable) ?>
<?php echo $this->_e(isset($variable) ? $variable : '') ?>

Где метод $this->_e() - это обертка над htmlspecialchars() с предустановленными параметрами.

В некоторых случаях, можно использовать $this->_e() явно:

{(true) ? $this->_e($str) : '...'}

Аналогично для функций:

{sprintf($lang_common['Last visit'], $page_statusinfo['last_visit'])}
{@my_text_func($var)}

эквивалентно:

<?php echo sprintf($lang_common['Last visit'], $page_statusinfo['last_visit']) ?>
<?php $this->_e(my_text_func($var)) ?>

По вполне понятным причинам, использовать с функциями форму {?...} нельзя.
Аналогично, по той же причине, запись вроде {?'text'} приведет к ошибке на этапе генерации HTML-разметки из шаблона.
Но никто не запрещает использовать, например, {@'a > b'} (здесь '>' будет заменено на &gt;)

Комментарии

Для того, чтобы оставить комментарий в тексте шаблона, можно использовать как комментирующим тэгом:

{* Комментарий на несколько строк *}

так и HTML-комментарием:

<!-- Комментарий на несколько строк -->

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

PHP-комментарии

Можно использовать и PHP-комментарии:

<?php // Комментарий до конца строки ?>
<?php # Комментарий до конца строки ?>
<?php /* Комментарий на несколько строк */ ?>

Естественно, что такие комментарии, как и любой другой PHP-код, не будут переданы браузеру.

Условный вывод разметки

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

Пара тэгов {if $var}некоторый_текст{endif} заставляет 'некоторый_текст' (это может быть как просто строка текста, так и кусок разметки произвольного размера, в том числе включающий в себя другие тэги шаблонизатора) быть переданным браузеру только в том случае, если переменная $var существует и "непуста". Пустыми считаются следующие переменные:

  • переменная с неопределенным (null) значением

  • логическая переменная со значением ложь (false)

  • числовая переменная со значением 0

  • строка не содержащая ни одного символа

  • строка "0"

  • массив не содержащий ни одного элемента

Подробнее можно узнать здесь

Думаю, стоит подчеркнуть разницу между

{if $var}{$var}{endif}

и (см. Вывод информации)

{?$var}

Для определенности предположим, что $var - это некоторое число. Тогда в первом случае если $var существует, но равно 0, ничего не будет отображено на странице, а во втором случае отобразится 0. Если число не нулевое, то оба случая идентичны друг другу. Если $var не существует, то в обоих случаях ничего не будет отображено.

Обратный вариант - нам нужно вывести текст, если переменная не существует или пуста - реализуется парой тэгов {ifnot $var}некоторый_текст{endif}.
Пример использования (взят из default_layout.tpl):

{* Is this a page that we want search index spiders to index? *}
{ifnot $pun_allow_index}
<meta name="ROBOTS" content="NOINDEX, FOLLOW" />
{endif}

Т.к. $pun_allow_index разрешает индексацию, если принимает значение true, а <meta ...>-тэг - запрещает, то он должен быть выведен, когда $pun_allow_index имеет значение false.

Для того, чтобы вывести одну информацию, если переменная существует и непуста, и другую информацию в противном случае, используется связка из трех тэгов: {if $var}текст1{else}текст2{endif}. Фактически это можно воспринимать как (более эффективную) замену паре условий {if $var}текст1{endif}{ifnot $var}текст2{endif}.
Пример:

{if $topics}
<p>Тут должен был быть список тем.</p>
{else}
<p>Форум пуст.</p>
{endif}

Если в ветке форума есть хотя бы одна тема, то будет выведен первый блок, а если в ветке форума нет еще ни одной темы, то мы увидим сообщение о пустом форуме.

Иногда из ложности условия {if $var} не следует напрямую необходимость вывести второй блок (который после {else}), а необходимо проверить еще одно условие. В таком случае вместо того, чтобы нагромождать тэги {else}{if $var2}текст2{endif}{endif} можно воспользоваться тэгом {elseif $var2}. Очевидно, что в этом случае отпадает необходимость и во втором {endif}. Более того, новое условие может сопровождаться своим собственным {else} или {elseif}, увеличивая таким образом количество возможных вариантов вывода информации браузеру.
Пример использования:

{if $user_is_guest}
<strong>Гость</strong>
{elseif $user_is_admin}
<strong>Админ</strong>
{elseif $user_is_moderator}
<strong>Модератор</strong>
{else}
<strong>Обычный пользователь</strong>
{endif}

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

{if (is_array($arr))}
{ifnot ($var1 && $var2)}
{elseif (count($arr) > 1)}

Да, упрощенные тэги превращаются в расширенные простым добавлением круглых скобок. Внутри круглых скобок может использоваться любое выражение PHP, результатом выполнения которого будет истина (true) или ложь (false).
Имейте в виду, что переменные при этом не проверяются на существование, как в упрощенных тэгах.

Для любознательных

Тэги

{if $variable}
{ifnot $variable}

эквивалентны следующему PHP-коду:

<?php if (!empty($variable)): ?>
<?php if (empty($variable)): ?>

Т.к. функция empty() принимает в качестве аргументов только переменные, то использование в упрощенных условных тэгах чего-либо помимо переменных приводит к ошибке на этапе генерации HTML-разметки.

Циклы

Для того, чтобы отобразить на странице последовательность однотипных данных, удобно воспользоваться циклами. Последовательный перебор элементов массива осуществляется тэгом {foreach $arr as $item}разметка{endforeach}, где $arr - это массив, а переменной $item на каждом шаге цикла присваивается значение следующего элемента массива. Разметка заключенная между {foreach} и {endforeach} будет выведена столько раз, сколько элементов в массиве (в том числе и ни разу, если массив пуст).

Иногда помимо самого элемента массива, удобно знать также и его индекс (ключ), который может быть как числом, так и текстовой строкой, в зависимости от внутреннего устройства массива. Для этого используется расширенный вариант тэга {foreach $arr as $key => $item}разметка{endforeach}, где $key на каждом шаге цикла принимает значение индекса элемента массива.

Для следующего примера предположим, что в массиве $messages хранятся заголовки сообщений, которые надо отобразить на странице вместе со ссылкой на само сообщение. При этом массив имеет такую структуру, что идентификатор сообщения совпадает с индексом этого сообщения в массиве.

{if $messages}
{* Есть хотя бы одно сообщение, выводим сообщения на страницу *}
<ul>
{foreach $messages as $id => $message}
    <li><a href="message.php?id={$id}">{@$message['subject']}</a></li>
{endforeach}
</ul>
{else}
<p>Нет сообщений.</p>
{endif}

Циклы могут быть вложенными, т.е. внутри разметки заключенной между парой {foreach}...{endforeach} может находиться еще один или несколько циклов для каких-то других данных. На каждой итерации внешнего цикла, вложенный цикл будет заново перебирать переданные ему данные и выводить соответствующую информацию на страницу. Таким образом вложенные циклы имеют смысл только если передаваемая им информация изменяется на каждой итерации внешнего цикла. Например, если каждый элемент массива тоже является массивом.

Для любознательных

Тэги

{foreach $arr as $item}
{foreach $arr as $key => $item}

эквивалентны следующему PHP-коду:

<?php foreach ($arr as $item): ?>
<?php foreach ($arr as $key => $item): ?>

т.е. ничего неожиданного.

Вместо переменной массива можно использовать функции PHP, возвращающие массивы (например explode()) или же определять массив вручную при помощи конструкции array(...).
Пример:

<dl>
{foreach array(1 => 'one', 2 => 'two', 3 => 'three') as $num => $str}
    <dt>{$num}</dt>
    <dd>{$str}</dd>
{endforeach}
</dl>

Подшаблоны и шаблоны-родители

Здесь я хочу описать средства для повторного использования фрагментов разметки.

Если на нескольких страницах используются одинаковые элементы (например, шапка страницы, меню и т.п.), то естественным желанием будет выделить эти элементы в отдельные шаблоны - подшаблоны (шаблоны-элементы). Подшаблоны должны храниться в отдельных *.tpl файлах. В движке дополнительно используется правило сохранять подшаблоны в папке 'elements/'. Для того, чтобы вывести содержимое подшаблона, в нужное место помещается тэг {include 'elements/sub_tpl_name'}, где 'sub_tpl_name' - имя файла подшаблона. Обратите внимание, что внутри тэга расширение файла *.tpl должно быть опущено.

Подшаблоны можно подключать по условию:

{if $show_menu}
{include 'elements/menu'}
{endif}

Или несколько раз в цикле:

{foreach $arr as $item}
{include 'elements/show_item'}
{endforeach}

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

Шаблон-родитель (терминология "родитель-потомок" идет из ООП) выполняет схожую функцию, но не родитель выводится внутри текущего шаблона (как в случае с подшаблонами), а текущий шаблон, являясь потомком шаблона-родителя, вставляется внуть последнего. Для того, чтобы сообщить шаблонизатору, что данный шаблон расширяет (extends) шаблон с именем, например, 'base_template' (расширение файла *.tpl опять опускается), нужно в самом начале текущего шаблона добавить тэг {extends 'base_template'}.

Используя механизм расширения (наследования) шаблонов можно вкладывать шаблоны друг в друга по принципу матрешки. Любой шаблон может расширять только один шаблон-родитель, но иметь неограниченное количество вложенных шаблонов-элементов.

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

{$content}

В переменную $content помещается вся разметка, сгенерированная шаблоном-потомком (не путать с шаблоном-элементом!), непосредственно перед тем, как шаблонизатор начинает обработку шаблона-родителя.

{block('content')}

Аналогично переменной $content, блок 'content' содержит в себе разметку, сгенерированную шаблоном-потомком. Этот метод появился сравнительно недавно, и является более надежным, т.к. переменную $content можно переопределить внутри шаблона, а блок с именем 'content' переопределить нельзя. Если внутри шаблона прибегнуть к средствам PHP, то переопределить можно и блок 'content', но это уже можно считать хаком.

Следующий пример в сильно упрощенной форме иллюстрирует все вышесказанное.

default.tpl

<html>
<head>
    <title>{$title}</title>
    <link rel="stylesheet" type="text/css" href="style/{$user['style']}.css" />
</head>
<body>
{block('content')}
</body>
</html>

main.tpl

{extends 'default'}
{include 'elements/header'}
{include 'elements/menu'}
{$content}
{include 'elements/footer'}

index.tpl

{extends 'main'}
    <p>Здесь должно было быть много-много разметки</p>

Для простоты предположим, что все подшаблоны содержат только тег <p>...</p>, где вместо троеточия помещено имя соответствующего файла. Тогда результат, отправленный браузеру будет таким:

<html>
<head>
    <title>Заголовок</title>
    <link rel="stylesheet" type="text/css" href="style/some_style.css" />
</head>
<body>
    <p>header</p>
    <p>menu</p>
    <p>Здесь должно было быть много-много разметки</p>
    <p>footer</p>
</body>
</html>

Расширение шаблонов может быть условным. Например, для формирования версии страницы для печати, можно воспользоваться альтернативным шаблоном main_print.tpl, в котором могут отсутствовать избыточные элементы оформления.

{if $print_mode_on}
{extends 'main_print'}
{else}
{extends 'main'}
{endif}
....
Для любознательных

В каком порядке формируется разметка?

Начнем с самого простого - {include ...}
Формирование разметки происходит следующим образом: шаблонизатор формирует разметку в текущем шаблоне строка за строкой, пока не натыкается на тэг {include 'elements/example'}. В этот момент он переключается на формирование разметки внутри elements/example.tpl, которая сразу же добавляется к уже сформированной разметке текущего шаблона (никакой буферизации не используется). Все переменные, доступные при формировании текущего шаблона, станут также доступными в elements/example.tpl. Если в elements/example.tpl импортируются какие-либо дополнительные переменные, то при возвращении в текущий шаблон, они также станут доступными и в нем.

Тэг {extends ...} помещает имя расширяемого шаблона (шаблона-родителя) в стек обработки и запускает буферизацию, после чего вся формируемая разметка будет складываться в буфер. Подробности можно прочитать здесь: ob_start() и ob_get_clean(). После завершения формирования разметки текущего шаблона, все содержимое помещается в переменную $content и в блок с именем 'content', и шаблонизатор приступает к обработке шаблона, находящегося в вершине стека обработки - родителя для текущего шаблона. Все переменные, доступные в текущем шаблоне, остаются доступными при обработке родительского шаблона, который в свою очередь может импортировать какие-либо свои дополнительные переменные.

Для любознательных 2

Имена шаблонов могут формироваться динамически:

{imports $tpl_mode}
{include 'elements/example_'.$tpl_mode}

Аналогично и для {extends}.

Блоки разметки

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

Определяется блок следующим образом:

{block 'block_name'}
<Разметка>
{endblock}

Для того, чтобы вставить содержимое этого блока в разметку, воспользуйтесь тэгом

{block('block_name')}

Обратите внимание на круглые скобки, они важны.

Порядок определения блоков важен для конечного результата. Блок объявленный раньше (например, в шаблоне-потомке) переопределяет любые блоки с таким же именем, объявленные позже (например, в шаблоне-родителе). Для того, чтобы изменить это поведение, можно воспользоваться следующей конструкцией:

{block 'block_name'}
{parent}
<Разметка>
{endblock}

Тэг {parent} сообщает шаблонизатору, что в это место нужно будет вставить содержимое следующего блока с таким же именем (в основном это блок в шаблоне-родителе, отсюда и имя тэга). Тэг {parent} может располагаться в любом месте в разметке внутри конструкции {block ...}...{endblock}

Если после такого блока не будет больше определено ни одного блока с таким же именем, то тэг {parent} будет просто удален из разметки в момент ее вывода браузеру.

Пример использования (взят из нашего движка, упрощен):
default_layout.tpl:

<html>
<head>
...
{block 'page_head_js'}
{if $required_fields}{include 'elements/required_fields_js'}{endif}
{endblock}
{block('page_head_js')}
...
</head>

Любой шаблон, для работы страницы, получаемой из которого, нужен файл common.js:

{block 'page_head_js'}
{parent}
<script type="text/javascript" src="js/common.js"></script>
{endblock}

Т.о. мы помещаем дополнительную строчку в блок 'page_head_js', который в результате будет находиться в блоке <head>...</head> страницы, отправляемой браузеру.

Блоки могут быть вложенными:

{block 'outer_block'}
<...>
{foreach $items as $item}
{block 'inner_block'}
{parent}
{$item}
{endblock}
{endforeach}
{block('inner_block')}
<...>
{endblock}

Здесь мы выводим содержимое массива $items в обратном порядке внутрь блока 'outer_block'. Стоит заметить, что в данном конкретном случае эффективнее было бы использовать array_reverse().

Как упоминалось выше, существует особый блок с именем 'content', который автоматически создается перед началом обработки шаблона-родителя и содержит разметку, сформированную в шаблоне-потомке. Для каждого шаблона-родителя в цепочке наследования этот блок переопределяется заново.

HTTP-заголовки

HTTP-заголовки отправляются тэгом {header 'текст заголовка'}. Подробности можно узнать здесь и здесь.

Пример:

{header 'Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'}
{header 'Content-type: text/html; charset=utf-8'}

{header}-тэги должны располагаться в шаблоне ДО любой HTML-разметки. Перед ними могут находиться только макросы {imports} (см. чуть ниже) или комментирующие тэги. Они не должны присутствовать в подшаблонах. В случае расширения шаблонов, {header}-макросы должны быть помещены в последний в цепочке родительский шаблон, т.е. в тот, который не содержит тэга {extends}.

Импортирование переменных

Откуда вообще берутся переменные, используемые в шаблонах?

Часть переменных (вместе с хранящимися в них данными, естественно) передаются шаблонизатору напрямую, когда он вызывается движком.

Для любознательных

Со стороны движка это выглядит приблизительно следующим образом:

Pe::get('view')->render('template_name', array(
    'var_name1' => $var_value1,
    'var_name2' => $var_value2,
...
    'var_nameN' => $var_valueN
));

Но далеко не все данные удобно каждый раз явно передавать шаблонизатору. Для таких данных в настройках движка прописаны правила импорта, а сам импорт в конкретный шаблон осуществляется посредством тэга {imports $var_name}, где $var_name - имя импортируемой переменной.

Процесс импорта происходит следующим образом:

  1. шаблонизатор проверяет, а не была ли переменная с таким именем явно передана при вызове шаблонизатора или импоритрована ранее?

  2. если такой переменной не существует, она создается (но пока не содержит данных)

  3. далее шаблонизатор обращается к средствам движка, чтобы по имени переменной определить есть ли для нее правила экспорта, и в случае успеха, помещает в переменную соответствующие данные, а в случае провала - неопределенное значение (null).

Т.о. получаем правила:

  • Любая переменная, переданная напрямую шаблонизатору, имеет приоритет перед импортом переменной с таким же именем внутри шаблона.

  • Если в начале шаблона импортировать некоторую переменную, а потом подключить (include) подшаблон, внутри которого тоже импортируется переменная с таким же именем (это может быть другая переменная, см. чуть ниже про переименование), то импорт внутри подшаблона игнорируется. И наоборот, если сначала был подключен подшаблон, а после него импорт, то приоритет за импортом внутри подшаблона.

  • Если текущий шаблон, расширяет (extends) некоторый родительский шаблон, и в каждом из них есть импорт переменной с одним и тем же именем, то приоритет имеет импорт в текущем шаблоне.

Если есть необходимость переименовать импортируемую переменную, можно воспользоваться тэгом {imports 'old_name' as $new_name}, где в кавычках указывается стандартное имя, для которого есть правило импорта, а после ключевого слова as - желаемое. Кавычки могут быть как одинарные, так и двойные.
Абстрактный пример:

{imports "pun_config" as $config1}
{imports 'pun_config' as $config2, 'pun_config' as $config3}

Т.о. мы получили 3 копии переменной $pun_config под разными именами. Этот пример содержит как одинарные, так и двойные кавычки. Кроме того, он показывает еще одну особенность тэга импорта - возможность импортировать сразу несколько переменных в одном объявлении, разделив их имена запятыми. Однако сразу оговорюсь, что я предпочитаю придерживаться правила "одно объявление == импорт одной переменной", поэтому на данный момент в шаблонах движка вы не увидите конструкций, подобных второй строчке примера.

Тэг импорта может быть помещен в любое место шаблона, но обязательно до первого использования импортируемой переменной. На него распространяются правила условного вывода, т.е. если тэг {imports} находится внутри условных тэгов, то импорт происходит в зависимости от выполнения условия. В случае с циклами эффективнее импортировать переменую до цикла, чем на каждой итерации внутри него.

Для любознательных

Тэги

{imports $pun_config}
{imports 'my_var' as $var}

эквивалентны следующему PHP-коду:

<?php isset($pun_config) or $pun_config = Pe::get('pun_config'); ?>
<?php isset($var) or $var = Pe::get('my_var'); ?>

О том, как работает Pe::get() обязательно будет статья, но как скоро, я пока сказать не могу.

Инициализация переменных

Иногда бывает полезным создать переменную и присвоить ей значение прямо в коде шаблона.
Это можно сделать при помощи тэга {set}.

Пример:

{set $tab_index = 1}
<form ...>
    <input ... tabindex="{$tab_index++}" />
{foreach ...}{* Мы не знаем, сколько <input> будет помещено в форму *}
    <input ... tabindex="{$tab_index++}" />
{endforeach}
    <input type="submit" ... tabindex="{$tab_index++}" />
</form>

Javascript в шаблонах

В качестве реакции на событие на некоторых страницах используются скрипты подобные такому:

<form id="quickpostform" method="post" action="post.php?tid={$id}" onsubmit="this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}">

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

Одно из возможных решений, не самое красивое, выглядит так:

<form id="quickpostform" method="post" action="post.php?tid={$id}" onsubmit="this.submit.disabled=true;if(process_form(this)){
return true;
}else{
this.submit.disabled=false;return false;}">

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

Но можно поступить проще: заключить текст скрипта в одинарные или двойные кавычки и поместить внутрь тэга вывода

onsubmit="{'this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}'}"
onsubmit="{"this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}"}"

К тексту, заключенному в кавычки применяются все правила относительно строк PHP. Т.е. строки

<p>{'qwerty {$var} 123'}</p>
<p>{"qwerty {$var} 123"}</p>{* переменной $var не существует *}
<p>{"qwerty {$var} 123"}</p>{* переменная $var содержит "Hello, World!" *}
<p>{"qwerty {\$var} 123"}</p>

будут передано браузеру как

<p>qwerty {$var} 123</p>
<p>qwerty  123</p>
<p>qwerty Hello, world! 123</p>
<p>qwerty {$var} 123</p>

Если в тексте, заключенном в кавычки нужно использовать символ кавычки, то его можно экранировать символом "\" (обратная косая черта):

{'Something\'s here'}
{"He said \"Hello!\""}

Редактировался yoorick (09.02.2012, 01:43:06)

Скоро приведу это к нормальному виду и допишу еще немного инструкций.

Еще стало очевидным, что надо чуть-чуть подправить шаблонизатор. {'...'} не учитывает тот факт, что в js-скрипте на событие может быть текст заключенный в одинарные кавычки. (на данный момент в движке нет ни одного js-скрипта одновременно использующего и одинарные кавычки и фигурные скобки).

Редактировался yoorick (24.10.2011, 23:41:34)

Добавил spoiler-секции "Для любознательных".

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

{imports 'admin_navlinks' as $navlinks}

нельзя было использовать более одного пробела до и после 'as', и тем более вместо пробела случайно поставить символ табуляции. Теперь он стал более лояльным в этом плане.
Заодно изменил обработку строк в кавычках, что отразил в статье в 8 части Javascript в шаблонах.
Теперь есть ощущение, что ее стоит объединить с 1 частью Вывод информации, да и 9 часть Замечание о переменных надо бы раскидать по другим...
Ох уж этот перфекционизм :)

Ссылка по теме:

artoodetoo: Моё наследование в шаблонах

Отредактировал статью.
Дополнил в соответствии с нововведениями в шаблонизаторе.
Подправил часть из того, что уже было написано.
По всей статье заменил слово "макрос" на слово тэг. Несмотря на то, что оно формально подходит, хочется добиться совместимости в терминологии с такими шаблонизаторами, как Smarty и Twig. В том же Twig, кстати, есть отдельное понятие макроса, которое совсем не похоже на то, что здесь называлось макросом (а теперь называется тэгом).

 

Дизайн сайта отсутствует
оформление: Группа «САМОВАРчик»

[ Сгенерировано за 0.015 сек, 8 запросов выполнено - Использовано памяти: 2.07 MiB (Пик: 2.19 MiB) ]