Первод статьи, автором которой является Fabien Potencier, ведущий разработчик и идеолог Symfony. Статья поднимает обсуждение о шаблонизаторах PHP в целом и представляет Twig — быстрый и функциональный шаблонизатор.
Итак, вы считаете что PHP это уже шаблонизатор? Так думал и я… и очень долго. Но недавно я передумал. Несмотря на то что PHP может использоваться как шаблонизатор, его синтаксис ужасен для этих целей.
На протяжении нескольких лет я продвигал наиболее успешные концепции веб-разработки, одной из которых является практика отделения отображения от логики приложения. Под моим влиянием, как ведущего разработчика Symfony, все проекты в Sensio спроектированы с учетом MVC архитектуры. Несомненно, это упрощает работу нескольких человек над одним проектом. Программисты работают над кодом (контроллеры и модель) и веб-дизайнеры (верстальщики) над отображением.
Шаблонизатор должен способствовать выполнению концепции разделения. Язык шаблонизатора должен быть достаточно гибким и мощным чтобы упростить внедрение логики отображения, не дав возможности травмировать логику приложения расширенными возможностями.
И когда я несколько дней назад спросил о лучших и популярных шаблонизаторах на PHP в Твиттере, некоторые, естественно, ответили «сам PHP». Это было вполне предсказуемо, ведь несколько недель назад я бы ответил так же.
Так почему же PHP (уже) не является удобным шаблонизатором?
Почему люди до сих пор думают что PHP это шаблонизатор? По-большей части, потому что PHP начал свое существование как язык-шаблонизатор, но в последнее годы он развивался другим путем. Если вы считаете что PHP до сих пор является шаблонизатором, расскажите мне хотя бы об одном изменении в языке, которое улучшило его как шаблонизатор? Я не могу вспомнить ни одого.
Шаблонизаторы стремительно развивались начиная с 1995 года, когда вышел первый релиз PHP/FI:
И значимым фактом является то, что PHP не поддерживает множество возможностей современных шалонизаторов.
Ниже описано то что я хочу видеть в современном шаблонизаторе:
PHP многословен. Только для того чтобы просто вывести переменную необходимо не менее 14 символов (нет, использование более компактного
И PHP становиться нелепо многословен при необходимости экранировать вывод (да, экранирование данных полученных из небезопасного источника является обязательным)
Сравнити это с двумя примерами написанными на Django:
По больше части это дело вкуса, но шаблонизаторы должны иметь красивые решения частых случаев. Для примера предположим, что необходимо отобразить массив и вывести текст по умолчанию, если этот массив пуст. Это очень распространенный случай, но версия на PHP не очень читаема:
Версия на Django гораздо лучше — спасибо конструкции
PHP развивался с учетом возможности повторного использования кода. Начиная с 5-й версии реализация объектов сделала шаг вперед и, после того как в следующей версии будут поддерживаться типажи, мы получим язык программирования удовлетворяющий наши требования. Я действительно рад всем этим изменения т.к. это помогает мне писать программы лучше, но это не имеет значения когда все что тебе нужно — это написать шаблон.
В Django ввели наследование шаблонов несколько лет назад, пытаясь повторить наследование классов в шаблонах:
Это элегантно, просто для восприятия и действительно мощно. Настолько мощно, что многие шаблонизаторы сейчас поддерживают эту возможность «из-коробки».
Я не говорю что PHP небезопасен. Но я хочу сказать что экранирование переменных в шаблоне это просто кошмар, как и продемонстрировал ранее:
Конечно, вы можете написать свою функции и сделать ее котороче, но это не то о чем я говорю:
Я считаю что безопасность должа быть на уровне по умолчанию, особенно для шаблонов, которые зачастую пишут не программисты, которые не обращают внимания на возможные XSS и CSRF уязвимости.
Насколько я знаю, Symfony был самым первым фреймворком поддерживающим автоматическое экранирование в шаблонах (2006); и все основные фреймворки пошли тем же путем: в Django по умолчанию включено экранирование начиная с версии 1.0, и в Ruby on Rails это тоже появится с версии 3.
Включенное автоматическое экранирование так же означает что аудировать(?) приложение гораздо проще. Взгляните на примеры с отключенным экранированием:
Эта возможность особенно необходима в том случае когда пользователи могу сами редактировать шаблоны сайта (например, из системы управления сайтом). Это не является необходимость, однако бывает важно. Исполнение шаблонов в «песочнице» означает возможность ограничить то что может быть выполенно: методы/функции которые можно вызывать, теги которые можно использовать и т. д.
Ни у PHP, ни у Django нет такого режима, так что продолжайте читать эту статью для дальнейшего объяснения этой секции.
Итак я приступил к поиску шаблонизатора который имел бы все возможности упомянутые выше. Я нашел много разных шаблонизаторов, но ни один из них не удовлетворил все мои нужды. Следующий раздел о некоторых из них, среди которых есть и рекомендованные мне ответами в Твиттер.
Это то что первым пришло в голову. И сам язык шаблонов Django был «навеян» Smarty. Smarty это стандарт де-факто для PHP.
Но у Smarty есть ряд проблем:
Насколько я понимаю, Smarty 3 вот-вот выйдет и достаточно сильно улучшит состояние:
Я протестировал обе версии, но скорость работы достаточно низкая (в конце имеется информация о тестах)
PHPTAL очень приятный проект, использующий синтаксис шаблонов Zope. Очень хорошо спроектирован, имеет большое количество возможностей, но не умеет работать с не-HTML шаблонами, что может стать проблемой, если вы хотите использовать один язык шаблонов также для писем и RSS лент.
Еще я думаю что синтаксис может оказаться непонятным веб-дизайнерам, особенно при использовании расширенных возможностей, таких как наследование шаблонов:
Одним из важных достоинств можно назвать автодополнение в среде разработки и гарантировано «правильно сформированный» HTML код.
Компонент шаблонов в составе eZ Components так же предоставляет очень удачный языком шаблонизатора. Здесь присутвуют почти все возможности шаблонизаторов… даже чресчур, если вы хотите знать мое мнение:
Наследование шаблонов не поддерживается, и меня беспокоит скорость работы. Это самая медленная библиотека что я тестировал, и сильно медленне остальных.
Dwoo — это, прежде всего, интересный проект. Он позиционирует себя как альтернативу Smarty. И их работа стоит внимания:
Dwoo подражает Smarty, но также вносит и свои дополнения, например, наследование шаблонов, и работает гораздо быстрее Smarty.
К сожалению, у Dwoo нет «песочницы» и ядро библиотеки недостаточно гибкое.
Calypso это реализация шаблонизатора Django на PHP. Я упоминаю о нем т.к. он клон Django и на него ссылались в Твиттере. Но сам автор считает что его реализация неудачна.
Когда я приступил к поиску PHP шаблонизатора, я сосредоточился на библиотеках копирующих поведение Django. Спустя несколько часов «гугления» был найден Twig. Его автором является Armin Ronacher, известный по проекту of Jinja (шаблонизатор для Python). Несомненно я испытываю крайнее уважение к Армину за его замечательную работу над Jinja. Twig скорее похож на Jinja, чем на Django, как описано в реализации.
Он написал Twig в 2008 году для платформы блогов Chypr. Но больше не возвращался к разработке и больше занимался разработкой на Python.
Когда я взглянул на код, я сразу понял что это то что я ищу. Главное отличие от Calypso в том что Twig компилирует шаблоны в обычный PHP-код. Я начал использовать эту библиотеку и в конце этой недели спросил у Армина не желает ли он дать своему проекту новую жизнь. Его ответ был полон энтузиазма, и я приступил к изучению кода. Моя версия сильно отличается от версии Армина, но «лексер» и «парсер» практически оригинальные.
Я потратил всего несколько дней на работу над кодом, но я уже горжусь результатом и думаю что пора показывать библиотеку публично. Вчера я написал документацию и сделал простой сайт. Осталось еще много работы: закончить документацию, добавить тесты и PHPDoc; но код уже целостен и функционален:
Несмотря на то что Twig самый функциональный шаблонизатор, он еще и самый быстрый.
В случае запуска с уже скомпилированными шаблонами потребление памяти, конечно, гораздо меньше для всех шаблонизаторов, и Twig использует меньше всех памяти.
Если вы хотите узнать больше, то посетите сайт, также можете принять участие в обсуждении или стать одним из разработчиков подписавшись на список рассылок для разработчиков.
Это мой первый перевод. Не только для хабра, а вообще. Поэтому приветствуются комментарии не только про шаблонизаторы и Twig, но и про сам перевод.
Перевод: Подведение итогов.
-----------------------------------------------------------------------
Итак, вы считаете что PHP это уже шаблонизатор? Так думал и я… и очень долго. Но недавно я передумал. Несмотря на то что PHP может использоваться как шаблонизатор, его синтаксис ужасен для этих целей.
На протяжении нескольких лет я продвигал наиболее успешные концепции веб-разработки, одной из которых является практика отделения отображения от логики приложения. Под моим влиянием, как ведущего разработчика Symfony, все проекты в Sensio спроектированы с учетом MVC архитектуры. Несомненно, это упрощает работу нескольких человек над одним проектом. Программисты работают над кодом (контроллеры и модель) и веб-дизайнеры (верстальщики) над отображением.
Шаблонизатор должен способствовать выполнению концепции разделения. Язык шаблонизатора должен быть достаточно гибким и мощным чтобы упростить внедрение логики отображения, не дав возможности травмировать логику приложения расширенными возможностями.
И когда я несколько дней назад спросил о лучших и популярных шаблонизаторах на PHP в Твиттере, некоторые, естественно, ответили «сам PHP». Это было вполне предсказуемо, ведь несколько недель назад я бы ответил так же.
Так почему же PHP (уже) не является удобным шаблонизатором?
Почему люди до сих пор думают что PHP это шаблонизатор? По-большей части, потому что PHP начал свое существование как язык-шаблонизатор, но в последнее годы он развивался другим путем. Если вы считаете что PHP до сих пор является шаблонизатором, расскажите мне хотя бы об одном изменении в языке, которое улучшило его как шаблонизатор? Я не могу вспомнить ни одого.
Шаблонизаторы стремительно развивались начиная с 1995 года, когда вышел первый релиз PHP/FI:
<!--include /text/header.html--> <!--getenv HTTP_USER_AGENT--> <!--ifsubstr $exec_result Mozilla--> Hey, you are using Netscape!<p> <!--endif--> <!--sql database select * from table where user='$username'--> <!--ifless $numentries 1--> Sorry, that record does not exist<p> <!--endif exit--> Welcome <!--$user-->!<p> You have <!--$index:0--> credits left in your account.<p> <!--include /text/footer.html-->
И значимым фактом является то, что PHP не поддерживает множество возможностей современных шалонизаторов.
Я буду использовать Django как пример современного шаблонизатора в своих примерах по причинам которые вы поймете позже.
Ниже описано то что я хочу видеть в современном шаблонизаторе:
Краткость
PHP многословен. Только для того чтобы просто вывести переменную необходимо не менее 14 символов (нет, использование более компактного
<?=
непреемлемо):<?php echo $var ?>
И PHP становиться нелепо многословен при необходимости экранировать вывод (да, экранирование данных полученных из небезопасного источника является обязательным)
<?php echo htmlspecialchars($var, ENT_QUOTES, 'UTF-8') ?>
Сравнити это с двумя примерами написанными на Django:
{{ var }} {{ var|escape }}
Синтаксис ориентированный на шаблоны
По больше части это дело вкуса, но шаблонизаторы должны иметь красивые решения частых случаев. Для примера предположим, что необходимо отобразить массив и вывести текст по умолчанию, если этот массив пуст. Это очень распространенный случай, но версия на PHP не очень читаема:
<?php if ($items): ?> <?php foreach ($items as $item): ?> * <?php echo $item ?> <?php endforeach; ?> <?php else: ?> No item has been found. <?php endif; ?>
Версия на Django гораздо лучше — спасибо конструкции
else
для тега for
:{% for item in items %} * {{ item }} {% else %} No item has been found. {% endfor %}
Повторное использование
PHP развивался с учетом возможности повторного использования кода. Начиная с 5-й версии реализация объектов сделала шаг вперед и, после того как в следующей версии будут поддерживаться типажи, мы получим язык программирования удовлетворяющий наши требования. Я действительно рад всем этим изменения т.к. это помогает мне писать программы лучше, но это не имеет значения когда все что тебе нужно — это написать шаблон.
В Django ввели наследование шаблонов несколько лет назад, пытаясь повторить наследование классов в шаблонах:
<!-- base.html --> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> {% block head %} <link rel="stylesheet" href="main.css" /> {% endblock %} </head> <body> {% block content %}{% endblock %} </body> </html> <!-- index.html --> {% extends "base.html" %} {% block head %} {{ block.super }} <link rel="stylesheet" href="main.css" /> {% endblock %} {% block content %} Index content {% endblock %}
Это элегантно, просто для восприятия и действительно мощно. Настолько мощно, что многие шаблонизаторы сейчас поддерживают эту возможность «из-коробки».
Безопасность
Я не говорю что PHP небезопасен. Но я хочу сказать что экранирование переменных в шаблоне это просто кошмар, как и продемонстрировал ранее:
<?php echo htmlspecialchars($var, ENT_QUOTES, 'UTF-8') ?>
Конечно, вы можете написать свою функции и сделать ее котороче, но это не то о чем я говорю:
<?php echo e($var) ?>
Я считаю что безопасность должа быть на уровне по умолчанию, особенно для шаблонов, которые зачастую пишут не программисты, которые не обращают внимания на возможные XSS и CSRF уязвимости.
Насколько я знаю, Symfony был самым первым фреймворком поддерживающим автоматическое экранирование в шаблонах (2006); и все основные фреймворки пошли тем же путем: в Django по умолчанию включено экранирование начиная с версии 1.0, и в Ruby on Rails это тоже появится с версии 3.
Включенное автоматическое экранирование так же означает что аудировать(?) приложение гораздо проще. Взгляните на примеры с отключенным экранированием:
{% autoescape off %} {{ object.as_html }} {% endautoescape %} {{ object.as_html|safe }}
Конечно, я в курсе возможных проблем с автоматическим экранированием. Вам все так же нужно быть внимательными при экранировании переменных в JavaScript, но это гораздо проще чем экранировать абсолютно все вручную.
Режим «Песочницы»
Эта возможность особенно необходима в том случае когда пользователи могу сами редактировать шаблоны сайта (например, из системы управления сайтом). Это не является необходимость, однако бывает важно. Исполнение шаблонов в «песочнице» означает возможность ограничить то что может быть выполенно: методы/функции которые можно вызывать, теги которые можно использовать и т. д.
Ни у PHP, ни у Django нет такого режима, так что продолжайте читать эту статью для дальнейшего объяснения этой секции.
Альтернативные шаблонизаторы для PHP
Итак я приступил к поиску шаблонизатора который имел бы все возможности упомянутые выше. Я нашел много разных шаблонизаторов, но ни один из них не удовлетворил все мои нужды. Следующий раздел о некоторых из них, среди которых есть и рекомендованные мне ответами в Твиттер.
На PHP написан миллион шаблонизаторов, я тестировал только самые «популярные». И т. к. я их не использую мной могут быть допущены ошибки использования того или иного шаблонизатора.
Smarty и Smarty 3
Это то что первым пришло в голову. И сам язык шаблонов Django был «навеян» Smarty. Smarty это стандарт де-факто для PHP.
Hello {$name|escape} {section name=item loop=$items} {$items[item]|escape} {/section}
Но у Smarty есть ряд проблем:
- Не объектно-ориентирован
- Нет наследования шаблонов
- Нет «песочницы»
- Нет автоматического экранирования
Насколько я понимаю, Smarty 3 вот-вот выйдет и достаточно сильно улучшит состояние:
- Объектно-ориентированная архитектура
- Автоматическое экранирование
- Наследование шаблонов
Я протестировал обе версии, но скорость работы достаточно низкая (в конце имеется информация о тестах)
PHPTAL
PHPTAL очень приятный проект, использующий синтаксис шаблонов Zope. Очень хорошо спроектирован, имеет большое количество возможностей, но не умеет работать с не-HTML шаблонами, что может стать проблемой, если вы хотите использовать один язык шаблонов также для писем и RSS лент.
<?xml version="1.0"?> <html> <body> Hello <span tal:content="name" /> <ul tal:repeat="item items"> <li tal:content="item"></li> </ul> </body> </html>
Еще я думаю что синтаксис может оказаться непонятным веб-дизайнерам, особенно при использовании расширенных возможностей, таких как наследование шаблонов:
<html metal:use-macro="layout.xml/main"> <body metal:fill-slot="content"> Hello <span tal:content="name" /> <ul tal:repeat="a array"> <li tal:content="a"></li> </ul> </body> </html>
<html metal:define-macro="main"> <metal:block define-slot="content"/> </html>
Одним из важных достоинств можно назвать автодополнение в среде разработки и гарантировано «правильно сформированный» HTML код.
eZ Components Templates
Компонент шаблонов в составе eZ Components так же предоставляет очень удачный языком шаблонизатора. Здесь присутвуют почти все возможности шаблонизаторов… даже чресчур, если вы хотите знать мое мнение:
{use $name} {use $items} Hello {$name} {foreach $items as $item} * {$item} {/foreach}
Наследование шаблонов не поддерживается, и меня беспокоит скорость работы. Это самая медленная библиотека что я тестировал, и сильно медленне остальных.
Dwoo
Dwoo — это, прежде всего, интересный проект. Он позиционирует себя как альтернативу Smarty. И их работа стоит внимания:
<html> <body> {block "content"}{/block} </body> </html>
{extends "layout.tpl"} {block "content"} {include("basic.tpl")} {/block}
Dwoo подражает Smarty, но также вносит и свои дополнения, например, наследование шаблонов, и работает гораздо быстрее Smarty.
К сожалению, у Dwoo нет «песочницы» и ядро библиотеки недостаточно гибкое.
Calypso
Calypso это реализация шаблонизатора Django на PHP. Я упоминаю о нем т.к. он клон Django и на него ссылались в Твиттере. Но сам автор считает что его реализация неудачна.
Twig
Когда я приступил к поиску PHP шаблонизатора, я сосредоточился на библиотеках копирующих поведение Django. Спустя несколько часов «гугления» был найден Twig. Его автором является Armin Ronacher, известный по проекту of Jinja (шаблонизатор для Python). Несомненно я испытываю крайнее уважение к Армину за его замечательную работу над Jinja. Twig скорее похож на Jinja, чем на Django, как описано в реализации.
Он написал Twig в 2008 году для платформы блогов Chypr. Но больше не возвращался к разработке и больше занимался разработкой на Python.
Когда я взглянул на код, я сразу понял что это то что я ищу. Главное отличие от Calypso в том что Twig компилирует шаблоны в обычный PHP-код. Я начал использовать эту библиотеку и в конце этой недели спросил у Армина не желает ли он дать своему проекту новую жизнь. Его ответ был полон энтузиазма, и я приступил к изучению кода. Моя версия сильно отличается от версии Армина, но «лексер» и «парсер» практически оригинальные.
Я потратил всего несколько дней на работу над кодом, но я уже горжусь результатом и думаю что пора показывать библиотеку публично. Вчера я написал документацию и сделал простой сайт. Осталось еще много работы: закончить документацию, добавить тесты и PHPDoc; но код уже целостен и функционален:
- Встроенное наследование шаблонов (шаблоны компилируются как классы)
- Автоматическое экранирование (отсутствуе дополнительное время на запуск — все делается во время компиляции)
- Очень безопасный режим «песочницы» (список допустимых тегов, фильтров и методов которые разрешены в шаблоне)
- Расширяемость: вы можете переписавать все что угодно, даже функции ядра, написав расширение; так же можно маниаулировать AST (Abstract Syntax Tree) (?) перед компиляцией. Используя эти возможности вы можете создат даже свой собственны язык — DSL (Domain Specific Language), ориентированный на ваше приложение.
Несмотря на то что Twig самый функциональный шаблонизатор, он еще и самый быстрый.
Библиотека | Время (сек) | Память (Кб) | Шаблонов в секунду |
---|---|---|---|
Twig | 3 | 1 190 | 3 333 |
PHPTAL | 3.8 | 2 100 | 2 632 |
Dwoo | 6.9 | 1 870 | 1 449 |
Smarty 2 | 12.9 | 2 350 | 775 |
Smarty 3 | 14.9 | 3 230 | 671 |
Calypso | 34.3 | 620 | 292 |
eZ Templates | 53 | 5 850 | 189 |
Я тестировал простые шаблоны содержащие 1 вывод и итерацию трех элементов, декорируя простым макетом. Время — среднее для 10 запусков; состоящее из 1 компиляции и 10 000 отображений (рендеринга). Для библиотек, не поддерживающих наследование был использован шаблон подключающий шапку (header) и подвал (footer) отдельно и, в библиотеках не поддерживающих автоматического экранирования, экранирование было выполнено вручную.
В случае запуска с уже скомпилированными шаблонами потребление памяти, конечно, гораздо меньше для всех шаблонизаторов, и Twig использует меньше всех памяти.
Библиотека | Память без компиляции (Кб) |
---|---|
Twig | 383 |
PHPTAL | 598 |
Dwoo | 1 645 |
Smarty 2 | 1 634 |
Smarty 3 | 1 790 |
Calypso | 614 |
eZ Templates | 2 783 |
Если вы хотите узнать больше, то посетите сайт, также можете принять участие в обсуждении или стать одним из разработчиков подписавшись на список рассылок для разработчиков.
-----------------------------------------------------------------------
Это мой первый перевод. Не только для хабра, а вообще. Поэтому приветствуются комментарии не только про шаблонизаторы и Twig, но и про сам перевод.
Перевод: Подведение итогов.