Creazione di filtri personalizzati

I filtri sono potenti strumenti per formattare e modificare i dati direttamente nei template Latte. Offrono una sintassi pulita utilizzando il simbolo della pipe (|) per trasformare variabili o risultati di espressioni nel formato di output desiderato.

Cosa sono i filtri?

I filtri in Latte sono essenzialmente funzioni PHP progettate specificamente per trasformare un valore di input in un valore di output. Si applicano utilizzando la notazione con la pipe (|) all'interno delle espressioni del template ({...}).

Comodità: I filtri consentono di incapsulare comuni attività di formattazione (come formattazione di date, modifica del case, troncamento) o manipolazione dei dati in unità riutilizzabili. Invece di ripetere complesso codice PHP nei tuoi template, puoi semplicemente applicare un filtro:

{* Invece di complesso PHP per troncare: *}
{$article->text|truncate:100}

{* Invece di codice per formattare la data: *}
{$event->startTime|date:'Y-m-d H:i'}

{* Applicazione di più trasformazioni: *}
{$product->name|lower|capitalize}

Leggibilità: L'uso dei filtri rende i template più chiari e più focalizzati sulla presentazione, poiché la logica di trasformazione viene spostata nella definizione del filtro.

Sensibilità al contesto: Un vantaggio chiave dei filtri in Latte è la loro capacità di essere sensibili al contesto. Ciò significa che un filtro può riconoscere il tipo di contenuto con cui sta lavorando (HTML, JavaScript, testo semplice, ecc.) e applicare la logica o l'escaping corrispondente, il che è fondamentale per la sicurezza e la correttezza, specialmente quando si genera HTML.

Integrazione con la logica dell'applicazione: Come le funzioni personalizzate, il callable PHP dietro un filtro può essere una closure, un metodo statico o un metodo di istanza. Ciò consente ai filtri di accedere ai servizi o ai dati dell'applicazione, se necessario, anche se il loro scopo principale rimane la trasformazione del valore di input.

Latte fornisce di default un ricco set di filtri standard. I filtri personalizzati ti consentono di estendere questo set con formattazioni e trasformazioni specifiche per il tuo progetto.

Se hai bisogno di eseguire logica basata su più input o non hai un valore primario da trasformare, è probabilmente più appropriato usare una funzione personalizzata. Se hai bisogno di generare markup complesso o controllare il flusso del template, considera un tag personalizzato.

Creazione e registrazione dei filtri

Esistono diversi modi per definire e registrare filtri personalizzati in Latte.

Registrazione diretta tramite addFilter()

Il modo più semplice per aggiungere un filtro è usare il metodo addFilter() direttamente sull'oggetto Latte\Engine. Specifichi il nome del filtro (come verrà usato nel template) e il callable PHP corrispondente.

$latte = new Latte\Engine;

// Filtro semplice senza argomenti
$latte->addFilter('initial', fn(string $s): string => mb_substr($s, 0, 1) . '.');

// Filtro con argomento opzionale
$latte->addFilter('shortify', function (string $s, int $len = 10): string {
	return mb_substr($s, 0, $len);
});

// Filtro che elabora un array
$latte->addFilter('sum', fn(array $numbers): int|float => array_sum($numbers));

Uso nel template:

{$name|initial}                 {* Stampa 'J.' se $name è 'John' *}
{$description|shortify}         {* Usa la lunghezza predefinita di 10 *}
{$description|shortify:50}      {* Usa la lunghezza di 50 *}
{$prices|sum}                   {* Stampa la somma degli elementi nell'array $prices *}

Passaggio degli argomenti:

Il valore a sinistra della pipe (|) viene sempre passato come primo argomento alla funzione del filtro. Qualsiasi parametro specificato dopo i due punti (:) nel template viene passato come argomenti successivi.

{$text|shortify:30}
// Chiama la funzione PHP shortify($text, 30)

Registrazione tramite estensione

Per una migliore organizzazione, specialmente quando si creano set di filtri riutilizzabili o li si condivide come pacchetti, il modo consigliato è registrarli all'interno di un'estensione 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);
	}
}

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

Questo approccio mantiene la logica del tuo filtro incapsulata e la registrazione semplice.

Utilizzo del caricatore di filtri

Latte consente di registrare un caricatore di filtri tramite addFilterLoader(). Si tratta di un unico callable che Latte richiederà per qualsiasi nome di filtro sconosciuto durante la compilazione. Il caricatore restituisce il callable del filtro PHP o null.

$latte = new Latte\Engine;

// Il caricatore può creare/ottenere dinamicamente callable di filtri
$latte->addFilterLoader(function (string $name): ?callable {
	if ($name === 'myLazyFilter') {
		// Immagina qui un'inizializzazione costosa...
		$service = get_some_expensive_service();
		return fn($value) => $service->process($value);
	}
	return null;
});

Questo metodo era principalmente destinato al caricamento pigro di filtri con un'inizializzazione molto costosa. Tuttavia, le pratiche moderne di dependency injection gestiscono solitamente i servizi pigri in modo più efficiente.

I caricatori di filtri aggiungono complessità e generalmente non sono raccomandati a favore della registrazione diretta tramite addFilter() o all'interno di un'estensione tramite getFilters(). Utilizza i caricatori solo se hai una ragione seria e specifica legata a problemi di prestazioni nell'inizializzazione dei filtri che non possono essere risolti altrimenti.

Filtri che utilizzano una classe con attributi

Un altro modo elegante per definire i filtri è utilizzare metodi nella tua classe dei parametri del template. Basta aggiungere l'attributo #[Latte\Attributes\TemplateFilter] al metodo.

use Latte\Attributes\TemplateFilter;

class TemplateParameters
{
	public function __construct(
		public string $description,
		// altri parametri...
	) {}

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

// Passaggio dell'oggetto al template
$params = new TemplateParameters(description: '...');
$latte->render('template.latte', $params);

Latte riconoscerà e registrerà automaticamente i metodi contrassegnati con questo attributo quando l'oggetto TemplateParameters viene passato al template. Il nome del filtro nel template sarà lo stesso del nome del metodo (shortify in questo caso).

{* Uso del filtro definito nella classe dei parametri *}
{$description|shortify:50}

Filtri contestuali

A volte un filtro necessita di più informazioni del semplice valore di input. Potrebbe aver bisogno di conoscere il tipo di contenuto della stringa con cui sta lavorando (ad esempio HTML, JavaScript, testo semplice) o addirittura di modificarlo. Questa è la situazione per i filtri contestuali.

Un filtro contestuale è definito come un filtro normale, ma il suo primo parametro deve essere tipizzato come Latte\Runtime\FilterInfo. Latte riconosce automaticamente questa firma e passa l'oggetto FilterInfo quando chiama il filtro. I parametri successivi ricevono gli argomenti del filtro come di consueto.

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

$latte->addFilter('money', function (FilterInfo $info, float $amount): string {
	// 1. Controlla il tipo di contenuto di input (opzionale, ma raccomandato)
	//    Consenti null (input variabile) o testo semplice. Rifiuta se applicato a HTML ecc.
	if (!in_array($info->contentType, [null, ContentType::Text], true)) {
		$actualType = $info->contentType ?? 'mixed';
		throw new \RuntimeException(
			"Filtro |money usato in un tipo di contenuto incompatibile $actualType. Atteso text o null."
		);
	}

	// 2. Esegui la trasformazione
	$formatted = number_format($amount, 2, '.', ',') . ' EUR';
	$htmlOutput = '<i>' . htmlspecialchars($formatted) . '</i>'; // Assicurati un escaping corretto!

	// 3. Dichiara il tipo di contenuto di output
	$info->contentType = ContentType::Html;

	// 4. Restituisci il risultato
	return $htmlOutput;
});

$info->contentType è una costante stringa da Latte\ContentType (ad esempio ContentType::Html, ContentType::Text, ContentType::JavaScript, ecc.) o null se il filtro è applicato a una variabile ({$var|filter}). Puoi leggere questo valore per controllare il contesto di input e scriverci per dichiarare il tipo di contesto di output.

Impostando il tipo di contenuto su HTML, comunichi a Latte che la stringa restituita dal tuo filtro è HTML sicuro. Latte quindi non applicherà il suo escaping automatico predefinito a questo risultato. Questo è fondamentale se il tuo filtro genera markup HTML.

Se il tuo filtro genera HTML, sei responsabile del corretto escaping di qualsiasi dato di input utilizzato in questo HTML (come nel caso della chiamata htmlspecialchars($formatted) sopra). L'omissione può creare vulnerabilità XSS. Se il tuo filtro restituisce solo testo semplice, non è necessario impostare $info->contentType.

Filtri sui blocchi

Tutti i filtri applicati ai blocchi devono essere contestuali. Questo perché il contenuto del blocco ha un tipo di contenuto definito (solitamente HTML), di cui il filtro deve essere consapevole.

{block heading|money}1000{/block}
{* Il filtro 'money' riceverà '1000' come secondo argomento
   e $info->contentType sarà ContentType::Html *}

I filtri contestuali forniscono un forte controllo su come i dati vengono elaborati in base al loro contesto, consentendo funzionalità avanzate e garantendo un comportamento di escaping corretto, specialmente durante la generazione di contenuto HTML.

versione: 3.0