Что нового в Symfony 2.7: Twig в роли полноправной составляющей фреймворка

Когда я (далее Я заменяйте на Fabien Potencier) начинал работать над Symfony2, twig не существовал вовсе. Поэтому для упрощения работы с PHP я создал Symfony Templating Engine.

Позднее, осознав все неудобства работы с PHP в качестве шаблонизатора, я решил написать свой язык для шаблонов, а именно — Twig, в основу которого я взял язык Jinja2 из Python. Таким образом Symfony2 стал первым популярным фреймворком, который использовал шаблонизатор не на PHP. Конечно, тогда я и не мог представить, что Twig станет настолько популярным среди разработчиков Symfony и поэтому я предусмотрел возможность использования в Symfony2 как Twig, так и PHP.

Вернемся в 2015 год. Очевидно, что Twig занимает лидирующие позиции у разработчиков не только Symfony2, но и у других PHP фреймворков. Его можно встретить во многих open-source CMS.

Раз Twig и так присутствует практически по всех приложениях, то что же тогда означает заголовок статьи, спросите вы. Для того, чтобы поддерживать и Twig, и PHP в Symfony мы добавили уровень абстракции. Как разработчик, вы имеете дело с этим уровнем когда обращаетесь к службе templating, таким образом в одном приложении вы свободно можете использовать либо PHP шаблонизатор, либо Twig. Не трудно догадаться, что такой уровень абстракции добавляет определенной сложности к работе и, конечно, понижает быстродействие.

В Symfony 3.0 я планирую вывести Templating Component в отдельную библиотеку (для тех, кто все-таки предпочитает использование PHP шаблонизатора), а Twig оставить в качестве центральной части фреймворка. Хорошая новость на данный момент заключается в том, что большая часть работы уже выполнена в Symfony 2.7.

Использование Twig в Symfony 2.6

В Symfony 2.6 вы не можете использовать Twig “напрямую”. Вам необходимо обращаться к службе templating. Более того, некоторые расширения Twig используют вспомогательные функции из шаблонизатора PHP, именно поэтому он всегда загружен, если он не указан в настройках templating.engines.

Таким образом, чтобы вывести Twig шаблон, вам потребуется, как минимум, написать следующие строки:

<?php
require_once __DIR__.'/../autoload.php.dist';
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
class SimpleKernel extends Kernel
{
    public function registerBundles()
    {
        return array(new FrameworkBundle(), new TwigBundle());
    }
    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load(function ($container) {
            $container->loadFromExtension('framework', array(
                'secret' => '$secret',
                'router' => array('resource' => ''),
                'templating' => array('engines' => array('twig')),
            ));
        });
    }
}
$kernel = new SimpleKernel('prod', false);
$kernel->boot();
$c = $kernel->getContainer();
$c->get('request_stack')->push(Request::create('/'));
echo $c->get('templating')->render('index.html.twig');

Как видите, Twig был включен в templating.engines в качестве единственного шаблонизатора.

Использование Twig без системы шаблонов

Начиная с Symfony 2.7 вы можете полностью проигнорировать настройки шаблонизатора и использовать его напрямую:

<?php
require_once __DIR__.'/../autoload.php.dist';
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
class SimpleKernel extends Kernel
{
    public function registerBundles()
    {
        return array(new FrameworkBundle(), new TwigBundle());
    }
    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load(function ($container) {
            $container->loadFromExtension('framework', array(
                'secret' => '$secret',
            ));
        });
    }
}
$kernel = new SimpleKernel('prod', false);
$kernel->boot();
$c = $kernel->getContainer();
$c->get('request_stack')->push(Request::create('/'));
echo $c->get('twig')->render('index.html.twig');

При таком подходе мы сталкиваемся с небольшими трудностями: вы не сможете использовать некоторые методы контроллера (render() или renderResponse()), веб-профайлер не будет показывать время затраченное на обработку шаблона, нотацию @bundle нельзя будет применить при обращении к другим шаблонам и еще пара малозначимых проблем. Большинство этих проблем мы решим в ближайшее время.

Что касается производительности?

Так как мы отказываемся от лишнего уровня абстракции, то понять код вам будет намного проще и вы сможете использовать нативные возможности Twig. Но существует еще один положительный побочный эффект. Как вы знаете, загрузка PHP кода (даже кеша машинных команд) оказывает отрицательное действие на производительность. Таким образом, чем меньше кода, тем быстрее работает ваше приложение.

Использование Twig напрямую без применения подсистем шаблонизации очень положительно сказывается на быстродействии, как вы можете увидеть в сравнении, полученное при помощи Blackfire.io.

В закладке metrics вы увидите, что количество загруженных классов в этом простом примере упало с 103 до 55, что на 48 классов меньше. Что в итоге сказывается на количество занятой памяти, оно упало примерно на 30%.

Также, в графике вызовов вы не найдете узлов по загрузке стандартных расширений Twig, так как затраченное на это время незначительно, за одним исключением — расширение Fragment. Эта проблема быстро проявила себя и после некоторых поисков, я изменил метод загрузки рендеров фрагментов на загрузку по требованию. Что приведет к еще большему повышению производительности в Symfony 2.7.