Extender Latte

Latte está diseñado pensando en la extensibilidad. Aunque su conjunto estándar de etiquetas, filtros y funciones cubre muchos casos de uso, a menudo necesitará añadir su propia lógica o sus propios auxiliares. Esta página ofrece una visión general de cómo extender Latte para que encaje a la perfección con los requisitos de su proyecto, desde auxiliares sencillos hasta sintaxis nueva y compleja.

Formas de extender Latte

Aquí tiene un repaso rápido de las principales maneras de personalizar y extender Latte:

  • Filtros personalizados: para dar formato o transformar datos directamente en la salida de la plantilla (por ejemplo, {$var|myFilter}). Ideales para tareas como formatear fechas, manipular texto o aplicar un escapado concreto. También puede usarlos para modificar bloques mayores de contenido HTML, envolviendo el contenido en un {block} anónimo y aplicando un filtro propio.
  • Funciones personalizadas: para añadir lógica reutilizable que se pueda llamar dentro de las expresiones de la plantilla (por ejemplo, {myFunction($arg1, $arg2)}). Útiles para cálculos, para acceder a auxiliares de la aplicación o para generar pequeños fragmentos de contenido.
  • Etiquetas personalizadas: para crear construcciones del lenguaje totalmente nuevas ({mytag}...{/mytag} o n:mytag). Las etiquetas son lo más potente: permiten definir estructuras propias, controlar el análisis de la plantilla e implementar lógica de renderizado compleja.
  • Pases del compilador: funciones que modifican el árbol de sintaxis abstracta (AST) de la plantilla después del análisis, pero antes de generar el código PHP. Se usan para optimizaciones avanzadas, comprobaciones de seguridad (como el Sandbox) o modificaciones automáticas del código.
  • Loaders personalizados: para cambiar cómo encuentra y carga Latte los archivos de plantilla (por ejemplo, cargarlos de una base de datos, de un almacenamiento cifrado, etc.).

Elegir el método de extensión adecuado es clave. Antes de crear una etiqueta compleja, valore si bastaría con un filtro o una función más sencillos. Veámoslo con un ejemplo: implementar un generador de Lorem ipsum que reciba como argumento el número de palabras a generar.

  • ¿Como etiqueta? {lipsum 40} – es posible, pero las etiquetas encajan mejor con estructuras de control o con la generación de marcado complejo. Además, no se pueden usar directamente dentro de expresiones.
  • ¿Como filtro? {=40|lipsum} – técnicamente funciona, pero los filtros están pensados para transformar la entrada. Aquí 40 es un argumento, no el valor que se transforma. Resulta semánticamente incorrecto.
  • ¿Como función? {lipsum(40)} – ¡esta es la opción más natural! Las funciones aceptan argumentos y devuelven valores, lo que las hace perfectas para usarlas en cualquier expresión: {var $text = lipsum(40)}.

Orientación general: use funciones para cálculos y generación, filtros para transformar y etiquetas para nuevas estructuras del lenguaje o marcado complejo. Use pases para manipular el AST y loaders para obtener las plantillas.

Registro directo

Para auxiliares específicos de un proyecto o añadidos rápidos, Latte permite registrar filtros y funciones directamente en el objeto Latte\Engine.

Use addFilter() para registrar un filtro. El primer argumento de su función de filtro será el valor situado antes de la tubería |, y los siguientes argumentos, los que se pasen tras los dos puntos :.

$latte = new Latte\Engine;

// Definición del filtro (callable: función, método estático, etc.)
$myTruncate = fn(string $s, int $length = 50) => mb_substr($s, 0, $length);

// Regístrelo
$latte->addFilter('truncate', $myTruncate);

// Uso en la plantilla: {$text|truncate} o {$text|truncate:100}

Use addFunction() para registrar una función utilizable dentro de las expresiones de la plantilla.

$latte = new Latte\Engine;

// Definición de la función
$isWeekend = fn(DateTimeInterface $date) => $date->format('N') >= 6;

// Regístrelo
$latte->addFunction('isWeekend', $isWeekend);

// Uso en la plantilla: {if isWeekend($myDate)}Weekend!{/if}

Para más detalles, vea Creación de filtros personalizados y Funciones.

La forma robusta: extensión de Latte

Aunque el registro directo es sencillo, la forma estándar y recomendada de agrupar y distribuir personalizaciones de Latte son las clases de extensión. Una extensión actúa como punto central de configuración para registrar varias etiquetas, filtros, funciones, pases del compilador y más.

¿Por qué usar extensiones?

  • Organización: mantiene juntas en una misma clase las personalizaciones relacionadas (etiquetas, filtros, etc. de una funcionalidad concreta).
  • Reutilización y distribución: empaquete sus extensiones con facilidad para usarlas en otros proyectos o compartirlas con la comunidad (por ejemplo, mediante Composer).
  • Toda la potencia: las etiquetas personalizadas y los pases del compilador solo se pueden registrar mediante extensiones.

Registrar una extensión

La extensión se registra en Latte con addExtension() (o mediante el archivo de configuración):

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

Si registra varias extensiones y estas definen etiquetas, filtros o funciones con el mismo nombre, gana la última añadida. Esto implica también que sus extensiones pueden sobrescribir etiquetas, filtros y funciones nativos.

Siempre que haga un cambio en una clase y el refresco automático no esté desactivado, Latte recompilará sus plantillas automáticamente.

Crear una extensión

Para crear su propia extensión debe crear una clase que herede de Latte\Extension. Para hacerse una idea de cómo es una extensión, eche un vistazo a la CoreExtension integrada.

Veamos qué métodos puede implementar:

beforeCompile (Latte\Engine $engine)void

Se llama antes de compilar la plantilla. El método sirve, por ejemplo, para inicializaciones relacionadas con la compilación.

getTags(): array

Se llama al compilar la plantilla. Devuelve un array asociativo nombre de etiqueta ⇒ callable, que son las funciones de análisis de las etiquetas. Más información.

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

La etiqueta n:baz representa un n:atributo puro, es decir, una etiqueta que solo se puede escribir como atributo.

En el caso de las etiquetas foo y bar, Latte reconocerá automáticamente si son pares y, en tal caso, podrán escribirse automáticamente con n:atributos, incluidas las variantes con los prefijos n:inner-foo y n:tag-foo.

El orden de ejecución de esos n:atributos lo determina su orden en el array devuelto por getTags(). Así, n:foo se ejecuta siempre antes que n:bar, aunque los atributos aparezcan en orden inverso en la etiqueta HTML, como <div n:bar="..." n:foo="...">.

Si necesita fijar el orden de los n:atributos entre varias extensiones, use el método auxiliar order(), donde el parámetro before o after determina qué etiquetas se ordenan antes o después de la etiqueta.

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

getPasses(): array

Se llama al compilar la plantilla. Devuelve un array asociativo nombre del pase ⇒ callable, que son las funciones que representan los llamados pases del compilador, los cuales recorren y modifican el AST.

También aquí se puede usar el método auxiliar order(). El valor de los parámetros before o after puede ser *, con el significado de antes o después de todos.

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

beforeRender (Latte\Runtime\Template $template)void

Se llama antes de cada renderizado de la plantilla. El método sirve, por ejemplo, para inicializar variables usadas durante el renderizado.

afterRender (Latte\Runtime\Template $template)void

Se llama después de cada renderizado de la plantilla. Se ejecuta incluso cuando el renderizado termina antes de tiempo con {exitIf} o lo interrumpe una excepción, así que es el lugar adecuado para tareas de limpieza o medición.

getFilters(): array

Se llama al registrar la extensión con el método addExtension(). Devuelve los filtros como un array asociativo nombre del filtro ⇒ callable. Más información.

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

getFunctions(): array

Se llama al registrar la extensión con el método addExtension(). Devuelve las funciones como un array asociativo nombre de la función ⇒ callable. Más información.

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

getProviders(): array

Se llama al registrar la extensión con el método addExtension(). Devuelve un array de proveedores, que suelen ser objetos que las etiquetas usan en tiempo de ejecución. Se accede a ellos mediante $this->global->.... Más información.

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

getCacheKey (Latte\Engine $engine)mixed

Se llama antes de renderizar la plantilla. El valor devuelto pasa a formar parte de la clave cuyo hash aparece en el nombre del archivo de plantilla compilada. Así, para valores de retorno distintos, Latte generará archivos de caché distintos.

versión: 3.x