a Sensio Labs Product

Гибкий, быстрый и безопасный
шаблонизатор для PHP

Репозитарий расширений

Наконец-то сделан репозитарий расширений Twig. В нем уже хранятся такие полезные расширения, как text фильтры, i18n и тэг debug.

Установить расширения очень просто, достаточно обеспечить их автозагрузку.

Запустить тест-кейсы тоже очень просто, так как надо настроить только путь до библиотеки Twig.

Вышел Twig 0.9.9

С релиза 0.9.8 прошло 5 месяцев, и вот готов Twig 0.9.9, было исправлено много ошибок, сделано много улучшений. К тому же, возможно, это последний релиз перед выходом Twig 1.0.

Как всегда, исправлено много ошибок и сделано много фишек для упрощения отладки. Полный список можно посмотреть в списке изменений.

Документация тоже обновлена, в соответствии с изменениями в коде.

В этом топике сообщается только о самых важных изменениях.

Обратная несовместимость

Узлы

Основные изменения затронули интерфейс Node. Введен единый способ описания узлов и атрибутов. Дочерние узлы теперь доступны с помощью getNode() (вместо обращения к свойству) и атрибуты с помощью getAttribute() (вместо обращения к массиву). Переход на новую систему должен быть довольно легким.

Что было переименовано

Некоторые вещи были переименованы для большей ясности и согласованности:

  • Переменная self переименована в _self;

  • Фильтр safe переименован в raw;

  • Фильтр urlencode переименован в url_encode;

  • переименован в Twig_Error_Syntax;

  • Twig_RuntimeError переименован в Twig_Error_Runtime.

Прочее

Так же сделаны несколько мелких изменений:

  • Фильтры odd и even теперь можно использовать по другому (смотрите новый функционал): вместо {{ foo|odd }} писать {{ foo is odd }};

  • Тэг include теперь объединяет переданные переменные с текущими по умолчанию (раньше надо было добавлять ключевое слово only в конец тэга);

  • Удаленна поддержка {{ 1 < i < 3 }} (используйте {{ i > 1 and i < 3 }});

  • Фильтр “in” был удален ({{ a|in(b) }} теперь надо писать {{ a in b }})

  • Все исключения в Twig наследуются от Twig_Error.

Новый функционал

Улучшено экранирование

Один из лучших аспектов Twig - система автоматического экранирования. Увы, раньше реализация была не самая хорошая. После продолжительных споров, Arnaud Le Blanc предложил разработать более эффективное решение. В настоящий момент код основан на его работе и работе помогавших ему людей. Большое им спасибо за их превосходную работу. Дискуссия еще не окончена, если вы хотите в ней поучаствовать.

Тестирование нового функционала

Добавлен новый оператор is. Интересно, лучше ли он обычных выражений? Пример использования:

{% if name is odd %}

{# проверка существует ли переменная (полезно, когда используется режим strict_variables) #}
{% if article is defined %}

{% if loop.index is divisibleby(3) %}

{% if loop.index is not divisibleby(3) %}

В документации есть полный список новинок.

Новые тэги/фильтры/тесты/операторы

Как и в любом другом новом релизе Twig, добавлены новые теги, фильтры и операторы:

  • добавлен оператор ** (возведение в степень);
  • добавлен тэг spaceless;
  • добавлена поддержка {{ 1 not in [2, 3] }} (это проще воспринимать чем {{ not (1 in [2, 3]) }});
  • Добавлены следующие фильтры: constant, trans, replace, json_encode;
  • Добавлена специальная переменная _charset содержащая текущую кодировку;
  • Добавлена специальная переменная _context содержащая текущий контент.

Внутренние улучшения

В течении последних месяцев, была сильно оптимизирована работа Twig.

Наследование шаблонов было переписано с целью лучшей имитации "реальных" классов (блоки теперь можно вызывать напрямую)

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

Вышел Twig 0.9.8

Twig 0.9.8 это технический релиз, исправляющий несколько не очень существенных багов.

Этот релиз обратно не совместим для тэга trans:

[twig]
{# старый синтаксис #}
{% trans count %}...{% plural %}...{% endtrans %}

{# новый синтаксис #}
{% trans %}...{% plural count %}...{% endtrans %}

Полный список исправленных багов:

  • Добавлен способ перевода строки из переменной ({% trans var %})
  • Исправлено поведение тэга trans при включеном расширении Escaper
  • Удален объект Twig_Template из вывода тэга debug
  • Исправлены объекты с определенным __isset()
  • Исправлен тэг set
  • Исправлена проверка типов для метода Twig_Environment::addFilter()

Вышел Twig 0.9.7

Всего месяц назад вышел Twig 0.9.6, а сегодня уже доступен Twig 0.9.7

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

Обратная несовместимость

Тэг set

Синтаксис тэга set изменен для улучшения читабельности

{# до 0.9.7 #}

{% set title as "Title" %}

{# начиная с 0.9.7 #}

{% set title = "Title" %}

Тэг include

Удален атрибут sandboxed тэга include, так как введен новый тэг – sandbox:

{# до 0.9.7 #}

{% include "foo.twig" sandboxed %}

{# начиная с 0.9.7 #}

{% sandbox %}
    {% include "foo.twig" %}
{% endsandbox %}

Тэг include также принимает в качестве аргумента объект класса Twig_Template.

Узлы

Подсистема узлов была подвергнута рефакторингу. Если у вас есть свои собственные тэги, вам необходимо обновить их, используя новый API:

  • Класс Twig_NodeList удален. Если ваш тэг наследует его, просто заменить базовый класс на Twig_Node и удалить методы getNodes() и setNodes().

  • Метод __toString() теперь генерируется автоматически базовым классом Twig_Node. Т.е. вы можете безбоязненно удалить свою реализацию этого метода.

  • Основные изменения касаются конструктора (подробнее можно почитать в описании Twig_Node)

Новые особенности

Динамическое и условное наследование

Уже описано здесь

Тэг sandbox

Как уже упоминалось, доступен новый тэг sandbox:

{% sandbox %}
    {% include "foo.twig" %}
    {% include "bar.twig" %}
{% endsandbox %}

Параметр strict_variables

По умолчанию Twig просто игнорирует неопределенные переменные. Это хорошо для рабочего проекта, но вызывает сложности при отладке. Параметр strict_variables управляет этим поведением Twig, и по умолчанию он отключен. Если же присвоить ему true, неопределенные переменные будут вызывать исключения.

Создавать новые тэги проще

Когда разработчику надо создать новый тэг, ему приходиться разобраться со многими понятиями: токены, потоки токенов, парсеры, компиляторы. Конечно, он может просто скопировать существующий код, но это не очень хорошо. В Twig 0.9.7 добавлен новый высокоуровневый API, облегчающий создание новых тэгов. Это API экспериментальное и может в будущем измениться, кроме того, документация пока не готова.

Как обновить

До обновления внимательно прочитайте топик, так как этот релиз может сломать ваш код. Затем действуйте как обычно:

  • Установите новую версию (через PEAR, SVN, Git или скачав новый архив);

  • Удалите закэшированные шаблоны;

  • Проверьте.

Жизнь на краю: динамическое и условное наследование

Сегодня мы поговорим о наследовании шаблонов. С тех пор как был создан Twig, очень многие интересуются – как делать динамические и/или условные шаблоны.

До версии Twig 0.9.6 включительно, тэг extends мог использоваться используется только со статическим именем шаблона.

{% extends "layout.twig" %}

...

Но что если вы хотите изменять имя макета? Скажем в зависимости от того является запрос AJAX или нет? Это стало возможным в Twig 0.9.7.

Во-первых, можно хранить название макета в переменной и передавать ее в шаблон.

{% extends layout %}

Здесь layout – это строковая переменная, которая содержит название шаблона и ее значение задается в контроллере, в зависимости от заголовка запроса.

Но есть и другой способ. Если вы хотите принять решение о подключаемом макете непосредственно в самом шаблоне, можно сделать следующее:

{% extends is_ajax ? 'layout_ajax.twig' : 'layout.twig' %}

Фактически, именем шаблона может быть любое валидное выражение (как это уже сделано с тэгом include); итак, теперь разрешено такое использование тэга extends:

{% extends 'layout' ~ extension %}

Вместо того, чтобы передавать шаблон как строку, вы можете передать непосредственно сам шаблон Twig_Template:

// {% extends layout %}

$layout = $twig->loadTemplate('some_layout_template.twig');

$twig->display('template.twig', array('layout' => $layout));

Заметьте, что аналогичная возможность добавлена и в тэг include:

// {% include template %}

// {% include template %}

$template = $twig->loadTemplate('some_template.twig');

$twig->display('template.twig', array('template' => $template));

А что под капотом?

Поддержка динамического наследования шаблонов была не простой задачей, так как Twig основывался на наследование классов в PHP. И, конечно, невозможно динамически менять родительский класс в PHP:

$bar = 'SomeBaseClass';

class Foo extends $bar
{
    // ...
}

Итак, Twig не использует больше PHP наследование, а просто имитирует его. Тесты производительности показывают, что скорость работы понижается незначительно. Уверен, что этот дает много возможностей и повышает гибкость приложения.

Если вам интересны подробности в коде – взгляните на лог изменений.

Вышел Twig 0.9.6

Я рад представить вам Twig 0.9.6. Это технический релиз, где исправлены некоторые ошибки и добавлено несколько новых особенностей. До того как выйдет Twig 1.0 осталось два основных тикета.

Кроме исправления ошибок и новых особенностей, есть два примечательных изменения внутри Twig (однако мало важные для пользователей библиотеки): для юнит-тестов используется PHPUnit и код оформлен в соответствии в стандартами PEAR .

Документация обновлена в соответствии со сделанными изменениями новой версии.

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

Обновление

Обратная совместимость соблюдена, обновление от 0.9.5 должно быть безопасным:

  • Установите новую версию (через PEAR, SVN, Git или скачав архив);
  • Удалите закэшированные шаблоны;
  • Проверьте работоспособность.

Исправленные ошибки

Мы исправили несколько ошибок, основные из них:

  • исправлен баг Lexer, когда шаблон имел слишком много текста между или в блоке;
  • исправлен баг Lexer, когда mbstring.func_overload использовалась вместе с mbstring.internal_encoding для кодировок отличных от ASCII;
  • исправлен баг разбора узлов для макросов (теперь макросы разбираются как любой другой узел);
  • исправлен баг – переменные определялись снаружи цикла и получали новое значение в цикле.

Новые особенности

Расширение i18n

Благодаря расширению i18n вы можете использовать различные языки в шаблонах. Это расширение работает только если подключено расширение PHP gettext.

Используйте тэг trans для того, чтобы отметить какая часть шаблона должна быть переведена:

{% trans %}
Hello World!
{% endtrans %}

Вы можете использовать внутри переменные, если это необходимо:

{% trans %}
Hello {{ name }}!
{% endtrans %}

Используйте тэг plural для перевода множественных числительных:

{% trans apple_count %}
Hey {{ name }}, I have one apple.
{% plural %}
Hey {{ name }}, I have {{ count }} apples.
{% endtrans %}

Поддержка __call()

Twig теперь использует __call() для обращения к методам переменных.

Новый алгоритм обращения к методам объекта следующий:

  • получаем все методы объекта $object (используя reflectionObject);
  • проверяем является ли $item в списке методов;
  • проверяем есть ли get$item в списке методов;
  • проверяем определенна ли __call и передаем $item как имя метода.

Тэг set

Тэг set теперь поддерживает расширенный синтаксис. Это весьма удобно, если вам надо сохранить в переменной кусок HTML:

{% set foo %}
  <div id="pagination">
    ...
  </div>
{% endset %}

Фильтр cycle

Фильтр cycle может быть использован для перечисления между элементами массива:

{% for i in 0..10 %}
  {{ ['odd', 'even']|cycle(i) }}
{% endfor %}

Массив может содержать любое число элементов:

{% set fruits as ['apple', 'orange', 'citrus'] %}

{% for i in 0..10 %}
  {{ fruits|cycle(i) }}
{% endfor %}

Документация на Twig переводиться на русский язык