Как использовать PHP для шаблонов вместо Twig

По умолчанию, в качестве движка шаблонов, Symfony использует Twig, но вы всё равно можете использовать чистый PHP-код по желанию. Оба механизма шаблонов одинаково поддерживаются в Symfony. Symfony добавляет к PHP некоторые приятные функции, чтобы написание PHP-шаблонов стало более мощным.

Tip

Если вы выберете не использовать Twig и отключите его, вам понадобится реализовать ваш собственный обработчик исключений через событие kernel.exception.

Отображение PHP-шаблонов

Если вы хотите использовать механизм PHP-шаблонов, то для начала убедитесь в том, что вы включили его в файле конфигурации вашего приложения:

  • YAML
    1
    2
    3
    4
    5
    # app/config/config.yml
    framework:
        # ...
        templating:
            engines: ['twig', 'php']
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!-- app/config/config.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:framework="http://symfony.com/schema/dic/symfony"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd
            http://symfony.com/schema/dic/symfony
            http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
    
        <framework:config>
            <!-- ... -->
            <framework:templating>
                <framework:engine id="twig" />
                <framework:engine id="php" />
            </framework:templating>
        </framework:config>
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    6
    $container->loadFromExtension('framework', array(
        // ...
        'templating' => array(
            'engines' => array('twig', 'php'),
        ),
    ));
    

Тепер вы можете отправлять шаблон PHP вместо шаблона Twig просто используя расширение .php вместо .twig в имени шаблона. Контроллер ниже отображает шаблон index.html.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// src/AppBundle/Controller/HelloController.php

// ...
public function indexAction($name)
{
    return $this->render(
        'AppBundle:Hello:index.html.php',
        array('name' => $name)
    );
}

Вы также можете использовать шорткат @Template чтобы отобразить шаблон по умолчанию AppBundle:Hello:index.html.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// src/AppBundle/Controller/HelloController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

// ...

/**
 * @Template(engine="php")
 */
public function indexAction($name)
{
    return array('name' => $name);
}

Caution

Включение шаблонов php и twig одновременно допускается, но это приведёт к ненужному побочному эффекту в вашем приложении: нотация @ для пространства имён Twig больше не будет поддерживаться для метода render():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public function indexAction()
{
    // ...

    // шаблоны пространста имён больше не будут работать в контроллерах
    $this->render('@App/Default/index.html.twig');

    // вы должны использоват традиционную нотацию шаблона
    $this->render('AppBundle:Default:index.html.twig');
}
1
2
3
4
5
{# внутри шаблона Twig, шаблоны пространства имён работают ожидаемо #}
{{ include('@App/Default/index.html.twig') }}

{# традиционная нотация шаблона также будет работать #}
{{ include('AppBundle:Default:index.html.twig') }}

Украшение шаблонов

Зачастую, шаблоны в проекте используют общие элементы, как то известные заголовок и нижний колонтитул (футер). В Symfony эта проблема рассматривается по-другому: один шаблон может быть украшен другим.

Щаблон index.html.php украшен шаблоном layout.html.php, благодаря вызову extend():

1
2
3
4
<!-- app/Resources/views/Hello/index.html.php -->
<?php $view->extend('AppBundle::layout.html.php') ?>

Hello <?php echo $name ?>!

Нотация AppBundle::layout.html.php звучит знакомо, не правда ли? Это та же нотация, которая используется для ссылки на шаблон. Часть :: просто означает, что элемент контроллера пуст, так что соответствующий файл напрямую хранится под views/.

Теперь, посмотрите на файл layout.html.php:

1
2
3
4
5
6
<!-- app/Resources/views/layout.html.php -->
<?php $view->extend('::base.html.php') ?>

<h1>Привет, приложение</h1>

<?php $view['slots']->output('_content') ?>

Макет сам по себе украшен другим (::base.html.php). Symfony поддерживает множественные уровни украшения: макет может сам по себе быть украшен другим. Когда часть пакета имени шаблона пуста, просмотры ищутся в каталоге app/Resources/views/. этот каталог хранит глобальные просмотры всего вашего проекта:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!-- app/Resources/views/base.html.php -->
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title><?php $view['slots']->output('title', 'Hello Application') ?></title>
    </head>
    <body>
        <?php $view['slots']->output('_content') ?>
    </body>
</html>

Для обоих макетов, выражение $view['slots']->output('_content') заменяется содержанием дочернего шаблона, index.html.php и layout.html.php соответственно (больше о слотах в следующем разделе).

Как вы видите, Symfony предоставляет методы для загадочного объекта $view. В шаблоне, переменная $view всегда доступна и ссылается на специальный объект, который предоставляет кучу методов, которые заставляют шаблонизатор двигаться.

Работа со слотами

Слот - это отрезок кода, определённый в шаблоне, и используемый повторно в любом макете, украшающим шаблон. В шаблоне index.html.php, определите слот title:

1
2
3
4
5
6
<!-- app/Resources/views/Hello/index.html.php -->
<?php $view->extend('AppBundle::layout.html.php') ?>

<?php $view['slots']->set('title', 'Hello World Application') ?>

Hello <?php echo $name ?>!

Базовый макет уже имеет код для вывода названия в заголовке:

1
2
3
4
5
<!-- app/Resources/views/base.html.php -->
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title><?php $view['slots']->output('title', 'Hello Application') ?></title>
</head>

Методы output() вставляет содержимое слота и опционально берёт значение по умолчанию, если слот не определён. А _content - это просто специальный слот, который содержит отображённый дочерний шаблон.

Лля больших слотов также существует расширенный синтаксис:

1
2
3
<?php $view['slots']->start('title') ?>
    Some large amount of HTML
<?php $view['slots']->stop() ?>

Включение других шаблонов

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

Создайте шаблон hello.html.php:

1
2
<!-- app/Resources/views/Hello/hello.html.php -->
Hello <?php echo $name ?>!

И измените шаблон index.html.php так, чтобы он включал его:

1
2
3
4
<!-- app/Resources/views/Hello/index.html.php -->
<?php $view->extend('AppBundle::layout.html.php') ?>

<?php echo $view->render('AppBundle:Hello:hello.html.php', array('name' => $name)) ?>

Метод render() оценивает и возвращает содержимое другого шаблона (это точно такой же метод, как тот, что используется в контроллере).

Встраивание других контроллеров

Но что, если вы хотите встроить результат другого контроллера в вашем шаблоне? Это очень удобно при работе с Ajax, или когда встроенный шаблон требует некоторые переменные, недоступные в основном в шаблоне.

Если вы создадите действие fancy и захотите включить его в шаблон index.html.php, просто используйте следующий код:

1
2
3
4
5
6
7
<!-- app/Resources/views/Hello/index.html.php -->
<?php echo $view['actions']->render(
    new \Symfony\Component\HttpKernel\Controller\ControllerReference('AppBundle:Hello:fancy', array(
        'name'  => $name,
        'color' => 'green',
    ))
) ?>

Здесь, строка AppBundle:Hello:fancy ссылается на действие fancy контроллера Hello:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// src/AppBundle/Controller/HelloController.php

class HelloController extends Controller
{
    public function fancyAction($name, $color)
    {
        // создайте некоторый объект, основанный на переменной $color
        $object = ...;

        return $this->render('AppBundle:Hello:fancy.html.php', array(
            'name'   => $name,
            'object' => $object
        ));
    }

    // ...
}

Но где определяется элемент массива $view['actions']? Как и $view['slots'], он называется помощником шаблона, и следующий раздел расскажет вам больше об этом.

Использование помощников в шаблонах

Система шаблонизации Symfony может быть легко расширена посредоством использования помощников. Помощники - это PHP-объекты, которые предоставляют функции, полезные в контексте шаблонов. actions и slots - это два встроенных помощника Symfony.

Создание ссылок между страницами

Говоря о веб-приложениях, нельзя не говорить о создании ссылок между страницами. Вместо того, чтобы жёстко кодировать URL в шаблонах, помощник router знает, как сгенерировать URL, основываясь на конфигурации маршрутизации. Таким образом, все ваши URL могут быть с лёгкостью обновлены путём изменения конфигурации:

1
2
3
<a href="<?php echo $view['router']->path('hello', array('name' => 'Thomas')) ?>">
    Поздоровайтесь с Томасом!
</a>

Метод path() берёт имя маршрута и массив параметров в качестве аргументов. Имя маршрута - это основной ключ, под которым ссылаются на маршруты, а параметры - это значения заполнителей, определённые в модели маршрута:

1
2
3
4
# src/AppBundle/Resources/config/routing.yml
hello: # The route name
    path:     /hello/{name}
    defaults: { _controller: AppBundle:Hello:index }

Использование ресурсов: изображений, Java-скриптов и таблиц стилей

Чем был бы Интернет без изображений, Java-скриптов и таблиц стилей? Symfony предоставляет тег assets для того, чтобы с лёгкостью с ними справляться:

1
2
3
<link href="<?php echo $view['assets']->getUrl('css/blog.css') ?>" rel="stylesheet" type="text/css" />

<img src="<?php echo $view['assets']->getUrl('images/logo.png') ?>" />

Главной целью помощника assets является сделать ваше приложение более портативным. Благодаря этому помощнику, вы можете перемещать корневой каталог вашего приложения в любое место под вашим корневым веб-каталогом, не изменяя ничего в коде вашего шаблона.

Профилирование шаблонов

Используя помощник stopwatch, вы можете засекать части вашего шаблона и отображать их во временной шкале WebProfilerBundle:

1
2
3
<?php $view['stopwatch']->start('foo') ?>
... вещи, которые засекаются
<?php $view['stopwatch']->stop('foo') ?>

Tip

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

Экранирование вывода

При использовании PHP-шаблонов, экранируйте переменные, когда они отображены пользователю:

1
<?php echo $view->escape($var) ?>

По умолчанию, метод escape() предполагает, что переменная выводится в рамках контекста HTML. Второй аргумент позволяет вам изменять контекст. Например, чтобы вывести что-то в Java-скрипте, используйте контекст js:

1
<?php echo $view->escape($var, 'js') ?>

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.