Estendere Latte

Latte è progettato pensando all'estensibilità. Il suo insieme standard di tag, filtri e funzioni copre molti casi d'uso, ma spesso serve aggiungere logica o strumenti di supporto specifici. Questa pagina offre una panoramica dei modi in cui potete estendere Latte per adattarlo perfettamente alle esigenze del vostro progetto, dai semplici helper a nuove sintassi complesse.

Modi per estendere Latte

Ecco una rapida panoramica dei principali modi in cui potete personalizzare ed estendere Latte:

  • Filtri personalizzati: per formattare o trasformare i dati direttamente nell'output del template (per esempio {$var|myFilter}). Ideali per la formattazione delle date, la manipolazione del testo o l'applicazione di un escaping specifico. Potete usarli anche per modificare blocchi più ampi di contenuto HTML, racchiudendo il contenuto in un {block} anonimo e applicandovi un filtro personalizzato.
  • Funzioni personalizzate: per aggiungere logica riutilizzabile richiamabile nelle espressioni del template (per esempio {myFunction($arg1, $arg2)}). Utili per i calcoli, per accedere agli helper dell'applicazione o per generare piccole porzioni di contenuto.
  • Tag personalizzati: per creare costrutti linguistici completamente nuovi ({mytag}...{/mytag} oppure n:mytag). I tag offrono il massimo delle possibilità: permettono di definire strutture proprie, di controllare l'analisi del template e di implementare logiche di rendering complesse.
  • Compiler pass: funzioni che modificano l'albero sintattico astratto (AST) del template dopo l'analisi ma prima della generazione del codice PHP. Servono per ottimizzazioni avanzate, controlli di sicurezza (come la Sandbox) o modifiche automatiche del codice.
  • Loader personalizzati: per cambiare il modo in cui Latte trova e carica i file dei template (per esempio caricandoli da un database, da uno storage cifrato ecc.).

Scegliere il metodo di estensione giusto è fondamentale. Prima di creare un tag complesso, valutate se non basti un filtro o una funzione più semplice. Illustriamolo con un esempio: implementare un generatore di Lorem ipsum che riceve come argomento il numero di parole da generare.

  • Come tag? {lipsum 40} – possibile, ma i tag sono più adatti alle strutture di controllo o alla generazione di markup complesso. I tag non si possono usare direttamente nelle espressioni.
  • Come filtro? {=40|lipsum} – tecnicamente funziona, ma i filtri servono a trasformare un input. Qui 40 è un argomento, non il valore da trasformare. Semanticamente non torna.
  • Come funzione? {lipsum(40)} – è la soluzione più naturale! Le funzioni accettano argomenti e restituiscono valori, il che le rende perfette per l'uso in qualsiasi espressione: {var $text = lipsum(40)}.

Regola generale: usate le funzioni per i calcoli e la generazione, i filtri per la trasformazione e i tag per nuove strutture del linguaggio o markup complesso. Usate i pass per manipolare l'AST e i loader per il recupero dei template.

Registrazione diretta

Per gli helper specifici di un progetto o per aggiunte rapide, Latte permette di registrare filtri e funzioni direttamente sull'oggetto Latte\Engine.

Usate addFilter() per registrare un filtro. Il primo argomento della funzione del filtro sarà il valore che precede la pipe |, mentre gli argomenti successivi sono quelli passati dopo i due punti :.

$latte = new Latte\Engine;

// definizione del filtro (callable: funzione, metodo statico ecc.)
$myTruncate = fn(string $s, int $length = 50) => mb_substr($s, 0, $length);

// lo registra
$latte->addFilter('truncate', $myTruncate);

// uso nel template: {$text|truncate} oppure {$text|truncate:100}

Usate addFunction() per registrare una funzione utilizzabile nelle espressioni del template.

$latte = new Latte\Engine;

// definizione della funzione
$isWeekend = fn(DateTimeInterface $date) => $date->format('N') >= 6;

// la registra
$latte->addFunction('isWeekend', $isWeekend);

// uso nel template: {if isWeekend($myDate)}Fine settimana!{/if}

Per maggiori dettagli vedi Creare filtri personalizzati e Funzioni.

La via solida: le estensioni di Latte

La registrazione diretta è semplice, ma il modo standard e consigliato di raccogliere e distribuire le personalizzazioni di Latte sono le classi Extension. Un'estensione funge da punto centrale di configurazione per registrare più tag, filtri, funzioni, compiler pass e altro ancora.

Perché usare le estensioni?

  • Organizzazione: tiene insieme in un'unica classe le personalizzazioni correlate (tag, filtri ecc. di una determinata funzionalità).
  • Riusabilità e condivisione: permette di impacchettare facilmente le vostre estensioni per usarle in altri progetti o condividerle con la comunità (per esempio via Composer).
  • Piena potenza: i tag personalizzati e i compiler pass possono essere registrati soltanto tramite un'estensione.

Registrare un'estensione

Un'estensione si registra in Latte con addExtension() (oppure tramite il file di configurazione):

$latte = new Latte\Engine;
$latte->addExtension(new MyProjectExtension);

Se registrate più estensioni e queste definiscono tag, filtri o funzioni con lo stesso nome, vince l'estensione aggiunta per ultima. Ne consegue anche che le vostre estensioni possono sovrascrivere tag, filtri e funzioni nativi.

Ogni volta che modificate una classe, se l'aggiornamento automatico non è disattivato, Latte ricompila automaticamente i vostri template.

Creare un'estensione

Per creare una vostra estensione dovete creare una classe che eredita da Latte\Extension. Per farvi un'idea dell'aspetto di un'estensione, date un'occhiata alla CoreExtension integrata.

Vediamo quali metodi potete implementare:

beforeCompile (Latte\Engine $engine)void

Viene chiamato prima della compilazione del template. Il metodo può servire, per esempio, per le inizializzazioni legate alla compilazione.

getTags(): array

Viene chiamato durante la compilazione del template. Restituisce un array associativo nome del tag ⇒ callable, cioè le funzioni di analisi dei tag. Per saperne di più.

public function getTags(): array
{
	return [
		'foo' => FooNode::create(...),
		'bar' => BarNode::create(...),
		'n:baz' => NBazNode::create(...),
		// ...
	];
}

Il tag n:baz rappresenta un puro n:attributo, è cioè un tag scrivibile solo come attributo.

Nel caso dei tag foo e bar, Latte riconosce automaticamente se sono di tipo pari e, in tal caso, essi possono essere scritti automaticamente con gli n:attributi, incluse le varianti con i prefissi n:inner-foo e n:tag-foo.

L'ordine di esecuzione di questi n:attributi è determinato dal loro ordine nell'array restituito da getTags(). Perciò n:foo viene sempre eseguito prima di n:bar, anche se nel tag HTML gli attributi sono indicati nell'ordine inverso, come <div n:bar="..." n:foo="...">.

Se avete bisogno di stabilire l'ordine degli n:attributi tra più estensioni, usate il metodo di supporto order(), dove il parametro before e/o after determina quali tag vengono ordinati prima o dopo il tag.

public function getTags(): array
{
	return [
		'foo' => self::order(FooNode::create(...), before: 'bar'),
		'bar' => self::order(BarNode::create(...), after: ['block', 'snippet']),
	];
}

getPasses(): array

Viene chiamato durante la compilazione del template. Restituisce un array associativo nome del pass ⇒ callable, cioè le funzioni che rappresentano i cosiddetti compiler pass, che attraversano e modificano l'AST.

Anche qui si può usare il metodo di supporto order(). Il valore dei parametri before o after può essere *, con il significato di prima/dopo tutti.

public function getPasses(): array
{
	return [
		'optimize' => Passes::optimizePass(...),
		'sandbox' => self::order($this->sandboxPass(...), before: '*'),
		// ...
	];
}

beforeRender (Latte\Runtime\Template $template)void

Viene chiamato prima di ogni rendering del template. Il metodo può servire, per esempio, a inizializzare le variabili usate durante il rendering.

afterRender (Latte\Runtime\Template $template)void

Viene chiamato dopo ogni rendering del template. Viene eseguito anche quando il rendering termina anticipatamente con {exitIf} o viene interrotto da un'eccezione, il che ne fa il posto giusto per la pulizia o per le misurazioni.

getFilters(): array

Viene chiamato al momento della registrazione dell'estensione con il metodo addExtension(). Restituisce i filtri come array associativo nome del filtro ⇒ callable. Per saperne di più.

public function getFilters(): array
{
	return [
		'batch' => $this->batchFilter(...),
		'trim' => $this->trimFilter(...),
		// ...
	];
}

getFunctions(): array

Viene chiamato al momento della registrazione dell'estensione con il metodo addExtension(). Restituisce le funzioni come array associativo nome della funzione ⇒ callable. Per saperne di più.

public function getFunctions(): array
{
	return [
		'clamp' => $this->clampFunction(...),
		'divisibleBy' => $this->divisibleByFunction(...),
		// ...
	];
}

getProviders(): array

Viene chiamato al momento della registrazione dell'estensione con il metodo addExtension(). Restituisce un array di provider, che di solito sono oggetti usati dai tag in fase di esecuzione. Vi si accede tramite $this->global->.... Per saperne di più.

public function getProviders(): array
{
	return [
		'myFoo' => $this->foo,
		'myBar' => $this->bar,
		// ...
	];
}

getCacheKey (Latte\Engine $engine)mixed

Viene chiamato prima del rendering del template. Il valore restituito entra a far parte della chiave il cui hash è contenuto nel nome del file del template compilato. Per valori restituiti diversi, quindi, Latte genererà file di cache diversi.

versione: 3.x