13 636
правок
Изменения
Нет описания правки
<span style="border: 2px #FF8000 dashed; padding: 4px">Про VMX::Template можно Template можно сказать «ох уж эти перлисты — что ни пишут, всё Template::Toolkit получается».</span> Это к тому, что идея вообще-то схожая, но реализация гораздо проще и быстрее.
== Использование ==
=== PHP ===
Требуется PHP версии не ниже 5.4. <source lang="code-php">
require_once 'template.php';
# Конструктор(значения опций - по умолчанию)
$template = new VMXTemplate(array(
'root' => '.', # директория с шаблонами
'cache_dir' => './cache', # директория для кэширования компилированного кода шаблонов
'print_error' => truefalse, # если true, ошибки компиляции выводятся на STDOUT
'raise_error' => false, # если true, при ошибке компиляции вызывается die()
'log_error' => false, # если true, ошибки компиляции логгируются через error_log()
'reload' => true, # если false, шаблоны будут считываться с диска только 1 раз, и вызовов stat() происходить не будет
'use_utf8' => true, # если true, использовать кодировку UTF-8 для строковых операций
'end_code' => '-->', # маркер конца директивы кода
'eat_code_line' => true, # (похоже на TT CHOMP) съедать "лишний" перевод строки, если в строке только директива?
'begin_subst' => '{', # маркер начала подстановки выражения(либо {, либо false) 'end_subst' => '}', # маркер конца подстановки выражения(либо }, либо false)
'compiletime_functions' => # дополнительные компилируемые функции
array('func' => callback), # хеш вида имя функции (в шаблонах) => callback($template, array $args),
# которому передаются скомпилированные выражения всех аргументов
'strip_space' => true, # немного legacy, устаревшеевстроенный фильтр:срезание пробелов из начал и концов строк 'wrapperfilters' => NULL, # если равно чемуфильтры -то, что можно вызвать, через это будет # пропущен вывод выполняются над выводом всех шаблонов array("глобальный фильтр"callback1, ...), 'strict_endauto_escape' => false, # требовать <!функция авто-экранирования, например "s" (для HTML- END имя_блока --> после <!-- BEGIN имя_блока -->безопасного режима)
));
# Очистка сохранённых данных для генерации ещё одной страницы:
$template->clear;
</sourcecode-php>
=== Perl ===
Perl версия обновлена и теперь в точности соответствует PHP-версии.
<source lang="perl">
use VMX::TemplateVMXTemplate;
# Конструктор
$template = new VMX::TemplateVMXTemplate(
'root' => '.', # директория с шаблонами
'cache_dir' => undef, # директория для кэширования компилированного кода шаблонов
# если >0, то шаблоны будут перечитываться с диска не чаще чем раз в reload секунд
'print_error' => 1, # если TRUE, ошибки компиляции попадают в вывод шаблона
'log_error' => 1, # если TRUE, ошибки компиляции печатаются на STDERR
'raise_error' => 0, # если TRUE, при ошибке компиляции вызывается die()
'use_utf8' => undef, # если TRUE, использовать "use utf8" на шаблонах
'begin_code' => '<!--', # маркер начала директивы кода
'end_code' => '-->', # маркер конца директивы кода
'begin_subst' => '{', # маркер начала подстановки выражения
'end_subst' => '}', # маркер конца подстановки выражения
'eat_code_line' => 1, # (похоже на TT CHOMP) если TRUE, съедать "лишний" перевод строки, если в строке только директива кода (begin_code..end_code)
'no_code_subst' => 1, # если TRUE, выполнять директивы кода (begin_code..end_code), но игнорировать их результат
'compiletime_functions' => # дополнительные компилируемые функции
{ 'func' => sub {} }, # хеш вида имя функции (в шаблонах) => coderef,
# которому передаются скомпилированные выражения всех аргументов и первым - сам $templateпарсер (объект VMXTemplate::Parser) 'filters' => [ sub {}, .. ], # немного legacyфильтры для запуска на выводе каждого внешнего шаблона (фильтр - функция, устаревшее:модифицирующая $_[0]) 'wrapperstrip_space' => undef0, # если coderefTRUE, через это будет пропущен вывод удалять пробелы и табы из начала и конца всех шаблонов ("глобальный фильтр")строк вывода 'strict_endauto_escape' => 0'', # требовать <!функция авто-экранирования, например "s" (для HTML- END имя_блока --> после <!-- BEGIN имя_блока -->безопасного режима)
);
# Выполнения полностью аналогичны PHP:
</source>
=== Различия =Синтаксис шаблонов ==
Шаблон — любой текст (типично — HTML), в который ''местами'Кэширование работает по-разному' включены директивы и/или подстановки.'''
* Perl: считаетсяГолая Bison-грамматика, что всё прогрессивное человечество уже давно использует <tt>mod_perl<для получения представления о синтаксисе: http:/tt> или /yourcmc.ru/git/vitalif/VMXTemplate/raw/master/template.y* Рабочая [[Платформы для запуска Perl веб-приложений|другие способы запуска веб-приложений]http://github.com/vitalif/lime LIME], при которых частых переинициализаций интерпретатора не происходит-грамматика: http://yourcmc. Иными словами, ''никто больше не использует CGI''ru/git/vitalif/VMXTemplate/raw/master/template. Таким образом, мы смело можем сохранить живой coderef lime (ссылку на функцию, или кому как больше нравится — анонимную функцию, замыкание, делегат) в промежутке между двумя запросамидля её работы нужен патченый LIME — см.https://github.com/vitalif/lime)* PHPРабочая {{CPAN|Parse:: интерпретатор PHP инициализируется заново при обработке каждого HTTPYapp}}-запросаграмматика: http://yourcmc. А живой coderef в промежутке между двумя инициализациями интерпретатора сохранить, видимо, невозможноru/git/vitalif/VMXTemplate/raw/master/template.yp
<pre>
<!-- FOR var = expression SET title --> Статистика<!-- END --><!-- SET headscripts --> <script language="javascript" type="text/javascript" src="{DOMAIN}/tpldata/jquery.min..js"></script>
<!-- END -->
<!-- INCLUDE "admin_header.tpl" -->
<!-- IF NOT srcid -->
<p>Добро пожаловать в простую OLAPообразную статистику. Выберите источник данных:</p>
<form action="?" method="GET">
<select style="width:100px" name="datasource">
<!-- FOR s = sources -->
<option value="{s s.id}">{s s.name}</option>
<!-- END -->
</select>
<input type="submit" value=" Продолжить " />
</form>
<!-- ELSEIF srcid == "test" || sources[srcid].mode == 'test' -->
<p>Тестовый режим.</p>
<!-- END -->
<!-- INCLUDE "admin_footer.tpl" -->
</pre>
=== Функции Выражения ===
* Выражения состоят из переменных, операторов и вызовов функций и методов объектов* Синтаксис обращений к переменным JS-подобный: <tt>hash.key</tt>, <tt>array[0]</tt>, <tt>hash['key']</tt>* Имена переменных 'Операторов нет''регистрозависимы''', фильтров нетимена встроенных функций и названия директив (BEGIN, есть END и т. п.) — '''регистронезависимы'''* Никаких ошибок при обращениях к необъявленным переменным не происходит* Компилируемые функции: <tt>function(arg1, arg2, . Пример..)</tt> или <tt>function single_arg</tt> (через пробел с одним аргументом)* Функции, определённые через FUNCTION:<tt>fn_name('arg' => 'value', 'arg2' => 'value2', ...)</tt> или <tt>exec('fn_name', { 'arg' => 'value', 'arg2' => 'value2', ... })</tt>* Включение других шаблонов: <tt>INCLUDE 'template.tpl'</tt>, <tt>INCLUDE('template.tpl', { 'arg' => 'value', ... })</tt>* Выполнение функции из другого шаблона: <tt>exec_from('template.tpl', 'fn_name', { 'arg' => 'value', 'arg2' => 'value2', ... })</tt>* Методы объектов: <tt>var.method(arg1, arg2, ...)</tt>
Синтаксис вызова функции одного аргумента: <pre><!-- function(block.key) --><!-- function block.key -->{block.key/s}{s block.key}</pre> Синтаксис вызова метода объекта: <pre>{object.method()}{object.method(arg1, arg2)}{call(object, "method")}{call(object, "method", array(arg1, arg2))}</pre> Последние два применения — как нетрудно заметить, обращение к функции call() и служат для вызова метода по вычисляемому имени. Цепочки вызовов методов типа <tt>object.method().another_method()</tt> не поддерживаются, ибо к ним без сохранения звеньев нервно относится даже сам PHP. === IF === Условный вывод: <pre><!-- IF function(block.key) --><!-- ELSEIF ... --><!-- END --><!-- IF NOT block.key -->...<!-- END --> </pre> ELSIF эквивалентно ELSE IF и ELSEIF. === SET === Запись значения переменной: <pre><!-- SET block.key -->...<!-- END --></nowiki><!-- SET block.key = выражение --></pre> === Включения === Включение другого шаблона также осталось: <pre><!-- INCLUDE another-file.tpl --><!-- INCLUDE "another-file.tpl" --></pre> По «динамическому» имени шаблона включение производится [[#Включения|функциями включения]]. Как несложно заметить, вторая строка — как раз вызов функции. === Блоки === Блок — это часть шаблона, выделенная в отдельную «функцию», хорошо кэшируемая и предназначенная для повторного вызова из других мест. Покрывает сразу несколько вещей — «блоки», «макросы» и «обёртки» из TT. Да-да, TT славится бессмысленным дублированием функционала. Наши блоки имеют несколько преимуществфункций:* блоки, определённые в одном шаблоне, можно смело вызывать из других по имени файла + имени блока!* блок можно определить просто как некоторое выражение.* блоки хорошо кэшируются — с VMX::Template вы не испытаете разочарования, если вызовете какой-нибудь блок 1000 раз. В отличие от TT. Блоки в шаблоне не могут быть вложенными, а циклы, SET и прочие вещи, их оборачивающие, не имеют на них никакого влияния. После компиляции блоки просто вырезаются и преобразуются в отдельные функции PHP/Perl’а. <pre><!-- BLOCK имя_блока -->...код...<!-- END --></pre> или <pre><!-- BLOCK имя_блока = выражение --></pre> Вместо слова <tt>BLOCK</tt> можно также использовать слово <tt>FUNCTION</tt> или <tt>MACRO</tt>. Вызывать блок из шаблона следует с помощью функций [[#EXEC|EXEC]], [[#EXEC_FROM|EXEC_FROM]]. Вызывать блок из кода следует с помощью функций [[#EXEC_FROM_INLINE]]. Также см. [[#Использование]]. == Функции == Функции используются в выражениях как <tt>ФУНКЦИЯ(АРГУМЕНТ, АРГУМЕНТ, ...)</tt> или как * <tt>ФУНКЦИЯ (<пробел) > ОДИН_АРГУМЕНТ</tt>. Вместо запятой ", " можно также использовать «=>», например <tt>HASH(КЛЮЧ => ЗНАЧЕНИЕ, КЛЮЧ => ЗНАЧЕНИЕ)</tt>. Синтаксической разницы между ", " и «=>» никакой нет.
Существующие функции перечислены ниже. Через «=» в подзаголовках указываются синонимы функций.
* I() — преобразует значение к целому числу.
=== Числа, логические операции = HTML-безопасный режим ====
==== LOG ====
Преобразование к целому числу.
==== SEQ, SNE, SGT, SLT, SGE, SLE ====
==== NEQ, NNE, NGT, NLT, NGE, NLE ====
==== YESNO ====
=== Строки ===
==== Q=QUOTE=ADDSLASHES, SQ=SQL_QUOTE, REQUOTE=RE_QUOTE=PREG_QUOTE ====
* Экранирование символов " ' \ и перевода строки бэкслэшем — quote(строка). * Экранирование символа " удвоением — sql_quote(строка). (актуально также для [[rupedia:CSV|CSV]]) * Экранирование символов, являющихся специальными в регулярных выражениях — re_quote(строка). (см. [http://perldoc.perl.org/perlre.html perldoc perlre]).
==== URI_QUOTE=URIQUOTE=URLENCODE ====
==== REPLACE, STR_REPLACE ====
* Замена Perl- (соответственно PCRE- в PHP-версии) регулярного выражения в строке — replace(RegExp, замена, строка). * Замена подстроки в строке — str_replace(искомое, замена, строка).
==== STRLEN ====
==== S=HTML=HTMLSPECIALCHARS, T=STRIP, H=STRIP_UNSAFE, NL2BR ====
* Преобразование символов < > & " ' в HTML-сущности. * Удаление всех [[lib:HTML|HTML]]/[[lib:XML|XML]] тегов. * Удаление только «небезопасных» HTML-тегов. * Преобразование переводов строк (\n) в HTML-тег <tt><nowiki><br /></nowiki></tt>.
==== CONCAT, JOIN=IMPLODE ====
* Конкатенация всех своих аргументов — concat(аргументы). Конкатенирует также все элементы всех переданных массивов. * Конкатенация элементов массива через разделитель — join(строка, аргументы). Конкатенирует также все элементы всех переданных массивов.
==== SUBST, SPRINTF, STRFTIME ====
Sprintf — он и в Африке [http://perldoc.perl.org/functions/sprintf.html sprintf].
==== STRLIMIT=TRUNCATE ====
Ограничение длины строки <tt>str</tt> максимальной длиной <tt>len</tt> — <tt>strlimit(str, len, dots = "...")</tt>. Если строка превышает заданную длину, она обрезается предпочтительно по пробелу или Tab’у, а в конец добавляется добавляется <tt>dots</tt> или по умолчанию <tt>"..."</tt>, если аргумент <tt>dots</tt> не передаётся. ==== PLURAL_RU ==== Выбор правильного окончания в русском языке в зависимости от количества: <tt>plural_ru(число, один, несколько, много)</tt>. Например (1 шаблон, 2-3-4-102 шаблонА, 5-6-15-… шаблонОВ): <tt><nowiki>{num} шаблон{plural_ru(num, '', 'а', 'ов')}</nowiki></tt>
=== Массивы и хеши ===
Сортировка массива по значениям.
==== EACH PAIRS ====
Массив хэшей вида <tt>{ id key => ключ, name value => значение }</tt> для хэша, в случае Perl ключи будут отсортированы по имени.
==== ARRAY, RANGE ====
* Создание массива. * Диапазон от A до B — range(A, B).
==== IS_ARRAY ====
==== COUNT, SUBARRAY=ARRAY_SLICE, SUBARRAY_DIVMOD ====
* Количество элементов массива, или 0, если аргумент — не массив — count(аргумент).* Аналог функции [http://php.net/manual/en/function.array-slice.php array_slice] из PHP.* Выбор из массива каждого div’того элемента, начиная с номера mod или нуля по умолчанию — subarray_divmod(массив, div, mod).
<tt>GET(что)</tt> — получение значения переменной верхнего уровня.
==== SET ====
==== SHIFT, POP, UNSHIFT, PUSH ====
* Вынуть элемент из начала массива, вынуть массива — <tt>shift(array)</tt>* Вынуть из конца, добавить конца — <tt>pop(array)</tt>* Добавить в начало — <tt>unshift(array, value)</tt>, добавить * Добавить в конец — <tt>push(array, value)</tt>.
=== Включения ===
Сюда относятся функции выполнения других шаблонов и/или их блоков. Во все эти функции можно передавать «данные» (tpldata) либо с помощью создания хеша функцией hash(), либо просто передачей аргументов как <tt>КЛЮЧ => ЗНАЧЕНИЕ, ...</tt>.
==== PARSE=INCLUDE=PROCESS ====
==== EXEC_FROM_INLINE ====
=== Прочее ===
Вычислить аргумент и вернуть пустую строку. Потенциально нужно для игнорирования результата, ибо все возвращаемые значения радостно подставляются в выходной поток.
==== RAW ====
Пустое преобразование первого аргумента со снятием флага «небезопасности». Нужно для подстановки значений «как есть» в HTML-безопасном режиме авто-экранирования.
==== DUMP=VAR_DUMP ====
Применение функции, имя которой передано как первый аргумент, ко всем переданным аргументам и элементам всех переданных массивов — map(«имя_функции», аргументы).
== Изменения относительно старых версий == * Ликвидированы assign_vars(), assign_block_vars(), tr_assign_vars() — теперь, как и обычно, передаётся просто хеш с данными $vars* Синтаксис типа {a->key} ликвидирован* Авто-переводы, которые были в перловой версии — тоже тю-тю (хотя, может, и будут возрождены)* SET теперь заканчивается обычным END, а не ENDSET* Обращение к счётчику цикла теперь {block_index}, а не {block.#}* Добавлены функции, операторы* Добавлены детальные сообщения об ошибках* Добавлен встроенный фильтр для ликвидации пробелов из начал/концов каждой строки шаблона == Различия PHP и Perl версий == ==== Кэширование работает по-разному ==== В целом, общий смысл — сделать так, чтобы шаблоны было не стыдно вызывать много раз, как много раз за один запрос, так и в целом, при этом максимально использовать механизмы интерпретатора самого языка. Но механизмы для этого применяются разные. Основная причина различий следующая: * Perl: считается, что всё прогрессивное человечество уже давно использует <tt>mod_perl</tt> или [[Категория:SwayПлатформы для запуска Perl веб-приложений|другие способы запуска веб-приложений]], при которых частых переинициализаций интерпретатора не происходит. Иными словами, ''никто больше не использует CGI''. Таким образом, мы легко можем сохранить живой coderef (ссылку на функцию, или кому как больше нравится — анонимную функцию, замыкание, делегат) в промежутке между двумя запросами. Так и живём — скомпилированный шаблон представляет собой просто хеш с набором анонимных функций, которые сохраняются в ''экземпляре объекта VMXTemplate'' и вызываются при обращении к шаблону или его блокам. Также существует и файловый кэш компилированного кода. '''Важное следствие:''' объект VMXTemplate между запросами нужно оставлять живым. Если его убить — кэш полностью очищается.* PHP: интерпретатор PHP всегда инициализируется заново при обработке каждого HTTP-запроса, а живой coderef в промежутке между двумя инициализациями интерпретатора сохранить, видимо, невозможно. Однако предполагается, что всё прогрессивное человечество давно использует APC/XCache/ZendOpCache/eAccelerator, и поэтому, когда текст шаблонов компилируется в файлы, а файлы подгружаются путём require, на самом деле они загружаются не с диска, а из памяти кэшера, причём — в уже скомпилированном виде. Кроме того, так как скомпилированный шаблон представляет собой класс — в рамках одного запроса он загружается максимум 1 раз, последующие вызовы происходят уже очень быстро. Ну и на всякий пожарный — хотя это, возможно, уже особого выигрыша и не даёт — нескомпилированный текст шаблонов тоже кэшируется в кэше переменных APC/XCache/eAccelerator, если таковой присутствует, и не перезагружается с диска лишний раз. Если <tt>reload = false</tt>, лишними считаются все разы, кроме первого, даже если файл шаблона менялся. В Perl действие <tt>reload</tt> немного отличается — <tt>reload = 0</tt> работает так же, как <tt>reload = false</tt> в PHP, но если <tt>reload > 0</tt>, то тексты шаблонов всё-таки перезагружаются с диска при изменении, но не чаще, чем раз в <tt>reload</tt> секунд. В PHP также есть ещё одна проблема — в процессе выполнения невозможно добавить метод в класс без использования извращений типа [http://pecl.php.net/package/classkit classkit], а хочется, потому что сгенерированные из кода шаблона функции должны быть методами — они дёргают разные функции от $this, подразумевая, что это объект класса VMXTemplate. Поэтому компилированный шаблон PHP-версии — это класс, производный от класса VMXTemplate. Как уже сказано выше, единожды за один HTTP-запрос он загружается в память, а при каждом вызове шаблона создаётся пустой объект этого класса, в него записывается ссылка на <tt>tpldata</tt> и поле <tt>parent</tt>, ссылающееся на родительский объект Template, и вызывается метод класса, соответствующий функции шаблона (<!-- FUNCTION … -->). ==== Несколько различается действие <tt>use_utf8 = true</tt> ==== * Общий смысл — «мои шаблоны и страницы в кодировке UTF-8».* PHP: «использовать mb_str* функции для работы со строками в выражениях».* Perl: «я передаю в шаблон все переменные с флагом UTF-8 = On, их можно смело конкатенировать с UTF-ными частями шаблона». Если кто-то не знает, в Perl строки имеют на себе флаг UTF-8 = да или нет, и при конкатенации строки без флага со строкой с флагом строка без флага будет автоматически переведена в UTF-8 из кодировки, соответствующей текущей локали. Что означает двойное UTF-8-кодирование в случае, если строка на самом деле всё-таки в UTF-8, но просто на ней не установлен флаг.*: Для приведения всех переменных шаблона к UTF-8 можно использовать функцию <tt>utf8on()</tt> из <tt>VMXTemplate::Utils</tt> (рекурсивный <tt>Encode::_utf8_on()</tt>). ==== Различается способ вывода ошибок при <tt>print_error = true</tt> ==== * Общий смысл — при <tt>print_error = true</tt> ошибки и предупреждения должны попасть на экран.* PHP: ошибки группируются и выводятся в отдельном div’е в конце страницы, либо просто print’ами по месту возникновения при вызове из консоли.* Perl: текст ошибок прицепляется к выводу шаблонизатора (возвращается вместе с результатом <tt>parse()</tt>). ==== Различается поведение сравнений ==== * PHP: Тип обычных операторов сравнения определяется во время выполнения. То есть, если во время выполнения одно из сравниваемых значений — число, они сравниваются как числа, иначе — как строки.* Perl: Тип обычных операторов сравнения определяется ''во время компиляции''. То есть, если из контекста понятно, что одно из сравниваемых значений — число (если это константа или результат, например, функции count), сравнение будет численным, иначе — строковым.* Пустые массивы и хеши ложны в PHP и истинны в Perl. То есть простая проверка «IF array» (приведение к булеву типу), если array пуст, в PHP вернёт false, а в Perl — true. ==== Различается поведение некоторых функций работы с массивами и хешами ==== * KEYS — в PHP порядок ключей массива/хеша сохраняется, а в Perl — нет и принимаются только хеши. Обусловлено реализацией хешей в этих языках.* PAIRS — в PHP порядок ключей сохраняется, в Perl-версии ключи будут отсортированы по имени.* RANGE — в Perl-версии принимает буквенные аргументы (A..Z = весь алфавит).* IS_ARRAY — в PHP-версии не проверяется, а не является ли он при этом хэшем, ибо трудоёмко (надо проверить, численные ли все ключи).* AGET и HGET в PHP идентичны GET.* ARRAY_MERGE: под Perl — только массивы (не хеши), под PHP — любые массивы.* DUMP — это Dumper в Perl’е и var_dump в PHP. ==== Строка исходного файла ==== В PHP-версии в шаблоны не включаются C-подобные «прагмы» #line, а в текст ошибок не включается имя файла шаблона и строка. Ибо решил — раз уж #line не поддерживается, нечего на строки заморачиваться. == А кстати, зачем вообще нужен шаблонизатор? == Ответы:* Чтобы структурировать код, осознанно используя для генерации HTML-ек язык с ограниченными возможностями. Так как возможности ограничены, сложные вычисления писать на нём автоматически не хочется, соответственно, они перемещаются в логику, разделение становится более явным.* Чтобы структурировать выполнение — сначала логика, потом HTML. В идеале «обратной связи» из шаблонов в логику быть не должно, то есть шаблону должно передаваться ровно столько данных, сколько ему нужно, чтобы в процессе выполнения он ничего не дочитывал. Это приблизительно называется MVP (Model-View-Presenter; View имеет связь с моделью только через Presenter) и сразу же ликвидирует:** Трудноуловимые проблемы производительности, происходящие по вине ленивых вычислений и вызовов методов модели, дочитывающих данные из БД, из view.** Проблемы с преждевременной отправкой HTTP-заголовков, после которой внезапно обнаруживается, что, оказывается, нужно было сделать редирект.* Второй вариант — классический MVC, шаблон — это View (представление), во View передаётся модель, и View отображает состояние модели так, как ему хочется. То есть, шаблон общается напрямую с живыми объектами модели, которые ему передают, «обратная связь» присутствует.*: {{Warning}} Важное ИМХО! Частая проблема классического подхода — по 100 запросов для чтения одного и того же свойства при отображении 100 объектов выборки, вместо того, чтобы прочитать это свойство за 1 запрос сразу для всех 100 объектов. Чтобы такого не было — нужно, чтобы объекты помнили, частью какой коллекции они являются, и при чтении свойства читали его сразу для всех объектов «своей» коллекции. Идея основана на предположении, что если у объекта, прочитанного из БД как часть большой выборки, запрашивается какое-то свойство — велика вероятность того, что это же свойство будет запрошено и у всех остальных объектов той же самой выборки. При такой реализации — и писать удобно (не нужно заморачиваться, что передавать в шаблон, а что нет), и производительность не страдает. Примечание: так как PHP — «язык наизнанку», сам немножко являющийся шаблонизатором, то при выполнении следующих требований можно писать и без шаблонизатора:* ''Руками'' писать в «шаблонном стиле»:*# Не смешивать сложные конструкции с HTML.*# Шаблоны выносить в отдельные функции и общаться с ними через 1 ассоциативный массив с данными.*# Вывод не печатать, а буферизовать (ob_start() → ob_get_contents() → ob_end_clean()) и возвращать.* Использовать читаемый стиль кода (?> и <?php только в конце строк, отступы насквозь через PHP и HTML).Это бывает весьма полезно, если нужно написать модуль к системе, которая сама написана без шаблонизатора или с каким-нибудь полу-кривым собственным, и не хочется вводить дополнительную зависимость. [[Категория:РазработкаТехактивы]]
[[Категория:Perl]]
[[Категория:PHP]]