Twig extensions allow you to add new features to Twig templates using your
own logic. In Symfony applications, they are created by extending the
AbstractExtension
class and defining the custom filters and functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// src/Twig/AppExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AppExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
new TwigFilter('product_number', [$this, 'formatProductNumber']),
];
}
public function formatProductNumber(string $number): string
{
// ...
}
}
To improve performance, it's common to split the extension into two classes: one to declare the filters/functions and another to contain the logic, which is loaded only when the filter or function is actually used in a template:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AppExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
new TwigFilter('product_number', [AppRuntime::class, 'formatProductNumber']),
];
}
}
use Twig\Extension\RuntimeExtensionInterface;
class AppRuntime implements RuntimeExtensionInterface
{
public function formatProductNumber(string $number): string
{
// ...
}
}
In Symfony 7.3, we're making custom Twig extensions even simpler and more powerful thanks to PHP attributes. Here's what the same extension looks like now:
1 2 3 4 5 6 7 8 9 10 11 12
namespace App\Twig;
use Twig\Attribute\AsTwigFilter;
class AppExtension
{
#[AsTwigFilter('product_number')]
public function formatProductNumber(string $number): string
{
// ...
}
}
With this new approach:
- You no longer need to extend the
AbstractExtension
base class; - Your extensions are lazy-loaded by default, so you get the same performance as before without having to split them into two classes;
- You don't need to define
getFilters()
andgetFunctions()
methods; just add the corresponding attribute (#[AsTwigFilter]
,#[AsTwigFunction]
) directly to your methods.
The new attributes also allow you to fully configure each filter and function using named arguments:
1 2 3 4
#[AsTwigFilter('...', needsEnvironment: true)]
#[AsTwigFilter('...', needsEnvironment: true, preEscape: true)]
#[AsTwigFunction('...', needsContext: true)]
#[AsTwigFunction('...', needsCharset: true, isSafe: ['html'])]
This new syntax makes your extensions cleaner, faster to write, and easier to maintain while keeping the full power of the Twig integration.
Note that
needsEnvironment
option is automatically detected when the 1st parameter of the function hasTwig\Environment
type.PHP attributes has been a huge contribution to the language. Many thanks to those who contributed to make this simplification of writing Twig filters and extensions possible.
Amazing work! It's going to make extending Twig so much smoother and cleaner.