MACRO — гибкий 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) на треть.

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

    Поделиться публикацией
    Похожие публикации
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 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)

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

                                                                          Самое читаемое