Création de filtres personnalisés

Les filtres sont des outils puissants pour formater et modifier les données directement dans les templates Latte. Ils offrent une syntaxe claire utilisant le symbole pipe (|) pour transformer les variables ou les résultats d'expressions au format de sortie souhaité.

Que sont les filtres ?

Les filtres dans Latte sont essentiellement des fonctions PHP conçues spécifiquement pour transformer une valeur d'entrée en une valeur de sortie. Ils sont appliqués à l'aide de la notation pipe (|) à l'intérieur des expressions de template ({...}).

Commodité : Les filtres vous permettent d'encapsuler des tâches de formatage courantes (comme le formatage des dates, la modification de la casse, le raccourcissement) ou la manipulation de données dans des unités réutilisables. Au lieu de répéter du code PHP complexe dans vos templates, vous pouvez simplement appliquer un filtre :

{* Au lieu de PHP complexe pour raccourcir : *}
{$article->text|truncate:100}

{* Au lieu de code pour formater la date : *}
{$event->startTime|date:'Y-m-d H:i'}

{* Application de plusieurs transformations : *}
{$product->name|lower|capitalize}

Lisibilité : L'utilisation de filtres rend les templates plus clairs et plus axés sur la présentation, car la logique de transformation est déplacée vers la définition du filtre.

Sensibilité au contexte : Un avantage clé des filtres dans Latte est leur capacité à être sensibles au contexte. Cela signifie qu'un filtre peut reconnaître le type de contenu avec lequel il travaille (HTML, JavaScript, texte brut, etc.) et appliquer la logique ou l'échappement approprié, ce qui est crucial pour la sécurité et l'exactitude, en particulier lors de la génération de HTML.

Intégration avec la logique applicative : Tout comme les fonctions personnalisées, l'appelable PHP derrière un filtre peut être une closure, une méthode statique ou une méthode d'instance. Cela permet aux filtres d'accéder aux services applicatifs ou aux données si nécessaire, bien que leur objectif principal reste la transformation de la valeur d'entrée.

Latte fournit par défaut un riche ensemble de filtres standard. Les filtres personnalisés vous permettent d'étendre cet ensemble avec des formatages et des transformations spécifiques à votre projet.

Si vous avez besoin d'effectuer une logique basée sur plusieurs entrées ou si vous n'avez pas de valeur principale à transformer, il est probablement plus approprié d'utiliser une fonction personnalisée. Si vous avez besoin de générer un balisage complexe ou de contrôler le flux du template, envisagez une balise personnalisée.

Création et enregistrement de filtres

Il existe plusieurs façons de définir et d'enregistrer des filtres personnalisés dans Latte.

Enregistrement direct via addFilter()

La manière la plus simple d'ajouter un filtre est d'utiliser la méthode addFilter() directement sur l'objet Latte\Engine. Vous spécifiez le nom du filtre (tel qu'il sera utilisé dans le template) et l'appelable PHP correspondant.

$latte = new Latte\Engine;

// Filtre simple sans arguments
$latte->addFilter('initial', fn(string $s): string => mb_substr($s, 0, 1) . '.');

// Filtre avec un argument optionnel
$latte->addFilter('shortify', function (string $s, int $len = 10): string {
	return mb_substr($s, 0, $len);
});

// Filtre traitant un tableau
$latte->addFilter('sum', fn(array $numbers): int|float => array_sum($numbers));

Utilisation dans le template :

{$name|initial}                 {* Affiche 'J.' si $name est 'John' *}
{$description|shortify}         {* Utilise la longueur par défaut de 10 *}
{$description|shortify:50}      {* Utilise la longueur 50 *}
{$prices|sum}                   {* Affiche la somme des éléments du tableau $prices *}

Passage d'arguments :

La valeur à gauche du pipe (|) est toujours passée comme premier argument à la fonction filtre. Tous les paramètres spécifiés après les deux points (:) dans le template sont passés comme arguments suivants.

{$text|shortify:30}
// Appelle la fonction PHP shortify($text, 30)

Enregistrement via une extension

Pour une meilleure organisation, en particulier lors de la création d'ensembles de filtres réutilisables ou de leur partage en tant que paquets, la méthode recommandée est de les enregistrer dans une extension Latte :

namespace App\Latte;

use Latte\Extension;

class MyLatteExtension extends Extension
{
	public function getFilters(): array
	{
		return [
			'initial' => $this->initial(...),
			'shortify' => $this->shortify(...),
		];
	}

	public function initial(string $s): string
	{
		return mb_substr($s, 0, 1) . '.';
	}

	public function shortify(string $s, int $len = 10): string
	{
		return mb_substr($s, 0, $len);
	}
}

// Enregistrement
$latte = new Latte\Engine;
$latte->addExtension(new App\Latte\MyLatteExtension);

Cette approche maintient la logique de votre filtre encapsulée et l'enregistrement simple.

Utilisation du chargeur de filtres

Latte permet d'enregistrer un chargeur de filtres à l'aide de addFilterLoader(). Il s'agit d'un seul appelable que Latte demandera pour tout nom de filtre inconnu lors de la compilation. Le chargeur retourne l'appelable PHP du filtre ou null.

$latte = new Latte\Engine;

// Le chargeur peut créer/obtenir dynamiquement des appelables de filtre
$latte->addFilterLoader(function (string $name): ?callable {
	if ($name === 'myLazyFilter') {
		// Imaginez une initialisation coûteuse ici...
		$service = get_some_expensive_service();
		return fn($value) => $service->process($value);
	}
	return null;
});

Cette méthode était principalement destinée au chargement paresseux de filtres avec une initialisation très coûteuse. Cependant, les pratiques modernes d'injection de dépendances gèrent généralement les services paresseux plus efficacement.

Les chargeurs de filtres ajoutent de la complexité et ne sont généralement pas recommandés au profit de l'enregistrement direct via addFilter() ou dans une extension via getFilters(). Utilisez les chargeurs uniquement si vous avez une raison sérieuse et spécifique liée à des problèmes de performance lors de l'initialisation des filtres qui ne peuvent pas être résolus autrement.

Filtres utilisant une classe avec attributs

Une autre manière élégante de définir des filtres est d'utiliser des méthodes dans votre classe de paramètres de template. Ajoutez simplement l'attribut #[Latte\Attributes\TemplateFilter] à la méthode.

use Latte\Attributes\TemplateFilter;

class TemplateParameters
{
	public function __construct(
		public string $description,
		// autres paramètres...
	) {}

	#[TemplateFilter]
	public function shortify(string $s, int $len = 10): string
	{
		return mb_substr($s, 0, $len);
	}
}

// Passage de l'objet au template
$params = new TemplateParameters(description: '...');
$latte->render('template.latte', $params);

Latte reconnaîtra et enregistrera automatiquement les méthodes marquées de cet attribut lorsque l'objet TemplateParameters est passé au template. Le nom du filtre dans le template sera le même que le nom de la méthode (shortify dans ce cas).

{* Utilisation du filtre défini dans la classe de paramètres *}
{$description|shortify:50}

Filtres contextuels

Parfois, un filtre a besoin de plus d'informations que la simple valeur d'entrée. Il peut avoir besoin de connaître le type de contenu de la chaîne avec laquelle il travaille (par exemple, HTML, JavaScript, texte brut) ou même de le modifier. C'est là qu'interviennent les filtres contextuels.

Un filtre contextuel est défini de la même manière qu'un filtre normal, mais son premier paramètre doit être typé comme Latte\Runtime\FilterInfo. Latte reconnaît automatiquement cette signature et passe l'objet FilterInfo lors de l'appel du filtre. Les paramètres suivants reçoivent les arguments du filtre comme d'habitude.

use Latte\Runtime\FilterInfo;
use Latte\ContentType;

$latte->addFilter('money', function (FilterInfo $info, float $amount): string {
	// 1. Vérifiez le type de contenu d'entrée (facultatif, mais recommandé)
	//    Autorisez null (entrée variable) ou texte brut. Refusez si appliqué à du HTML, etc.
	if (!in_array($info->contentType, [null, ContentType::Text], true)) {
		$actualType = $info->contentType ?? 'mixed';
		throw new \RuntimeException(
			"Filtre |money utilisé dans un type de contenu incompatible $actualType. Attendu text ou null."
		);
	}

	// 2. Effectuez la transformation
	$formatted = number_format($amount, 2, '.', ',') . ' EUR';
	$htmlOutput = '<i>' . htmlspecialchars($formatted) . '</i>'; // Assurez un échappement correct !

	// 3. Déclarez le type de contenu de sortie
	$info->contentType = ContentType::Html;

	// 4. Retournez le résultat
	return $htmlOutput;
});

$info->contentType est une constante de chaîne de Latte\ContentType (par exemple, ContentType::Html, ContentType::Text, ContentType::JavaScript, etc.) ou null si le filtre est appliqué à une variable ({$var|filter}). Vous pouvez lire cette valeur pour vérifier le contexte d'entrée, et écrire dessus pour déclarer le type du contexte de sortie.

En définissant le type de contenu sur HTML, vous indiquez à Latte que la chaîne retournée par votre filtre est du HTML sûr. Latte n'appliquera alors pas son échappement automatique par défaut à ce résultat. C'est crucial si votre filtre génère du balisage HTML.

Si votre filtre génère du HTML, vous êtes responsable de l'échappement correct de toutes les données d'entrée utilisées dans ce HTML (comme dans le cas de l'appel htmlspecialchars($formatted) ci-dessus). Omettre cela peut créer des vulnérabilités XSS. Si votre filtre ne retourne que du texte brut, vous n'avez pas besoin de définir $info->contentType.

Filtres sur les blocs

Tous les filtres appliqués aux blocs doivent être contextuels. C'est parce que le contenu du bloc a un type de contenu défini (généralement HTML), dont le filtre doit être conscient.

{block heading|money}1000{/block}
{* Le filtre 'money' recevra '1000' comme deuxième argument
   et $info->contentType sera ContentType::Html *}

Les filtres contextuels offrent un contrôle puissant sur la manière dont les données sont traitées en fonction de leur contexte, permettant des fonctionnalités avancées et garantissant un comportement d'échappement correct, en particulier lors de la génération de contenu HTML.

version: 3.0