Новый шаблонизатор. Прошу КРИТИКУ синтаксиса

Статус
В этой теме нельзя размещать новые ответы.

Mendel

Гуру форума
Регистрация
27 Янв 2008
Сообщения
215
Реакции
65
Написал новый движок шаблонизатора.
Предлагаю пообсуждать пока еще на нем ничего не написано.
Все замечания и пожелания категорически приветствуются!
Суть вопроса: пока под этим модулем ничего не написано, хочу попросить покритиковать синтаксис потому как хоть я его уже написал, но проще сейчас исправить чем потом с этим жить
Надеюсь что на нулледе не будет как на серче оффтопа на тему а нужен ли шаблонизатор. Шаблонизатор есть, суть вопроса - "как сделать его лучше" а не "как от него избавиться"
В прошлый раз на нулледе сказали много полезного (*уже учтено здесь*), давайте и теперь ;)
Введение
Класс обеспечивает работу с шаблонами. Цель - вынести весь HTML код
в отдельные файлы *.tpl которые будут содержать весь дизайн и т.п. Програмируя суть не заморачиваешься о HTML, код проще писать и читать, дизайнеру нет надобности разбираться в PHP коде.
Описание класса
Класс написан по шаблону singleton, т.е. предусматривает существование только одного экземпляра класса.
Экземпляр создается методом init.
$templ = templ::init(каталог_шаблонов, каталог_скинов, режим_шаблонизатора);
Режим_шаблонизатора может быть: file, folder, db
Режим db подразумевает хранение шаблонов в базе данных. На данный момент в стадии разработки. Режим folder подразумевает, что для каждого скина отдельная папка а не файл. Используется редко. В данном режиме нельзя использовать inc в скинах. По умолчанию используется тип file. В этом режиме каждому скину соответствует
файл в папке скинов. Далее описывается работа именно этого режима.
Каталог_скинов это каталог в котором хранятся скины. По умолчанию равен каталогу шаблонов.
Каталог_шаблонов это каталог в котором лежат шаблоны страниц. По умолчанию равен dirname(__FILE__).'/../templ/' т.е. если модуль шаблонизатора расположен в каталоге /include/ и при инициализации шаблонизатора не указать параметров
Код:
$templ = templ::init();
то шаблонизатор будет искать шаблоны и скины в папке /templ/
Метод set добавляет переменную в шаблонизатор.
Код:
$templ->set(имя_переменной,значение);
Метод get используется для чтения переменной из шаблонизатора. Строго говоря желательно отправлять переменную в шаблонизатор когда все манипуляции с ней завершены. Плюс как правило если переменная нужна проще сохранить ее в пространстве переменных самого php но в некоторых случаях удобнее взять ее из шаблонизатора.
Код:
$templ->get(имя_переменной);
Метод do_templ выполняет интерпретацию шаблона и возвращает результат. В отличии от старых версий выводит результат нужно самому. Так сделано потому, что некоторые классы наследующие шаблонизатор выводят html-заголовки и организуют кэширование.
Код:
$templ->go(имя_шаблона,имя_скина);
Имя скина по умолчанию равно template.
Типичная работа с модулем выглядит примерно так:
PHP:
$templ = templ::init(); // инициируем модуль
// собственно логика приложения
$templ->set(имя_переменной1,значение1); // передаем первую переменную
...
$templ->set(имя_переменнойN,значениеN); // передаем N-ную переменную
echo $templ->do_templ(имя_шаблона); // выводим данные с использованием имя_шаблона
Синтаксис языка шаблонов
HTML-код состоит из двух частей - скина и шаблона. Скин это общие для сайта или раздела элементы, а шаблон это часть которая специфична для конкретной страницы. Как правило "скин" содержит дизайн а шаблон структуру данных конкретной страницы.
Работа шаблонизатора начинается с поиска файла соответствующего скина. Если шаблон не указан ищется шаблон с именем template. Если шаблон не найден, то считается что содержимое шаблона равно {BODY}.
В первую очередь в файле скина производится замена конструкций {inc=имя_инклюда} на содержимое соответствующего файла.
Имена подключаемых файлов, как и имена скинов и шаблонов указываются без расширения, которое равно .tpl
Файлы ищутся в папке со скинами. Все несуществующие инклюды считаются равными пустой строке.
Далее шаблонизатор ищет конструкцию {body} и заменяет ее на содержимое файла шаблона. Если шаблон не найден он считается равным пустой строке.
Следующим шагом шаблонизатор заменяет все конструкции {inc=имя_инклюда} на содержимое соответствующих файлов. Эти файлы ищутся уже в папке шаблонов. Все несуществующие инклюды считаются равными пустой строке.
Далее шаблонизатор ищет все конструкции вида {$имя_переменной=значение переменной}, добавляет соответствующие переменные в массив переменных для подстановки и удаляет эти конструкции из шаблона. Это удобно для хранения текстовых констант прямо в шаблоне давая дизайнеру возможность править тексты, или как один из вариантов организации мультиязычности или тайтл хранить в шаблоне, или разместить бокс с "подсказками" в скине, а сами тексты подсказок хранить в шаблонах.
Основные конструкции шаблонизатора
Все конструкции вида {имя_переменной} которые не являются управляющими конструкциями заменяются на значение соответствующей переменной.
Конструкция вида
Код:
{if=имя_переменной}
тело
{/if=имя_переменной}
заменяется на "тело" если значение переменной "имя_переменной" истино, и заменяется на пустую строку если оно ложно.
Конструкция вида
Код:
{if!=имя_переменной}
тело
{/if!=имя_переменной}
в отличии от предидущей заменяется на "тело" если значение переменной "имя_переменной" ЛОЖНО, и заменяется на пустую строку если оно ИСТИННО.
Массивы
Вывод таблиц и прочих массивов обеспечивается следующей конструкцией:
Код:
<array=имя_массива>
тело
</array=имя_массива>
Для каждого элемента массива выводится "тело" с заменой соответствующих переменных. Внутри "тела" массива применимы переменные вида {имя_массива.имя переменной}. Аналогично и условия в условных конструкциях.
Пример php-кода передачи массива:
PHP:
$people=array();
$people[]=array('name'=>'Иван','woman'=>FALSE,'age'=>18);
$people[]=array('name'=>'Петр','woman'=>FALSE,'age'=>36);
$people[]=array('name'=>'Анна','woman'=>TRUE,'age'=>20);
$people[]=array('name'=>'Гена','woman'=>FALSE,'age'=>33);
$people[]=array('name'=>'Лена','woman'=>TRUE,'age'=>21);
$templ->set('people',$people);
код шаблона для вывода этого массива:
Код:
{array=people}
{people.name}, {people.age} лет.
{if=people.woman}Женщина{/if=people.woman}
{if!=people.woman}Мужчина{/if!=people.woman}
{/array=people}
"Волшебные" переменные
В шаблонизаторе есть некоторые предопределенные переменные:
_SELF - адрес страницы откуда вызван скрипт (аналог PHP_SELF) удобна для форм.
В массивах есть такие переменные:
_first - истина если это первая запись в массиве и ложна если НЕ первая.
_last - истинна если последняя запись в массиве и ложна если НЕ последняя.
_odd - истина если запись нечетная по порядку
_even - истина если четная по порядку
Четность/нечетность удобна для того, чтобы можно было выделять разным цветом рядом идущие строки, что улучшает юзабилити интерфейса. Часто не нужно выводить тот или иной тег или надпись если запись первая или последняя но выводить для всех остальных.
Зарезервированные выражения
if=
if!=
array
$
inc
body
_self
_first
_last
_odd
_even
прочие переменные через _
а также аналогичные выражения большими буквами.
HOOK`и шаблонов
HOOK`и это когда в шаблоне делается некая пометка типа "здесь кончается главный блок" или "это начало верхнего меню" или "тут конец левой колонки" и т.п. Когда программист пишет плагин, то он просто указывает что такой-то шаблон нужно вставить вместо того или иного хука. Т.о. при разработке дизайна сайта не обязательно учитывать сразу все плагины. Достаточно расставить в нужных местах хуки, а уже разработчик плагина будет задумываться о том как правильно оформить свой плагин. Далее разработчик плагина хочет в этом месте вывести свой код... к примеру блок тэгов или календарь или рекламную ссылку или любой другой.
Метки хуков в шаблонизаторе имеют такой синтаксис: {#имя_метки}
Программист должен зарегистрировать соответствующий хук примерно таким кодом:
$templ->hook('имя_метки','имя_шаблона');
шаблонизатор встретив метку в шаблоне смотрит есть ли хуки для этой метки.
Если хуков нет, то он просто удаляет метку. Если хуки есть, то он добавляет соответствующие файлы шаблонов вместо метки. Для каждой метки может быть неограниченное количество хуков. Они выводятся в порядке их объявления в программе. Шаблоны хуков берутся в папке шаблонов а не скинов.
Существует также дополнительный синтаксис хуков:
$templ->hook('имя_метки','#код_хука');
т.е. если хук начинается со знака решетки, то вместо того, чтобы искать файл с хуком шаблонизатор просто вставляет все что идет после решетки в шаблон. Рекомендуется использовать такой синтаксис в исключительных случаях, поскольку правила хорошего стиля говорят, что весь html-код должен быть в шаблонах а не в коде, однако в некоторых случаях это является разумным - например когда код вводится в админке и хранится в базе, или когда код настолько прост, что глупо плодить сотни маленьких файлов шаблонов. К примеру код банеров проще выводить напрямую без шаблонов.
Свежая версия описания будет лежать здесь: Для просмотра ссылки Войди или Зарегистрируйся
 
Ряд вопросов возник:

Регистрация обьектов поддерживается?
Как поведет себя клас если ему передать многомерный массив и попытаться обойти его первый элемент?

Код:
<array=имя_массива[индекс]>
тело
</array=имя_массива[индекс]>

Поддерживает ли асоциативные массивы ? Имею ввиду как текущий индекс узнать?

Есть методы для игнорирования "хуков" или их перерегистрации?

Имхо - сыро,очень.
 
Регистрация обьектов поддерживается?
Нет и не нужно. Мне и так уже начинает казаться что по сложности начинаю приблежаться к смарти.
Как поведет себя клас если ему передать многомерный массив и попытаться обойти его первый элемент?
Хороший вопрос :) Вопрос многомерных массивов у меня в туду с 2006 года (еще в старом шаблонизаторе) да пока ни разу на практике не понадобились поэтому ими не занимался. Ответ - незнаю, но думаю что не получится. По крайней мере в том синтаксисе что вы привели не получится точно, а если через точку (таблица.индекс1) то может и будет работать...
Приведите мне пример где это нужно и я проверю на нем и сделаю чтобы работало.
Поддерживает ли асоциативные массивы ? Имею ввиду как текущий индекс узнать?
Никак. Спасибо что напомнили. добавлю волшебные переменные _n _n1 где первая это индекс а вторая номер начиная с единицы (в табличках чаще всего нумеруют с единицы а в массиве оно как правило с нуля)
Есть методы для игнорирования "хуков" или их перерегистрации?
Нет а зачем? Модуль1 зарегистрировал хуку, Модуль2 зарегистрировал ту же хуку - вывелась сначала одна потом другая... а зачем отменять или перерегистрировать? имхо смысла нет. Если есть подскажите зачем - сделать не проблема.
Имхо - сыро,очень.
В чем сырость? Сыровато конечно но не "очень". Было бы не сыро я бы на обсуждение не выставлял бы... когда второй шаблонизатор выставил на обсуждение было уже поздно менять синтаксис - к тому времени уже пять лет как под него писались скрипты и т.п.
Если же сырость в ограниченном функционале так это фича а не баг :)
 
Код:
{array=people}
{people.name}, {people.age} лет.
{if=people.woman}Женщина{/if=people.woman}
{if!=people.woman}Мужчина{/if!=people.woman}
{/array=people}
Smarty подобный синтаксис имхо более читабелен. Вам не кажется, что "{/if=people.woman}" проще писать как {/if} т.к. часто возникает необходимость в более сложных проверках, например:
Код:
{if $a > 10 && $b < 10 && $nebo == 'sinee' }...{/if}
копировать условие дважды будет очень нудно.

Чтобы понять, насколько хорош шаблонизатор - нужно написать на нём несколько шаблонов, сразу всплывут все недостатки и преимущества.
 
Smarty подобный синтаксис имхо более читабелен.
Какой именно момент более удобен? Как по вашему лучше сделать? Тут несколько конструкций указано. Просьба быть чуточку более конкретным. Ведь раз я написал свой вариант, то для меня неочевидны преимущества других решений :) Как говорится глаз замылен.
Вам не кажется, что "{/if=people.woman}" проще писать как {/if}
Возможно проще. Я подумаю об этом. Для меня безусловно проще так как я написал ибо не запутаешься (при вложенных ифах), но я так уже пять лет пишу, так что я не показатель :)
т.к. часто возникает необходимость в более сложных проверках, например:
Код:
{if $a > 10 && $b < 10 && $nebo == 'sinee' }...{/if}
А вот здесь я против категорически. Кесарю кесарево. IF`ы принимают только константы и никаких условий. Реализовать просто, но это задача программиста делать сложные условия, вот пусть он их и делает, а дизайнеру уже передает результат в виде условия типа {if=sinij_krug_v_nebe} это одна из причин отказа от смарти - слишком много лишнего функционала который почти невозможно удалить.
Чтобы понять, насколько хорош шаблонизатор - нужно написать на нём несколько шаблонов, сразу всплывут все недостатки и преимущества.
Согласен, но многое можно учесть еще на этапе обсуждения.
 
Какой именно момент более удобен? Как по вашему лучше сделать? Тут несколько конструкций указано. Просьба быть чуточку более конкретным. Ведь раз я написал свой вариант, то для меня неочевидны преимущества других решений :) Как говорится глаз замылен.
Вы пишете шаблонизатор, Вам его и использовать. Я использую smarty и его синтаксис мне нравится больше, чем предложенный Вами. Т.к. Smarty является самым распространённым шаблонизатором, то Вы можете потратить 10 минут и посмотреть его синтаксис (анализ конкурента, с этого следует начинать разработку). Конкретно мне нравится синтаксис использования переменных {$var}, {$array.a}, {$obj->do()}. Про условные выражения я уже написал.

Я сам был долгое время ярым противником smarty, но потом понял, что моё время дороже и проще взять готовое решение, чем поддерживать собственное, о чём ни капельки не жалею.

Можно продолжить обсуждение, если есть какие-то конкретные вопросы или примеры синтаксиса.

Например, как у Вас обстоят дела с экранированием переменных (анти-xss)?
 
Т.к. Smarty является самым распространённым шаблонизатором, то Вы можете потратить 10 минут и посмотреть его синтаксис (анализ конкурента, с этого следует начинать разработку).
Смотрел. :) Даже писал что-то под него.. не понравилось.
Особенно напрягает доработка чужих скриптов на нем. Все время программист нарушает MVC вынося то что явно к контроллеру относится в шаблон. Ну и много других неприятных моментов.
Сейчас уже и не упомню всего... Давно это было...
Конкретно мне нравится синтаксис использования переменных {$var}, {$array.a}, {$obj->do()}.
Т.е. Вам нравится чтобы в начале был знак бакса? Как по мне так это дело вкуса и строго говоря лишнее усложнение..
Могу разве что сделать недокументированный воркараунд с удалением бакса в начале переменной на случай если человек по ошибке напишет как в смарти (недокументированную потому, что чем синтаксис проще тем лучше, и значит основным будет без бакса).
проще взять готовое решение, чем поддерживать собственное, о чём ни капельки не жалею.
лично для меня проще написать свое чем поддерживать чужое.
Сколько раз я слышал от клиентов слова "а разраб меня отфутболил сказав что это не его дело, разбирайтесь с смарти сами, а я не понимаю о чем это они, помоги плиз...", нафиг-нафиг... :)
Впрочем у каждого свой подход.
как у Вас обстоят дела с экранированием переменных (анти-xss)?
Это не уровень шаблонизатора. В данный момент курю этот вопрос на уровне ядра/вьюва.
Можно продолжить обсуждение, если есть какие-то конкретные вопросы или примеры синтаксиса.
У меня есть сомнения по поводу get, set, go, inc, body,array
также я не уверен по поводу {if!=условия} однако если сделать {if=!условия} то это усложнит шаблоны при парсинге.
Вообще не уверен что правильные слова использовал.
Ну и может еще какие-то волшебные переменные предложите.
А вообще уже и без конкретики здесь несколько дельных советов прозвучало :)
 
Я скажу почему я перешёл на смарти:
1) Популярность, постоянно совершенствуется, фиксятся баги
2) Огромный возможности, легко расширяется
3) Т.к. он популярен, многие знают его синтаксис. Он у меня даже шаблоны email писем парсит, а шаблоны пишут далёкие от программирования люди.

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

Использование "бакса" {$var} в переменных визуально отделяет их от тегов. К примеру, можно объявить переменную {$doit} а можно объявить вызов ф-ции {doit} или {doit}xxx{/doit}. Доллар решает эту проблему.

Раньше я использовал более простой шаблонизатор и каждый раз в контроллерах приходилось делать кучу assign'ов, + надо было заботиться об экранировании переменных. Часто в шаблонах менялась логика и в 90% случаях можно было обойтись правкой шаблона, но приходилось дополнительно делать изменение и в контроллере. Smarty решил эти проблемы, теперь в контроллер залезаю намного реже. Приведу даже пример:

PHP:
$user = new User();
$view->assign( 'user', $user );

шаблон
Код:
Привет, {$user->getName()|escape}

так бы пришлось передать юзернейм отдельно и заэкранировать его. Или в интернет-магазине передаётся объект продукта и в шаблоне иногда нужно использовать цену в raw формате 1500.10 (для подстановки в javascript), а в остальных случаях в отформатированом виде: 1 500 руб. 10 коп.

Ещё пример: поддержка форм в шаблонах что-то вроде
Код:
{form name="myform"}
{input name="x"}
{select name="y"}
{/form}
Сами формы создаются в контроллере и при ошибках валидации подсвечиваются, автозаполняются и т.п.

Если при каждом нестандартном случае Вам придётся править код шаблонизатора - это неправильно.
 
Надеюсь что на нулледе не будет как на серче оффтопа на тему а нужен ли шаблонизатор. Шаблонизатор есть, суть вопроса - "как сделать его лучше" а не "как от него избавиться"
Я дико извиняюсь, но не мог бы ли ты дать ссылочку на ветку на серче, где рассказывают как избавиться от шаблонизатора.
Не хочу тут оффтопить, но реально, сколько ни видел хороших шаблонизаторов - это или простые как угол дома str_replace("{!VAR1}", $arr['var1'], $tpl); или супер-мега навороченные а-ля смарти, с поддержкой циклов, условий, вложенных шаблонов и т.п.

Причем любые обсуждения в стиле "как сделать его лучше" приводят к тому, что шаблонизатор наполняется и наполняется разными фишками, и рано или поздно распухает до полноценного "скрипта в скрипте", в котором дизайнеру уже не легче разбираться, чем в самом php.

С другой стороны, советовать "выкинь все лишнее" тоже нельзя - что ж это будет за шаблонизатор такой, в котором даже цикл спрограммировать нельзя. )) Заклюют, скажут твой шаблонизатор - ***но, не поддерживает ифов.

Очень хочу почитать что умные люди на серче по этому поводу думают. Буду благодарен за ссылочку.

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

ТС просил привести примеры.
Многомерный массив.
Допустим если некий массив
Код:
array('vasa','street','country','state','actions'=>'delete','edit','block','unblock')
Дальше там другие васи,пети,вани и тп.
Задача - показать список и вывести список доступных действий,учитывая что действия могут быть разные и список их создается еще до передачи парсеру.

В вашей системе это нереализуемо ?!

Что касается обьектов, это можно заменить системой плагинов,но у вас плагинов нету,как я понял? А разрабатывать шаблон в таких "зажатых" условиях даже врагу не пожелаешь.
 
Статус
В этой теме нельзя размещать новые ответы.
Назад
Сверху