New in Symfony 2.7: Twig as a First-Class Citizen

Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.
When I started to work on Symfony2, Twig didn't exist. Anyway, to ease using PHP as a templating engine, I created the Symfony Templating Component.
Later on, not very satisfied with using PHP as a templating language, I decided to create a new templating language, Twig, based on the Python Jinja2 language. And Symfony2 became the first popular framework to adopt a non-PHP templating engine in core. Of course, I had no idea if it would become a popular choice amongst Symfony developers, and so Symfony2 lets you use both PHP and Twig in your applications.
Fast forward to 2015; it's clear that Twig won the heart of Symfony2 developers and the heart of many PHP developers; Twig is now also used by many Open-Source CMSes besides Symfony.
But what would Twig as a First-Class Citizen mean in Symfony2 then? To be able to support PHP and Twig in Symfony, we added an abstraction layer. As a developer, you mostly interact with this abstraction via the templating
service; that allows one application to use PHP and Twig. As you can imagine, this abstraction adds a layer of complexity and it comes with some performance impact.
For Symfony 3.0, I'd like to extract the Templating Component into an independent library (for the few people using PHP with Symfony) but I'd also like for Twig to be front and center in the framework. The good news is that most of the work has already been done in Symfony 2.7.
Using Twig in Symfony 2.6
In Symfony 2.6, you cannot use Twig "directly"; you must instead use the templating sub-system via the templating
service. Moreover, as some Twig extensions depends on PHP helpers, the PHP templating system is always loaded even if not configured in the templating.engines
option.
So, the minimal code you can write to render a Twig template reads as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
<?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' => '$ecret',
'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');
As you can see, the Twig bundle has been enabled and the templating.engines
sets Twig as the only engine.
Using Twig without the Templating sub-system
As of Symfony 2.7, the templating
configuration can be removed altogether and Twig can be used directly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
<?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' => '$ecret',
));
});
}
}
$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');
Using Twig directly has some drawbacks: you cannot use some base controller class shortcuts (like render()
or renderResponse()
),
the web profiler does not display the time it took to render templates anymore, the "@bundle" notation cannot be used when referencing templates, and some other smaller things. Luckily, all those issues will be fixed in the coming weeks.
What about performance?
Not calling an abstraction layer simplifies the code you have to understand and it allows to easily use all native Twig capabilities. But there is another nice side-effect. As you probably know, loading code in PHP (even with an opcode cache) induces a performance penalty; the less code you have, the most performant your app is.
Using Twig directly, without enabling the templating sub-system allows for a nice performance boost as demonstrated by this Blackfire.io profile comparison.
If you have a look at the "metrics" tab, you will see that the number of classes loaded for this simple example drops from 103
to 55
; that's 48 less classes to load in the new code. Not loading the classes also means less memory consumption: it drops by 30%.
Also, in the call graph, you won't see any nodes for the various default Twig extensions as the time it takes to load them is negligible... except for one: the Fragment extension. Spotting this anomaly was easy when I profiled the code, and after digging into the problem more, I made a change to lazy-load the fragment renderers. That will also give a nice performance boost in Symfony 2.7.
Help the Symfony project!
As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.
Comments
Comments are closed.
To ensure that comments stay relevant, they are closed for old posts.
this is wrong. This notation is the Twig native notation (and in earlier versions of Symfony, the case where it was impossible to use it was actually when using the templating engine with multiple engines enabled)
The issue might be related for the Bundle:Foo:test.html.twig notation, not to the @Bundle notation
@Hugo in real world, the memory consumption gain will be much smaller (because a typical Symfony request is likely to use much more stuff than this small snippet)
Thanks Fabien and the Symfony Team.
Scott
sudo melody self-update # be sure to use the very last melody release
melody run -vvv 5a807d97c77c496e871a your-name-here
;)