New in Symfony 7.3: Twig Extension Attributes


Contributed by
Jérôme Tamarelle
in
#52748

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:

// 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:

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:

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() and getFunctions() 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:

#[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.

Sponsor the Symfony project.