diff --git a/quick_tour/the_architecture.rst b/quick_tour/the_architecture.rst
index a323461885d..6bc8b99ce7a 100644
--- a/quick_tour/the_architecture.rst
+++ b/quick_tour/the_architecture.rst
@@ -158,30 +158,23 @@ Twig Extension & Autoconfiguration
Thanks to Symfony's service handling, you can *extend* Symfony in many ways, like
by creating an event subscriber or a security voter for complex authorization
-rules. Let's add a new filter to Twig called ``greet``. How? Create a class
-that extends ``AbstractExtension``::
+rules. Let's add a new filter to Twig called ``greet``. How? Create a simple class
+with your logic::
// src/Twig/GreetExtension.php
namespace App\Twig;
use App\GreetingGenerator;
- use Twig\Extension\AbstractExtension;
- use Twig\TwigFilter;
+ use Twig\Attribute\AsTwigFilter;
- class GreetExtension extends AbstractExtension
+ class GreetExtension
{
public function __construct(
private GreetingGenerator $greetingGenerator,
) {
}
- public function getFilters(): array
- {
- return [
- new TwigFilter('greet', [$this, 'greetUser']),
- ];
- }
-
+ #[AsTwigFilter('greet')]
public function greetUser(string $name): string
{
$greeting = $this->greetingGenerator->getRandomGreeting();
@@ -197,8 +190,8 @@ After creating just *one* file, you can use this immediately:
{# templates/default/index.html.twig #}
{# Will print something like "Hey Symfony!" #}
{{ name|greet }}
-
-How does this work? Symfony notices that your class extends ``AbstractExtension``
+
+How does this work? Symfony notices that your class uses a Twig attribute
and so *automatically* registers it as a Twig extension. This is called autoconfiguration,
and it works for *many* many things. Create a class and then extend a base class
(or implement an interface). Symfony takes care of the rest.
diff --git a/reference/attributes.rst b/reference/attributes.rst
index a8399dafe28..7878446351c 100644
--- a/reference/attributes.rst
+++ b/reference/attributes.rst
@@ -123,6 +123,8 @@ Twig
~~~~
* :ref:`Template `
+* :ref:`AsTwigFilter `
+* :ref:`AsTwigFunction `
Symfony UX
~~~~~~~~~~
diff --git a/templates.rst b/templates.rst
index c6312abb33c..3ca54276540 100644
--- a/templates.rst
+++ b/templates.rst
@@ -1553,23 +1553,20 @@ as currency:
{# pass in the 3 optional arguments #}
{{ product.price|price(2, ',', '.') }}
-Create a class that extends ``AbstractExtension`` and fill in the logic::
+.. _templates-twig-filter-attribute:
+
+Create a class with a method that contains the filter logic, then add
+the ``#[AsTwigFilter]`` attribute to define the name and options of
+the Twig filter::
// src/Twig/AppExtension.php
namespace App\Twig;
- use Twig\Extension\AbstractExtension;
- use Twig\TwigFilter;
+ use Twig\Attribute\AsTwigFilter;
- class AppExtension extends AbstractExtension
+ class AppExtension
{
- public function getFilters(): array
- {
- return [
- new TwigFilter('price', [$this, 'formatPrice']),
- ];
- }
-
+ #[AsTwigFilter('price')]
public function formatPrice(float $number, int $decimals = 0, string $decPoint = '.', string $thousandsSep = ','): string
{
$price = number_format($number, $decimals, $decPoint, $thousandsSep);
@@ -1579,24 +1576,19 @@ Create a class that extends ``AbstractExtension`` and fill in the logic::
}
}
-If you want to create a function instead of a filter, define the
-``getFunctions()`` method::
+.. _templates-twig-function-attribute:
+
+If you want to create a function instead of a filter, use the
+``#[AsTwigFunction]`` attribute::
// src/Twig/AppExtension.php
namespace App\Twig;
- use Twig\Extension\AbstractExtension;
- use Twig\TwigFunction;
+ use Twig\Attribute\AsTwigFunction;
- class AppExtension extends AbstractExtension
+ class AppExtension
{
- public function getFunctions(): array
- {
- return [
- new TwigFunction('area', [$this, 'calculateArea']),
- ];
- }
-
+ #[AsTwigFunction('area')]
public function calculateArea(int $width, int $length): int
{
return $width * $length;
@@ -1608,6 +1600,16 @@ If you want to create a function instead of a filter, define the
Along with custom filters and functions, you can also register
`global variables`_.
+.. versionadded:: 7.3
+
+ Support for the ``#[AsTwigFilter]`` and ``#[AsTwigFunction]`` attributes was introduced in Symfony 7.3.
+ Previously, you had to extend the ``AbstractExtension`` class, and override the
+ ``getFilters()`` and ``getFunctions()`` methods.
+
+When using autoconfiguration, the tag ``twig.attribute_extension`` is added automatically
+when a Twig attribute is used on a method of a class. Otherwise, when autoconfiguration is not enabled,
+it needs to be added in the service definition.
+
Register an Extension as a Service
..................................
@@ -1631,9 +1633,10 @@ this command to confirm that your new filter was successfully registered:
Creating Lazy-Loaded Twig Extensions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-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
+When using attributes to extend Twig, the services are initialized only when
+the functions or filters are used to render the template. But in case you use the
+classic approach by extending the ``AbstractExtension`` class, Twig initialize all extensions before
+rendering 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