Macros : au-delà du simple include
Une macro Twig fonctionne comme une fonction réutilisable dans un template, avec ses propres paramètres et valeurs par défaut. Contrairement à include, elle ne partage pas le contexte global : tout ce dont elle a besoin doit lui être passé explicitement, ce qui la rend plus prévisible en équipe.
{# _macros.html.twig #}
{% macro champ(nom, valeur = '', type = 'text') %}
<label>{{ nom }}</label>
<input type="{{ type }}" name="{{ nom }}" value="{{ valeur }}">
{% endmacro %}
{# formulaire.html.twig #}
{% import '_macros.html.twig' as ui %}
{{ ui.champ('email', type = 'email') }}
Le mot-clé import avec un alias évite les collisions de noms et rend l'origine de chaque macro explicite à l'appel.
embed : composer sans dupliquer
embed combine include et extends : il insère un sous-template tout en permettant de redéfinir certains de ses blocs, comme une carte produit dont on personnalise le pied selon le contexte.
{% embed 'carte.html.twig' with { produit: item } %}
{% block footer %}
<strong>En stock</strong>
{% endblock %}
{% endembed %}
C'est plus léger qu'un système d'héritage à plusieurs niveaux quand une seule variation ponctuelle est nécessaire.
Le sandbox pour du contenu non fiable
Si des templates proviennent d'utilisateurs (éditeur de mise en page, CMS), l'extension SandboxExtension restreint les tags, filtres et méthodes accessibles. Elle s'active globalement ou uniquement dans un bloc {% sandbox %}...{% endsandbox %}, ce qui évite qu'un template tiers appelle une méthode PHP dangereuse via un objet exposé.
Filtres avec fonctions fléchées
Depuis Twig 3, un filtre ou une fonction simple peut être défini avec une fonction fléchée, sans créer de classe dédiée :
$twig->addFilter(new TwigFilter('initiales', fn($nom) => strtoupper($nom[0]) . '.'));
Maîtriser les espaces et le typage strict
Le tag {%- ... -%} supprime les espaces avant/après une instruction, utile pour générer du HTML compact sans multiplier les commentaires {# #} de contrôle. Le tag apply permet aussi d'appliquer un filtre à un bloc entier plutôt qu'à une seule expression : {% apply upper %}...{% endapply %}.
Enfin, en production, vérifiez que auto_reload est désactivé et que le cache de templates compilés pointe vers un répertoire persistant : chaque template n'est alors compilé en PHP qu'une seule fois, ce qui élimine le coût de parsing à chaque requête.