How to Write a custom Twig Extension
Warning: You are browsing the documentation for Symfony 3.x, which is no longer maintained.
Read the updated version of this page for Symfony 7.2 (the current stable version).
If you need to create custom Twig functions, filters, tests or more, you'll need to create a Twig extension. You can read more about Twig Extensions in the Twig documentation.
Create the Extension Class
Suppose you want to create a new filter called price
that formats a number into
money:
1 2 3 4
{{ product.price|price }}
{# pass in the 3 optional arguments #}
{{ product.price|price(2, ',', '.') }}
Create a class that extends AbstractExtension
and fill in the logic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// src/AppBundle/Twig/AppExtension.php
namespace AppBundle\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AppExtension extends AbstractExtension
{
public function getFilters()
{
return [
new TwigFilter('price', [$this, 'formatPrice']),
];
}
public function formatPrice($number, $decimals = 0, $decPoint = '.', $thousandsSep = ',')
{
$price = number_format($number, $decimals, $decPoint, $thousandsSep);
$price = '$'.$price;
return $price;
}
}
Note
Prior to Twig 1.26, your extension had to define an additional getName()
method that returned a string with the extension's internal name (e.g.
app.my_extension
). When your extension needs to be compatible with Twig
versions before 1.26, include this method which is omitted in the example
above.
Here's how to create a custom function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// src/AppBundle/Twig/AppExtension.php
namespace AppBundle\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class AppExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new TwigFunction('area', [$this, 'calculateArea']),
];
}
public function calculateArea(int $width, int $length)
{
return $width * $length;
}
}
Tip
Along with custom filters and functions, you can also register global variables.
Register an Extension as a Service
Next, register your class as a service and tag it with twig.extension
. If you're
using the default services.yml configuration,
you're done! Symfony will automatically know about your new service and add the tag.
You can now start using your filter in any Twig template.
Creating Lazy-Loaded Twig Extensions
1.35
Support for lazy-loaded extensions was introduced in Twig 1.35.0 and 2.4.4.
Including the code of the custom filters/functions in the Twig extension class is the simplest way to create extensions. However, Twig must initialize all extensions before rendering any template, even if the template doesn't use an extension.
If extensions don't define dependencies (i.e. if you don't inject services in them) performance is not affected. However, if extensions define lots of complex dependencies (e.g. those making database connections), the performance loss can be significant.
That's why Twig allows to decouple the extension definition from its
implementation. Following the same example as before, the first change would be
to remove the formatPrice()
method from the extension and update the PHP
callable defined in getFilters()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// src/AppBundle/Twig/AppExtension.php
namespace AppBundle\Twig;
use AppBundle\Twig\AppRuntime;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AppExtension extends AbstractExtension
{
public function getFilters()
{
return [
// the logic of this filter is now implemented in a different class
new TwigFilter('price', [AppRuntime::class, 'formatPrice']),
];
}
}
Then, create the new AppRuntime
class (it's not required but these classes
are suffixed with Runtime
by convention) and include the logic of the
previous formatPrice()
method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// src/AppBundle/Twig/AppRuntime.php
namespace AppBundle\Twig;
use Twig\Extension\RuntimeExtensionInterface;
class AppRuntime implements RuntimeExtensionInterface
{
public function __construct()
{
// this simple example doesn't define any dependency, but in your own
// extensions, you'll need to inject services using this constructor
}
public function formatPrice($number, $decimals = 0, $decPoint = '.', $thousandsSep = ',')
{
$price = number_format($number, $decimals, $decPoint, $thousandsSep);
$price = '$'.$price;
return $price;
}
}
3.4
The RuntimeExtensionInterface
was introduced in Symfony 3.4.
If you're using the default services.yaml
configuration, this will already
work! Otherwise, create a service
for this class and tag your service with twig.runtime
.