Latte erweitern

Latte wurde mit Blick auf Erweiterbarkeit entwickelt. Obwohl seine Standard-Suite von Tags, Filtern und Funktionen viele Anwendungsfälle abdeckt, müssen Sie oft Ihre eigene spezifische Logik oder Hilfswerkzeuge hinzufügen. Diese Seite bietet einen Überblick über die Möglichkeiten, Latte zu erweitern, damit es perfekt zu den Anforderungen Ihres Projekts passt – von einfachen Helfern bis hin zu komplexer neuer Syntax.

Möglichkeiten, Latte zu erweitern

Hier ist ein kurzer Überblick über die wichtigsten Möglichkeiten, wie Sie Latte anpassen und erweitern können:

  • Benutzerdefinierte Filter: Zur Formatierung oder Transformation von Daten direkt in der Template-Ausgabe (z. B. {$var|myFilter}). Ideal für Aufgaben wie Datumsformatierung, Textbearbeitung oder Anwendung spezifischer Escapings. Sie können sie auch verwenden, um größere Blöcke von HTML-Inhalten zu bearbeiten, indem Sie den Inhalt in einen anonymen {block} einschließen und einen benutzerdefinierten Filter darauf anwenden.
  • Benutzerdefinierte Funktionen: Zum Hinzufügen wiederverwendbarer Logik, die innerhalb von Ausdrücken im Template aufgerufen werden kann (z. B. {myFunction($arg1, $arg2)}). Nützlich für Berechnungen, Zugriff auf Anwendungs-Hilfsfunktionen oder Generierung kleiner Inhaltsteile.
  • Benutzerdefinierte Tags: Zum Erstellen völlig neuer Sprachkonstrukte ({mytag}...{/mytag} oder n:mytag). Tags bieten die meisten Möglichkeiten, ermöglichen die Definition eigener Strukturen, steuern das Template-Parsing und implementieren komplexe Rendering-Logik.
  • Kompilierungsdurchläufe: Funktionen, die den abstrakten Syntaxbaum (AST) des Templates nach dem Parsen, aber vor der Generierung von PHP-Code modifizieren. Sie werden für fortgeschrittene Optimierungen, Sicherheitsprüfungen (wie die Sandbox) oder automatische Code-Anpassungen verwendet.
  • Benutzerdefinierte Loader: Um zu ändern, wie Latte Template-Dateien findet und lädt (z. B. Laden aus einer Datenbank, verschlüsseltem Speicher usw.).

Die Wahl der richtigen Erweiterungsmethode ist entscheidend. Bevor Sie ein komplexes Tag erstellen, überlegen Sie, ob ein einfacherer Filter oder eine Funktion ausreichen würde. Lassen Sie uns dies am Beispiel der Implementierung eines Lorem ipsum-Generators veranschaulichen, der die Anzahl der zu generierenden Wörter als Argument akzeptiert.

  • Als Tag? {lipsum 40} – Möglich, aber Tags sind besser für Steuerstrukturen oder die Generierung komplexen Markups geeignet. Tags können nicht direkt in Ausdrücken verwendet werden.
  • Als Filter? {=40|lipsum} – Technisch funktioniert das, aber Filter sind dazu gedacht, den Eingabewert zu transformieren. Hier ist 40 ein Argument, nicht der Wert, der transformiert wird. Das wirkt semantisch falsch.
  • Als Funktion? {lipsum(40)} – Dies ist die natürlichste Lösung! Funktionen akzeptieren Argumente und geben Werte zurück, was ideal für die Verwendung in beliebigen Ausdrücken ist: {var $text = lipsum(40)}.

Allgemeine Empfehlung: Verwenden Sie Funktionen für Berechnungen/Generierung, Filter für Transformationen und Tags für neue Sprachkonstrukte oder komplexes Markup. Verwenden Sie Durchläufe zur Manipulation des AST und Loader zum Abrufen von Templates.

Direkte Registrierung

Für projektspezifische Hilfswerkzeuge oder schnelle Erweiterungen ermöglicht Latte die direkte Registrierung von Filtern und Funktionen im Latte\Engine-Objekt.

Zur Registrierung eines Filters verwenden Sie die Methode addFilter(). Das erste Argument Ihrer Filterfunktion ist der Wert vor dem |-Zeichen, und die folgenden Argumente sind diejenigen, die nach dem Doppelpunkt : übergeben werden.

$latte = new Latte\Engine;

// Definition des Filters (Callable-Objekt: Funktion, statische Methode usw.)
$myTruncate = fn(string $s, int $length = 50) => mb_substr($s, 0, $length);

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

// Verwendung im Template: {$text|truncate} oder {$text|truncate:100}

Sie können auch einen Filter Loader registrieren, eine Funktion, die dynamisch Callable-Objekte für Filter basierend auf dem angeforderten Namen bereitstellt:

$latte->addFilterLoader(fn(string $name) => /* gibt Callable-Objekt oder null zurück */);

Zur Registrierung einer Funktion, die in Template-Ausdrücken verwendet werden kann, verwenden Sie addFunction().

$latte = new Latte\Engine;

// Definition der Funktion
$isWeekend = fn(DateTimeInterface $date) => $date->format('N') >= 6;

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

// Verwendung im Template: {if isWeekend($myDate)}Wochenende!{/if}

Weitere Informationen finden Sie in den Abschnitten Erstellen benutzerdefinierter Filter und Funktionen.

Robuster Weg: Latte Extension

Während die direkte Registrierung einfach ist, ist der standardmäßige und empfohlene Weg, Latte-Erweiterungen zu verpacken und zu verteilen, über Extension-Klassen. Eine Extension dient als zentraler Konfigurationspunkt für die Registrierung mehrerer Tags, Filter, Funktionen, Kompilierungsdurchläufe und anderer Elemente.

Warum Extensions verwenden?

  • Organisation: Hält zusammengehörige Erweiterungen (Tags, Filter usw. für eine bestimmte Funktion) in einer Klasse zusammen.
  • Wiederverwendbarkeit und Teilen: Verpacken Sie Ihre Erweiterungen einfach zur Verwendung in anderen Projekten oder zum Teilen mit der Community (z. B. über Composer).
  • Volle Leistung: Benutzerdefinierte Tags und Kompilierungsdurchläufe können nur über Extensions registriert werden.

Registrieren einer Extension

Eine Extension wird bei Latte mit der Methode addExtension() registriert (oder über die Konfigurationsdatei):

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

Wenn Sie mehrere Erweiterungen registrieren und diese gleichnamige Tags, Filter oder Funktionen definieren, hat die zuletzt hinzugefügte Erweiterung Vorrang. Das bedeutet auch, dass Ihre Erweiterungen native Tags/Filter/Funktionen überschreiben können.

Wann immer Sie eine Änderung an der Klasse vornehmen und die automatische Aktualisierung nicht deaktiviert ist, kompiliert Latte Ihre Templates automatisch neu.

Erstellen einer Extension

Um eine benutzerdefinierte Erweiterung zu erstellen, müssen Sie eine Klasse erstellen, die von Latte\Extension erbt. Um eine Vorstellung davon zu bekommen, wie eine solche Erweiterung aussieht, schauen Sie sich die integrierte CoreExtension an.

Sehen wir uns die Methoden an, die Sie implementieren können:

beforeCompile (Latte\Engine $engine)void

Wird vor der Kompilierung des Templates aufgerufen. Die Methode kann beispielsweise für Initialisierungen im Zusammenhang mit der Kompilierung verwendet werden.

getTags(): array

Wird bei der Kompilierung des Templates aufgerufen. Gibt ein assoziatives Array Tag-Name ⇒ Callable-Objekt zurück, wobei es sich um Funktionen zum Parsen von Tags handelt. Weitere Informationen.

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

Das Tag n:baz stellt ein reines n:Attribut dar, also ein Tag, das nur als Attribut geschrieben werden kann.

Bei den Tags foo und bar erkennt Latte automatisch, ob es sich um paarweise Tags handelt, und wenn ja, können sie automatisch mit n:Attributen geschrieben werden, einschließlich Varianten mit Präfixen n:inner-foo und n:tag-foo.

Die Ausführungsreihenfolge solcher n:Attribute wird durch ihre Reihenfolge im von der getTags()-Methode zurückgegebenen Array bestimmt. n:foo wird also immer vor n:bar ausgeführt, auch wenn die Attribute im HTML-Tag in umgekehrter Reihenfolge als <div n:bar="..." n:foo="..."> angegeben sind.

Wenn Sie die Reihenfolge von n:Attributen über mehrere Erweiterungen hinweg bestimmen müssen, verwenden Sie die Hilfsmethode order(), wobei der Parameter before xor after angibt, welche Tags vor oder nach dem Tag sortiert werden.

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

getPasses(): array

Wird bei der Kompilierung des Templates aufgerufen. Gibt ein assoziatives Array Durchlaufname ⇒ Callable-Objekt zurück, wobei es sich um Funktionen handelt, die sogenannte Kompilierungsdurchläufe darstellen, die den AST durchlaufen und modifizieren.

Auch hier kann die Hilfsmethode order() verwendet werden. Der Wert der Parameter before oder after kann * mit der Bedeutung vor/nach allen sein.

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

beforeRender (Latte\Engine $engine)void

Wird vor jedem Rendern des Templates aufgerufen. Die Methode kann beispielsweise zur Initialisierung von Variablen verwendet werden, die während des Renderings verwendet werden.

getFilters(): array

Wird vor dem Rendern des Templates aufgerufen. Gibt Filter als assoziatives Array Filtername ⇒ Callable-Objekt zurück. Weitere Informationen.

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

getFunctions(): array

Wird vor dem Rendern des Templates aufgerufen. Gibt Funktionen als assoziatives Array Funktionsname ⇒ Callable-Objekt zurück. Weitere Informationen.

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

getProviders(): array

Wird vor dem Rendern des Templates aufgerufen. Gibt ein Array von Providern zurück, bei denen es sich normalerweise um Objekte handelt, die von Tags zur Laufzeit verwendet werden. Der Zugriff erfolgt über $this->global->.... Weitere Informationen.

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

getCacheKey (Latte\Engine $engine)mixed

Wird vor dem Rendern des Templates aufgerufen. Der Rückgabewert wird Teil des Schlüssels, dessen Hash im Dateinamen des kompilierten Templates enthalten ist. Für unterschiedliche Rückgabewerte generiert Latte also unterschiedliche Cache-Dateien.

Version: 3.0