23 ноября 2008 в 18:45

MACRO — гибкий PHP шаблонизатор, с человеческим «лицом»

PHP*
Раз уж сегодня на хабре день РНР шаблонизаторов, то не могу не рассказать о MACRO — наиболее гибком шаблонизаторе с читаемыми шаблонами, среди известных мне.

Причина его создания проста — используемый нами в то время WACT, становился все более монструозным. Подробнее о причинах и первоначальной идее можно прочитать у нас на форуме.

Основные «фишки»


Гибкость


Внутри можно легко использовать обычные php-вставки, а сам шаблонизатор содержит очень небольшой набор правил своего использования. Это дает нам очень гибкий инструмент, с поддержкой, как pull, так и push доступа к данным.

Высокий реюз шаблонов


Мощные средства для компоновки шаблонов: обворачивание(wrap), включение(include), переиспользование(apply) в рамках одного и того же шаблона. Примеры шаблонной магии можно посмотреть в вики проекта. Благодаря использованию MACRO, нам удалось полностью избавиться от дублирования в шаблонах.

Скорость


Код первоначального шаблона сначала переводится в готовый к запуску php-скрипт, причем шаблон компилируется целиком, поэтому композиция (разбиение шаблона на части, обворачивание и т.д. ) почти никак не влияет на скорость исполнения шаблона, так как он собирается в одно целое(класс) при компиляции, а область видимости(контекст) организуется с помощью методов скомпилированного класса. То есть большинство «тяжелых» операций либо выполняются на стадии компиляции, либо используют встроенные в РНР средства, что позволило добиться хороших «скоростных» показателей.

Расширяемость


Легкость добавления своих тегов и своих фильтров. А также возможность создавать целые пакеты со своими тегами или фильтрами.

Модифицируемость


Гибкость настраивания и легкость допиливания достигается за счет низкого связанности компонентов и адекватного ООП.

Читаемость


Шаблоны, несмотря на всю гибкость, нормально читаются непрограммистами, после их неглубокого зомбирования.

Немного дегтя


MACRO по сути является syntactic sugar для нативного РНР. И если вы не платите верстальщику зарплату уже три месяца, то он вполне может загубить весь сайт, имея доступ только к шаблонам.

Show me the code!


Пример шаблона:
{{insert into="content_zone" file="page.phtml"}}
<img src={$#photo.largeFileUrl} />
<dl>
<dt>Автор:</dt><dd>{$#photo.member.name}</dd>
<dt>Категория:</dt><dd>{$#photo.category.title}</dd>
<dt>Название:</dt><dd>{$#photo.title}</dd>
<dt>Теги:</dt>
<dd>
{{list using='{$#photo.tags}' as='$tag'}}
 <ul>
 {{list:item}}
  <li>{$tag.title|uppercase}</li>
 {{/list:item}}
 </ul>
 {{list:default}}
 Нет тегов
 {{/list:default}} 
{{/list}}
</dd>
{{insert file="photo/marks.phtml"/}}
</dl>
{{/insert}}


* This source code was highlighted with Source Code Highlighter.


Тэги


include и list, в приведенном примере, являются тэгами. Одни теги могут находиться только внутри других тегов, некоторые теги должны содержать обязательные атрибуты, некоторые атрибуты должны содержать только определенные значения и так далее. Все это проверяется компилятором, и сопровождается подробными описаниями в случае ошибки.

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

Выражения


Выражения (output expressions) используются для вывода каких-либо значений переменных. Выражения в нашем примере, это {$tag.title|uppercase} и {$#photo.largeFileUrl}. Выражения, стоящие в шаблонах — это по сути операции echo. Точкой разделяются части пути до выводимой переменной. Выражение {$tag.title} эквивалентно <?php if(isset($tag['title'])) echo $tag['title']; ?>.

Подробно выражения описаны в разделе "Выражения".

Фильтры


Фильтры используются для модификации/форматирования значений, выводимых в выражениях. Выражения с фильтрами в нашем примере, это {$tag.title|uppercase}. Фильтр uppercase — переводит значения в верхний регистр. По-сути это алиас на php-функцию strtoupper, которая применяется для переменной, указанной в выражении.

Обычно фильтр представляет из себя враппер для какой-нибудь часто используемой функции php. Однако, ничего не мешает создавать свои уникальные фильтры, так как делать это достаточно просто. Например, несколько дней назад мне довелось добавить фильтр склоняющий существительные в зависимости от числа (1 человек, 2 человека и далее).

Немного о скорости


Разрабатывать шаблонизатор с прицелом на высокую скорость не имеет смысла без набора тестов. Если кратко, то на верстке, близкой к «боевой», MACRO медленнее чистого РНР в 1,75 раза, но быстрее ближайшего «человеческого» шаблонизатора (smarty) на треть.

Подробные результаты тестов можно посмотреть(и скачать) на соответствующей странице.

korchasa @korchasa
карма
35,7
рейтинг 0,0
Пользователь
Похожие публикации
Самое читаемое Разработка

Комментарии (96)

  • +5
    Smartyклон?! :) «Show me the code!» похоже на смарти
    • +3
      в целом интересно, хотя давно не использую такого рода шаблонизаторы, именно по тому, что «медленнее чистого РНР».
      • –1
        К сожалению pure PHP шаблоны плохочитаемы.
        • –2
          Абсолютно нормально читаемы. или {} лучше чем <? ?> ?? Это смешно ;)
          • +1
            > или {} лучше чем <? ?>

            {$val} содержит меньше синтаксического мусора, чем <? echo $val; ?>, а уж тем более, чем у <? echo $this->val; ?>

            Когда таких параметров становятся десятки, в одном случае мы видим аккуратный HTML с вставками, в другом — тонну закорючек :)



            Мне, как бы, сейчас приходится и на Smarty шаблоны использовать, и на PHP, благо, фреймворку моему пофиг, какой вид юзать, использование унифицировано. Так вот, Smarty — на голову компактнее и нагляднее. И PHP используется только когда или уж совсем скорость критична, или когда логика очень сложная и 3/4 шаблона — из кода состоит :D



            А так — надо будет сабж прикрутить, оценить. Синтаксис там чуть более громоздкий, чем у Smarty, но терпимый. М.б. некоторые компоненты с большим числом вложений, на него переведу, если эффективен окажется.
            • +3
              Да? а <?=$somthing ?>

              попробуйте — работает ;)
              • –2
                Век живи, век учись, блин :D Спасибо, ловите плюс :)
              • +3
                Насколько я знаю использование этого метода не рекомендуется.
                • 0
                  Совершенно верно.
                • 0
                  кем не рекомендуется?
                  я про этот метод прочёл в документации к Zend Framework
                  • 0
                    framework.zend.com/manual/ru/coding-standard.coding-style.html
                    Раздел: «B.4. Стиль кодирования. B.4.1. Обрамление PHP-кода»
                    Цитата:
                    " PHP-код должен всегда обрамлятся полными PHP-тегами:
                    <?php

                    ?>
                    Короткие теги не допустимы. "
                    • 0
                      эм…
                      вот пример использвоания:
                      framework.zend.com/manual/de/zend.form.quickstart.html#zend.form.quickstart.render

                      в самом низу, лучше поиском "<?="

                      собссно, из этого примера я и узнал о короткой версии тега

                      странно, что в русской версии они пишут о недопустимости, а в немецкой приводят в качестве примера
                      • 0
                        слишком много русских быдлокодеров развелось
                      • 0
                        А посмотрите эту же страницу на английском языке (ему я доверяю больше, чем другим, ибо оф. документация написана на нём) и на том же русском, и обратите внимание, что в том месте где в немецком варианте короткие теги, в анлийском и русском вариантах короткие теги не используются…

                        Offtop… за что минусовали не понял… ссылку же дал на оф. документацию…
                        • 0
                          на русском:
                          ===== begin cut =====
                          … и скрипт вида для отображения формы:

                          <h2>Please login:</h2>
                          <?= $this->form ?>

                          Как вы наверное заметили, код контроллера не является полным
                          ===== end cut =====

                          на английском:
                          ===== begin cut =====
                          And a view script for displaying the form:

                          <h2>Please login:</h2>
                          <?= $this->form ?>

                          As you'll note from the controller code, there's more work to do
                          ===== end cut =====
            • +1
              Если уж делать шаблонизатор не нативный — то, по моему мнению, лучше делать это на том, что для этого предназначено — на XSLT, ан егородить велосипед. А если хочется чегото простого — то тогда юзать натив PHP
              • 0
                У XSLT синтаксис ещё страшнее :) А так — да, никто не мешает и его использовать.
            • –1
              pure PHP себя отлично показывает и быстро и наглядно (все зависит от того как оформлять), хорошо подходит альтернативный синтаксис структур управления

              и также для других команд, да и расширяемость лучше, главное не путать бизнес логику и отображение.
              • –1
                он не альтернативный, он — устаревший
          • 0
            pastebin.com/f6f42f82b
            • 0
              [input name=«xxx» value="[?=htmlspecialchars($xxx);?]" /]
              • +1
                а с переводами строк что делать? а с кавычками? =)
                впрочем, проблема не только в этом, а ещё и в том, что шаблон получается невалидным xml => его труднее воспринимать, а подсветка синтаксиса сходит с ума…
                • 0
                  >а с переводами строк что делать? а с кавычками? =)

                  ?

                  php -r 'echo htmlspecialchars("qqq\"qqq\nqqq");'
                  qqq"qqq
                  qqq
                  


                  >а ещё и в том, что шаблон получается невалидным xml => его труднее воспринимать

                  Хм. Как валидность xml влияет на восприятие? :) А в браузере уже всё валидно будет.

                  > а подсветка синтаксиса сходит с ума…

                  Редакторы надо нормальные использовать :)
                  • +1
                    ! ну и поедет у тебя вся вёрстка, ибо в аттрибутах кавычки и переводы тоже должны экранироваться, а не только угловые скобочки да амперсандики.

                    угловые скобки рябят в лазах.

                    и какой же редактор понимает инструкции препроцессору внутри аттрибутов?
                    • 0
                      >ну и поедет у тебя вся вёрстка, ибо в аттрибутах кавычки и переводы тоже должны экранироваться, а не только угловые скобочки да амперсандики.

                      Мы, наверное, друг друга недопонимаем.

                      >и какой же редактор понимает инструкции препроцессору внутри аттрибутов?

                      Требуется уточнение:
                      — Откуда вдруг взялся препроцессор, что ты под ним понимаешь?
                      — Что ты понимаешь под «пониманием редактором»? Тут возможно двоякое поведение. Редакторы, типа mcedit в этом случае тупо подсвечивают строку как строку. Редакторы в массе своей, от vim до kdevelop подсвечивают в этом случае в строке php-синтаксис. Что для тебя более правильно? :)
                      • 0
                        pastebin.com/f3e2d5100

                        www.w3.org/TR/REC-xml/#sec-pi

                        правильное — подсвечивать ошибки, в частности: угловые скобки внутри атрибута — грубейшая ошибка.
                        • 0
                          Давай мух отдельно, а котлеты — отдельно.

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

                          Если используешь в качестве шаблона PHP — наверное, ты знаешь, что делаешь. И это, как раз, не тот случай, за который я ратую :) Но даже в этом случае проблема не встаёт. Шаблоны — на то и шаблоны, чтобы использовать их декомпозитивно. И уж на такую фигню, как угловую скобку внутри параметра, можно забить. Всё равно проверять на валидность можно только конечный результат, а не шаблон.
    • 0
      Нет, не клон. Именно из-за того, что WACT стал похходить на smarty и пришлось от него отказаться.
  • НЛО прилетело и опубликовало эту надпись здесь
  • +5
    Лучший шаблонизатор это PHP =)
    • 0
      MACRO потому так и называется, что является сахаром для нативного РНР ;)
    • +2
      перед такими заявлениями определяйте критерий лучшести :)
  • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        И есть ли там какие-нибудь итераторы по массивам? (php-вставки — зло)
        • НЛО прилетело и опубликовало эту надпись здесь
          • +1
            Если работаете только вы — вполне нормально.

            Но верстальщики почему-то начинают бояться шаблона, когда видят в нем php-код, и лезут с кучей вопросов перед тем, как начать что-то делать (это не мой опыт, мне рассказывали).
            • НЛО прилетело и опубликовало эту надпись здесь
              • 0
                Так а я о чем? PHP-вставки в шаблон — зло.

                Или вы не об этом спрашивали?

                Мой первый вопрос был дополнением к вашему, если что :-)
                • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              По опыту — не начинают, если им объяснить, что на php-вставки просто не надо обращать внимания. Расстановка этих вставок — дело программиста.
              • 0
                Просто у нас это только pull-данные:

                <?php $articles = ArticleDao::findForFront(); ?>

                • 0
                  а разве это не работа контроллера?
                  • +1
                    Существуют понятия «активный шаблон» и pull-данные. Очень удобная штука во многих случаях, например, облако тэгов, которое отображается на каждой странице.
                  • +1
                    работа контроллера — получить модели, получить отображения. и рассказать последним о первых (академически).
            • 0
              >верстальщики почему-то начинают бояться шаблона, когда видят в нем php-код, и лезут с кучей вопросов перед тем, как начать что-то делать (это не мой опыт, мне рассказывали)

              Подтверждаю этот опыт личной практикой :)
        • 0
          Да, конечно, тэг лист
      • 0
        Нет, ибо чаще всего это (как и в вашем примере) не уровень представления.
        • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            А можно ссылку на определение «шаблонизатора»? ;)

            xslt — это вполне шаблонизатор. Молоток не перестает быть молотком, если к нему приделать сбоку пистолет.
            • НЛО прилетело и опубликовало эту надпись здесь
              • 0
                Если уж так захотелось:
                <?php $selected_user = $users[$get->userId()] ?>
                {$selected_user.name}
                Я использую macro уже больше года, и не помню, чтобы у меня возникли проблемы с динамическими вложенными ключами. Приведенный код вообще страшен, т.к. если в get не будет userId, или в $users не будет такого элемента, то плохо.
                • НЛО прилетело и опубликовало эту надпись здесь
                  • НЛО прилетело и опубликовало эту надпись здесь
                    • 0
                      Как я написал выше — это не такая частая потребность, чтобы переписывать парсер.
                      • НЛО прилетело и опубликовало эту надпись здесь
                        • 0
                          ну не надо к словам придираться. Не переписывать, а дописывать. Один класс — lmbMacroExpression
                          • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        https://svn.limb-project.com/limb/misc/template_engines_bench/
    • 0
      Все зависит от машинки и немного от сложности шаблона. На моей домашней машине:
      korchasa@korchasa:/www/localhost/korchasa-bench$ cat /proc/cpuinfo
      processor: 0
      vendor_id: AuthenticAMD
      cpu family: 6
      model: 8
      model name: Unknow CPU Type
      stepping: 1
      cpu MHz: 1350.114
      cache size: 256 KB
      с включенным APC, в забандленной версии — 150-160 rps.
  • +1
    Не знаю, привык к Smarty.
  • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
  • НЛО прилетело и опубликовало эту надпись здесь
  • –6
    интересно чо за пидрила всех заплюсовал? гавнодрищ, выйди из тени, морду будем бить.
    • +1
      phpdude, Вы перелогиниться забыли (http://habrahabr.ru/blogs/php/45311/#comment_1144725)
      • НЛО прилетело и опубликовало эту надпись здесь
  • НЛО прилетело и опубликовало эту надпись здесь
  • +1
    Хм. Интересно, думаете, стоит менять в и так тормознутом проекте smarty (который тормозит кроме всего прочего вывод страницы до 1.5 сек) на MACRO? Или только потеря времени?
    • 0
      Smarty ощутимо сильнее тормозит при увеличении вложенности шаблонов, из-за раздельной компиляции, и копировании контекста. Ничего этого у нас нет, потому если проводить тесты на «боевых» шаблонах с вложенностью, хотя бы, в 3-4 уровня, то smarty будет угрюмо плестись в хвосте.
  • 0
    Честно говоря не вижу смысла в использовании шаблонизаторов. Читаемость шаблонов не аргумент (некоторые и среиалайз строки читают на ура).
    Увеличение производительности — тоже не будет при использовании того же eAccelerator.

    По мне так лучше семантическая(наглядная) верстка и php вставки(нативный шаблон).
    • 0
      Читаемость строк это как раз — главный аргумент. Правки в нативном РНР тупо дольше делать. Всякие хэлперы (например, Zend'овские) улучшают читаемость, но это все равно «кашица».
      • 0
        А чем <?=$myVar;?> отличается по читабельности {$myVar}… скобочками?
        • 0
          {$myVar} это не <?=$myVar;?>, а <?=htmlspecialchars($myVar);?>. Но это простой пример. Чем сложнее, тем хуже читать pure PHP:
          {$#book→getAuthor().full_name}

          • 0
            Извиняюсь, сообщение порезалось:

            {$item.title}

            <?=(isset($item['title'])? htmlspecialchars($item['title']): '';?>

            А дальше все хуже и хуже…
          • 0
            Вот хоть убей, но обработкой должен заниматься контроллер а не представление.
            Опять же имхо.
            • 0
              Обработкой чего?
              • 0
                Данных.
                • 0
                  На вкус и цвет. Нам это проще делать в шаблоне, благодаря самому macro, чем раздувать контроллеры.

                  Лучше пусть верстальщик напишет {$text|nl2br}, чем он полезет в контроллер.
            • +2
              должен? с какой стати?
              откуда контроллер будет знать, что для конкретно этого отображения для sanitize будет нужно htmlspecialchars (html, браузер), а для другого — консоль/ncurses не нужно?
              • 0
                Ой зря ты это начал… ;)
                • 0
                  хм… а мне кажется, что не особо-то и попрёшь против этого высказывания… хотя — посмотрим :-)
                  • 0
                    Я? Нет, конечно. Во-первых я всеми двумя руками за минимизацию контроллеров. А во-вторых мы их в CLI не используем.

                    Обычно, если мне нужно и cli и html, то я пишу reporter'ы. Хотя идея с контроллерами интересная.

                    Сейчас как раз пишу генератор моделей/контроллеров/etc. Там то либо command, либо controller. И, в принципе, мне нужно поддерживать оба интерфейса(cli, html). Может и получится объединить их. Пока только не очень понимаю, как абстрагироваться от того, как приходят параметры. Можно конечно все свести к lmbHttpRequest, но как-то это не красиво.
                    • +1
                      да какая разница, CLI был как пример. если показалось слишком отдалено от реальности, то: обычный хтмл для браузера и rss. контроллер один, модель одна, отображения и санитайзы разные.
            • 0
              Обработкой данных должен заниматься контроллер. Но он не должен заниматься шаблонизацией.

              Если в зависимости от условия у тебя то жирным, то курсивом текст выделяться должен, то совать strong или em в контроллер — это несравнимо больший грех, чем if'ы в шаблон :)
        • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            Ну если на прямую то вот:
            <?=(empty($_GET['category'])? 'выберите категорию': strtoupper(htmlspecialchars($_GET['category'])));?>

            А при грамотном MVC это будет так:
            <?=$view->get('category', 'выберите категорию')?>

            зы: пример странный, через get передавать русский текст…
            зы2: strtoupper вообще говоря лишняя функция, это проще и лучше делать через css
            • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          Напишите пример php кода, аналогичный тому, что представлен в статье.
    • НЛО прилетело и опубликовало эту надпись здесь
  • +1
    Я правильно понял — MACRO не является самостоятельным продуктом? И употребить его вне Limb3 будет проблематично, если вообще возможно?
    • 0
      У него ровно две зависимости — пакеты CORE и FS. Если делать меньше, то это уже будет дублирование кода.
  • +3
    Что-то жутко знакомое, противное, казалось бы, уже отжившее свой век встретило меня на странице результатов тестов…
    «Снежинки», — задумчиво пробормотал я и закрыл страничку.
    • 0
      Согласен, но так как на остальной части сайта они было, то пришлось делать и здесь. Обещаю, что завтра они исчезнут навсегда.
  • +1
    Познакомился с MACRO летом этого года; применил его в двух «боевых» проектах, впечатления в-основном положительные: к синтаксису привыкаешь быстро, теги и фильтры достаточно обширны и покрывают значительную часть потребностей (к тому же никто не мешает добавить свои), парсер услужливо подсказывает, где ошибки (незакрытые теги, неверные параметры, некорректная вложенность и прочие), высокая скорость (компилятор на выходе отдает уже native-PHP, отсюда и :)

    Из минусов (имхо): скомпилированные шаблоны валятся в одну кучу, а имена файлов хешируются, что затрудняет работу с ними (к примеру, если надо удалить какой-то конкретный, чтобы он перекомпилировался при обращении); кастомные теги/фильтры со сложной macro-логикой (например, form-тег select с optiongroup) своими силами создавать тяжко, т.к. не описан соответствующий SDK :/ правда компенсируется это весьма оперативным и благожелательным коммьюнити 8)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.