Twig для верстальщиков#

Этот документ описывает синтаксис и семантику шаблонов. Будет наиболее полезен тем, кто создает Twig шаблоны

Краткий обзор#

Шаблон — это просто текстовой файл. Вы можете генерировать любой текстовый формат (HTML, XML, CSV, LaTeX, и тп.). Шаблон может иметь любое расширение, обычно это .html или .xml.

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

Рассмотрим небольшой пример шаблона, в котором показаны некоторые основы создания шаблонов:

<!DOCTYPE html>
<html>
    <head>
        <title>Мой сайт</title>
    </head>
    <body>
        <ul id="navigation">
        {% for item in navigation %}
            <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
        {% endfor %}
        </ul>

        <h1>Моя статья</h1>
        {{ a_variable }}
    </body>
</html>

Есть два вида разделителей {% ... %} и {{ ... }}. Первый используется для выполнения выражений, например, циклов, второй — для вывода результата выражения в шаблоне.

Интеграция с IDE#

Многие IDE поддерживают подсветку синтаксиса и авто-дополнение для Twig:

Also, TwigFiddle is an online service that allows you to execute Twig templates from a browser; it supports all versions of Twig.

Переменные#

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

Вы можете использовать точку (.) для доступа к атрибутам переменной (методы, свойства объекта, или массивы PHP), или так называемый «индекс» синтаксис ([]):

{{ foo.bar }}
{{ foo['bar'] }}

Примечание

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

Реализация

Рассмотрим что будет, когда Twig ищет foo.bar на уровне PHP:

  • проверяет, что foo это массив, и bar это ключ в массиве;

  • если нет, и foo это объект и bar соответствующие свойство;

  • если нет, и foo это объект и bar соответствующий метод (даже если bar конструктор — use __construct());

  • если нет, и foo это объект, проверяет есть ли метод getBar;

  • если нет, и foo это объект, проверяет есть ли метод isBar;

  • если нет, и foo это объект, проверяет есть ли метод hasBar;

  • если нет, то возвращает значение null.

Twig также поддерживает специальный синтаксис для доступа к элементам в массивах PHP, foo['bar']:

  • проверяет, что foo — массив и bar — существующий в нем ключ;

  • если нет, вернется null.

Если переменная или атрибут не существует, вы получите значение null. когда для параметра strict_variables установлено значение false; в качестве альтернативы, если strict_variables установлен, Twig выдаст ошибку (см. параметры среды).

Примечание

Если вы хотите получить динамический атрибут переменной, используйте attribute функцию вместо этого.

Функция attribute также полезна, когда атрибут содержит специальные символы (например, - которые интерпретируются как оператор минус):

{# эквивалент неработающего foo.data-foo #}
{{ attribute(foo, 'data-foo') }}

Глобальные переменные#

Следующие переменные всегда доступны в шаблонах:

  • _self: ссылается на текущий шаблон;

  • _context: ссылается на текущее окружение;

  • _charset: ссылается на текущую кодировку.

Переменные#

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

{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}

Фильтры#

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

Следующий пример удаляет все HTML-теги из name и преобразует в верхний регистр первый символ каждого слова:

{{ name|striptags|title }}

У фильтров, которые принимают аргументы, есть круглые скобки вокруг аргументов. В этом примере, добавится после list запятая :

{{ list|join(', ') }}

Чтобы применить фильтр к блоку кода — оберните его тегом apply:

{% filter upper %}
    Этот текст будет в верхнем регистре
{% endfilter %}

Перейдите на страницу filters, чтобы узнать больше о встроенных фильтрах.

Функции#

Функции можно вызвать для генерации контента. После названия функции идут фигурные скобки (()) в скобках могут быть аргументы.

Например, функция range возвращает список, содержащий арифметическую прогрессию целых чисел:

{% for i in range(0, 3) %}
    {{ i }},
{% endfor %}

Перейдите на страницу functions, чтобы узнать больше о встроенных функциях.

Именованные аргументов#

{% for i in range(low=1, high=10, step=2) %}
    {{ i }},
{% endfor %}

Использование именованных аргументов делает шаблоны более понятными:

{{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}

{# В сравнении с  #}

{{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}

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

{# Первый аргумент — формат даты, который задан в приложении глобально #}
{{ "now"|date(null, "Europe/Paris") }}

{# Или можно пропустить ``format``, но указать ``timezone`` #}
{{ "now"|date(timezone="Europe/Paris") }}

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

{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}

Совет

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

Управляющие конструкции#

К управляющим конструкциям относится все условные операторы (такие как if/elseif/else), for- циклы, а также блоки. Управляющие конструкции находятся внутри``{% … %}`` блоков.

Например, чтобы отобразить список пользователей users, используется тег for:

<h1>Пользователи</h1>
<ul>
    {% for user in users %}
        <li>{{ user.username|e }}</li>
    {% endfor %}
</ul>

Тег if может быть использован для проверки выражения:

{% if users|length > 0 %}
    <ul>
        {% for user in users %}
            <li>{{ user.username|e }}</li>
        {% endfor %}
    </ul>
{% endif %}

Перейдите на страницу tags, чтобы узнать больше о встроенных тегов.

Комментарии#

Чтобы закомментировать часть шаблона, используйте следующий синтаксис {# ... #}. Это бывает полезно при отладке или добавлении полезной информации для других дизайнеров или себя:

{# примечание: это закомментировано, пока не используется
    {% for user in users %}
        ...
    {% endfor %}
#}

Подключение шаблонов#

Тег include используется для подключения одного шаблона в другой.

{{ include('sidebar.html') }}

По умолчанию включенные шаблоны имеют доступ к тому же контексту, что и шаблон. который включает их. Это означает, что любая переменная, определенная в основном шаблоне также будут доступны во включенном шаблоне:

{% for box in boxes %}
    {{ include('render_box.html') }}
{% endfor %}

Подключенный шаблон render_box.html имеет доступ к переменной box.

Название файла с шаблоном определяется в загрузчике шаблона. Например, \Twig\Loader\FilesystemLoader позволяет получить доступ к другим шаблонам по названию файла. Вы можете получить доступ к шаблонам в подкаталогах, используя слэш /:

{{ include('sections/articles/sidebar.html') }}

Такое поведение зависит от применения вложения Twig.

Наследование шаблонов#

Самая мощная часть Twig — это наследование шаблонов. Наследование шаблонов позволяет вам создать основной «скелет» шаблона, который содержит все элементы вашего сайта и определить теги blocks, которые будут перезаписаны в дочерних шаблонах.

Звучит сложно, на самом деле все просто. Проще всего разобраться с наследованием на примере.

Давайте создадим основной шаблон, base.html, который определит простой «скелет» HTML-документа, который можно использовать для простой страницы с двумя колонками:

<!DOCTYPE html>
<html>
    <head>
        {% block head %}
            <link rel="stylesheet" href="style.css" />
            <title>{% block title %}{% endblock %} - Мой сайт</title>
        {% endblock %}
    </head>
    <body>
        <div id="content">{% block content %}{% endblock %}</div>
        <div id="footer">
            {% block footer %}
                &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
            {% endblock %}
        </div>
    </body>
</html>

В этом примере, тег block определяет четыре блока, которые будут заполнены в дочернем шаблоне. Все теги block говорят шаблонизатору, что они могут быть переопределены в дочернем шаблоне.

Дочерний шаблон может выглядеть следующим образом:

{% extends "base.html" %}

{% block title %}Главная{% endblock %}
{% block head %}
    {{ parent() }}
    <style type="text/css">
        .important { color: #336699; }
    </style>
{% endblock %}
{% block content %}
    <h1>Главная</h1>
    <p class="important">
        Приветствую на своем потрясном сайте!
    </p>
{% endblock %}

Тег extends ключевой здесь. Он сообщает шаблонизатору, что этот шаблон «расширяет» другой шаблон. Когда шаблонизатор обрабатывает шаблон, он первым делом смотрит на родительский шаблон. Тег ``extends`` должен быть первым в шаблоне.

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

Используя функцию parent, можно отобразить содержание родительского блока. Она возвращает первоначальное содержание родительского блока:

{% block sidebar %}
    <h3>Оглавление</h3>
    ...
    {{ parent() }}
{% endblock %}

Совет

Страница документации extends описывает более продвинутые функции, такие как вложенности блоков, область применения, динамическое и условное наследование.

Примечание

Twig также поддерживает множественное наследование с использованием тега use.

Экранирование HTML#

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

По умолчанию в Twig автоматическое экранирование переменных включено.

Стратегию автоматического экранирования можно настроить с помощью autoescape и по умолчанию - `` html``.

Работа с ручным экранированием#

Если ручное экранирование включено, ответственность за безопасность переменных лежит на вас. Что экранировать? Любую переменную, которой вы не доверяете.

Экранирование осуществляется с помощью фильтра escape или e:

{{ user.username|e }}

По умолчанию фильтр escape использует html режим экранирования, но в зависимости от ситуации вы можете использовать любые другие доступные способы экранирования:

{{ user.username|e('js') }}
{{ user.username|e('css') }}
{{ user.username|e('url') }}
{{ user.username|e('html_attr') }}

Работа с автоматическим экранированием#

Вне зависимости от того включено экранирование или нет, вы можете добавить экранирование для блока кода с помощью тега autoescape:

{% autoescape %}
    В этом блоке все будет автоматически экранировано (с помощью HTML режима)
{% endautoescape %}

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

{% autoescape 'js' %}
    В этом блоке все будет автоматически экранировано (с помощью JavaScript режима)
{% endautoescape %}

Экранирование#

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

Самый простой способ для вывода ({{) это использовать следующее выражение:

{{ '{{' }}

Для больших блоков нужно использовать тег verbatim.

Макросы#

Макросы сопоставимы с функциями в обычных языках программирования. Они полезны, когда нужно повторить многократно HTML-код, но не копировать его. Макрос определяется тегом macro.

Операторы#

Twig позволяет использовать операторы везде.

Примечание

Приоритет операторов, сначала идут операторы с наименьшим приоритетом: ?: (ternary operator), b-and, b-xor, b-or, or, and, ==, !=, <=>, <, >, >=, <=, in, matches, starts with, ends with, .., +, -, ~, *, /, //, %, is (tests), **, ??, | (filters), [], and .:

{% set greeting = 'Hello ' %}
{% set name = 'Fabien' %}

{{ greeting ~ name|lower }}   {# Hello fabien #}

{# используйте круглые скобки, чтобы изменить приоритет #}
{{ (greeting ~ name)|lower }} {# hello fabien #}

Литералы#

Самая простая форма выражений - литералы. Литералы соответствуют типам данных PHP: строки, числа и массивы. Существуют следующие литералы:

  • "Привет мир": Все заключенное в одинарные или двойные кавычки является строками. Это полезно, когда нужно использовать строки в шаблоне (например, в качестве аргументов для вызова функций, фильтров или просто, чтобы расширить или подключить шаблон). Строка может содержать разделитель, который нужно экранировать обратным слэшем (\) – как в примере 'It\'s good'. Если строка содержит обратную косую черту (напр., 'c:\Program Files') убрать черту можно удвоив её (напр., 'c:\\Program Files').

  • 42 / 42.23: Целые числа и числа с плавающей точкой записываются так как есть. Если есть точка — это float, иначе — integer.

  • ["foo", "bar"]: Массивы определяются как набор данных разделенных запятыми (,) и заключенных в квадратные скобки ([]).

  • {"foo": "bar"}: Хеши определяются списком ключей и значений разделенными запятой (,) и заключены в фигурные скобки ({}).

    {# ключи как строка #}
    { 'foo': 'foo', 'bar': 'bar' }
    
    {# ключи как названия (эквивалентно предыдущему варианту) #}
    { foo: 'foo', bar: 'bar' }
    
    {# ключи как число #}
    { 2: 'foo', 4: 'bar' }
    
    {# ключи можно не указывать, если они совпадает с именем переменной #}
    { foo }
    {# эквивалентно следующему #}
    { 'foo': foo }
    
    {# ключи как выражения (выражение должно быть заключено в круглые скобки) #}
    {% set foo = 'foo' %}
    { (foo): 'foo', (1 + 1): 'bar', (foo ~ 'b'): 'baz' }
    
  • true / false: true истинное значение, false

    ложное значение.

  • null: null специальное значение. Это значение возвращается, когда переменной не существует. none является псевдонимом для null.

Массивы и хеши могут быть вложены друг в друга:

{% set foo = [1, {"foo": "bar"}] %}

Совет

Использование двойных или одинарных кавычек не влияет на производительность, но интерполяция строк (подстановка переменных) поддерживается только в двойных кавычках.

Математические операторы#

Twig позволяет производить математические операции над данными. Поддерживаются следующие операторы:

  • +: Складывает два объекта вместе (операнды приводятся к числами). {{ 1 + 1 }} выведет 2.

  • -: Вычитает из первого аргумента второй. {{ 3 - 2 }} выведет 1.

  • /: Деление чисел. Возвращает число с плавающей точкой. {{ 1 / 2 }} равнозначно {{ 0.5 }}.

  • %: Вычисляет целый остаток от деления. {{ 11 % 7 }} выведет 4.

  • //: Делит два числа и возвращает результат целое число. {{20 // 7 }} выведет 2, {{ -20  // 7 }} is -3 (это просто синтаксический сахар для round фильтра).

  • *:Умножение. {{ 2 * 2 }} вернет 4.

  • **: Возводит левый аргумент в степень правого аргумента {{ 2 ** 3 }} вернет 8.

Логические операторы#

Вы можете сочетать выражения используя следующие операторы:

  • and: Возвращает true, если левое и правое значение являются true.

  • or: Возвращает true, если левое или правое значение являются true.

  • not: Противоположное значение.

  • (expr): Группа выражений.

Примечание

Twig также поддерживает битовые операторы: (b-and, b-xor, and b-or).

Примечание

Операторы чувствительны к регистру.

Операторы сравнения#

Следующие операторы сравнения поддерживаются в любом выражении: ==, !=, <, >, >=, and <=.

Вы также можете проверить, если строка начинается starts with или заканчивается ends with другой строкой:

{% if 'Fabien' starts with 'F' %}
{% endif %}

{% if 'Fabien' ends with 'n' %}
{% endif %}

Примечание

For complex string comparisons, the matches operator allows you to use regular expressions:

{% if phone matches '/^[\\d\\.]+$/' %}
{% endif %}

Оператор содержания#

Оператор in осуществляет проверку на совпадение.

Возвращает true, если левое значение содержится в правом:

{# вернет true #}

{{ 1 in [1, 2, 3] }}

{{ 'cd' in 'abcde' }}

Совет

Вы можете использовать проверку на совпадение для строк, массивов или объектов реализующих интерфейс Traversable.

Для отрицания используйте оператор not in:

{% if 1 not in [1, 2, 3] %}

{# эквивалентно #}
{% if not (1 in [1, 2, 3]) %}

Оператор проверки#

Оператор is выполняет тесты. Тесты можно использовать для проверки переменной на соответствие общему выражению. Правый операнд - это имя теста:

{# проверит является ли переменная нечетной #}

{{ name is odd }}

Так же можно использовать аргументы:

{% if post.status is constant('Post::PUBLISHED') %}

Для отрицания используйте оператор, используйте оператор is not:

{% if post.status is not constant('Post::PUBLISHED') %}

{# эквивалентно #}
{% if not (post.status is constant('Post::PUBLISHED')) %}

Результаты работы можно посмотреть в тестах tests.

Другие операторы#

Следующие операторы очень полезны, но не попадают ни в одну из других категорий:

  • |: Применяет фильтр.

  • ..: Создает последовательность от левого до правого значения, (это просто синтаксический сахар для функции range):

    {{ 1..5 }}
    
    {# эквивалентно #}
    {{ range(1, 5) }}
    

    Обратите внимание, что вы должны использовать круглые скобки при объединении его с оператором фильтра из-за :ref: правил приоритета операторов <twig-expressions>:

    (1..5)|join(', ')
    
  • ~: Преобразует все значения в строки и соединяет их. {{ "Привет " ~ name ~ "!" }} вернет (предположим, что name это 'Иван') Привет Иван!.

  • ., []: Получает атрибут объекта.

  • ?:: Тернарный оператор:

    {{ foo ? 'yes' : 'no' }}
    {{ foo ?: 'no' }} is the same as {{ foo ? foo : 'no' }}
    {{ foo ? 'yes' }} is the same as {{ foo ? 'yes' : '' }}
    
  • ??: null-оператор объединения:

    {# возвращает значение foo, если foo определено, а не null, иначе 'no' #}
    {{ foo ?? 'no' }}
    

Подстановка переменных#

Подстановка переменных (#{expression}) доступна для любого выражения находящегося в строке с двойными скобками. Результатом выражения будет строка:

{{ "foo #{bar} baz" }}
{{ "foo #{1 + 2} baz" }}

Управление пробелами#

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

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

Twig поддерживает два модификатора:

  • Обрезка пробелов с помощью модификатора -: удаляет все пробелы (включая новые строки);

  • Обрезка пробелов в строках с помощью модификатора ~: удаляет все пробелы (исключая новые строки). Использование этого модификатора справа отключает значение по умолчанию удаление первой новой строки, унаследованной от PHP.

Модификаторы можно использовать с любой стороны тегов, например, в {%- или -%} и они занимают все пробелы для этой стороны тега. Можно использовать модификаторы с одной стороны тега или с обеих сторон:

{% set value = 'no spaces' %}
{#- нет начальных и конечных пробелов -#}
{%- if true -%}
    {{- value -}}
{%- endif -%}
{# выведет 'no spaces' #}

<li>
    {{ value }}    </li>
{# outputs '<li>\n    no spaces    </li>' #}

<li>
    {{- value }}    </li>
{# выведет '<li>no spaces    </li>' #}

<li>
    {{~ value }}    </li>
{# выведет '<li>\nno spaces    </li>' #}

Совет

В дополнение к модификаторам пробелов, Twig также имеет фильтр spaceless который удаляет пробелы between HTML tags:

{% apply spaceless %}
    <div>
        <strong>foo bar</strong>
    </div>
{% endapply %}

{# выведет <div><strong>foo bar</strong></div> #}

Расширения#

Twig может быть легко расширен. Если вы хотите создать свое собственное расширение, читайте раздел Создание Расширения.