Praktiken für Entwickler
Installation
Am besten installieren Sie Latte mit Composer:
composer require latte/latte
Unterstützte PHP-Versionen (gilt für die jeweils letzten Patch-Versionen von Latte):
| Version | kompatibel mit PHP |
|---|---|
| Latte 3.1 | PHP 8.2 – 8.5 |
| Latte 3.0 | PHP 8.0 – 8.5 |
Wie rendert man ein Template?
Wie rendert man ein Template? Verwenden Sie einfach diesen schlichten Code:
$latte = new Latte\Engine;
// Cache-Verzeichnis
$latte->setCacheDirectory('/path/to/tempdir');
$params = [ /* Variablen des Templates */ ];
// oder $params = new TemplateParameters(/* ... */);
// in die Ausgabe rendern
$latte->render('template.latte', $params);
// oder in eine Variable rendern
$output = $latte->renderToString('template.latte', $params);
Die Parameter können Arrays sein oder besser noch ein Objekt, das Typprüfung und Vorschläge im Editor mitbringt.
Beispiele zur Verwendung finden Sie auch im Repository Latte examples.
Leistung und Caching
Latte-Templates sind ausgesprochen schnell, weil Latte sie direkt in PHP-Code kompiliert und auf der Festplatte cacht. Sie haben damit keinerlei zusätzlichen Overhead gegenüber Templates, die in reinem PHP geschrieben sind.
Der Cache wird bei jeder Änderung der Quelldatei automatisch neu erzeugt. Sie können Ihre Latte-Templates während der Entwicklung also bequem bearbeiten und die Änderungen sofort im Browser sehen. In der Produktionsumgebung lässt sich diese Funktion abschalten, um ein wenig Leistung zu sparen:
$latte->setAutoRefresh(false);
Beim Deployment auf einem Produktionsserver kann das erstmalige Erzeugen des Caches, besonders bei größeren Anwendungen, verständlicherweise eine Weile dauern. Latte hat eine eingebaute Vorbeugung gegen cache stampede. Das ist die Situation, in der beim Server eine große Zahl gleichzeitiger Requests eingeht und diese, weil der Cache von Latte noch nicht existiert, ihn alle zugleich erzeugen würden. Was die CPU in die Höhe treibt. Latte ist schlau, und bei mehreren gleichzeitigen Requests erzeugt nur der erste Thread den Cache, die übrigen warten und nutzen ihn dann.
Sie können den Cache auch beim Deployment vorab erzeugen (zum Beispiel in einem Deploy-Skript), und zwar mit der Methode
Engine::warmupCache(). Sie kompiliert das angegebene Template im Voraus in den Cache, sodass der erste Besucher nicht
warten muss: $latte->warmupCache('template.latte').
Wege, Latte zu erweitern
Latte lässt sich auf mehreren Wegen anpassen, von einfachen Helfern bis zu völlig neuen Sprachkonstrukten. Die Seite Latte erweitern behandelt sie ausführlich; hier ein kurzer Überblick:
- Eigene Filter: zum Formatieren oder Umwandeln von Daten in der
Ausgabe des Templates (z. B.
{$var|myFilter}). - Eigene Funktionen: für eigene Logik, die Sie in
Template-Ausdrücken aufrufen (z. B.
{myFunction($arg)}). - Eigene Tags: für völlig neue Sprachkonstrukte
(
{mytag}...{/mytag}odern:mytag). - Compiler-Pässe: Funktionen, die den AST des Templates zwischen dem Parsen und dem Erzeugen des PHP-Codes verändern (zum Beispiel Optimierungen oder Sicherheitsprüfungen).
- Eigene Loader: zum Ändern der Art, wie Latte Template-Dateien findet und lädt.
Wollen Sie Ihre Erweiterungen über Projekte hinweg wiederverwenden oder mit anderen teilen, bündeln Sie sie in einer Klasse Latte-Extension.
Parameter als Klasse
Besser, als dem Template die Variablen als Array zu übergeben, ist es, eine Klasse zu erstellen. Sie bekommen typsichere Schreibweise, gute Vorschläge in der IDE und einen Weg, Filter und Funktionen zu registrieren.
class MailTemplateParameters
{
public function __construct(
public string $lang,
public Address $address,
public string $subject,
public array $items,
public ?float $price = null,
) {}
}
$latte->render('mail.latte', new MailTemplateParameters(
lang: $this->lang,
subject: $title,
price: $this->getPrice(),
items: [],
address: $userAddress,
));
Automatisches Escaping einer Variablen abschalten
Enthält eine Variable einen HTML-String, können Sie sie so kennzeichnen, dass Latte sie nicht automatisch (und damit doppelt)
escapt. Damit müssen Sie im Template kein |noescape angeben.
Am einfachsten hüllen Sie den String in ein Objekt Latte\Runtime\Html:
$params = [
'articleBody' => new Latte\Runtime\Html($article->htmlBody),
];
Latte escapt außerdem keine Objekte, die das Interface Latte\Runtime\HtmlStringable implementieren. Sie können
also eine eigene Klasse erstellen, deren Methode __toString() HTML-Code zurückgibt, der nicht automatisch
escapt wird:
class Emphasis implements Latte\Runtime\HtmlStringable
{
public function __construct(
private string $str,
) {
}
public function __toString(): string
{
return '<em>' . htmlspecialchars($this->str) . '</em>';
}
}
$params = [
'foo' => new Emphasis('hello'),
];
Die Methode __toString muss korrektes HTML zurückgeben und für das Escaping der Parameter
sorgen, sonst kann eine XSS-Sicherheitslücke entstehen!
Wie man Latte um Filter, Tags usw. erweitert
Wie fügt man Latte einen eigenen Filter, eine Funktion, einen Tag usw. hinzu? Das erfahren Sie im Kapitel Latte erweitern. Wollen Sie Ihre Änderungen in verschiedenen Projekten wiederverwenden oder mit anderen teilen, sollten Sie anschließend eine Extension erstellen.
Beliebiger Code im Template {php ...}
Innerhalb des Tags {do} lassen sich nur PHP-Ausdrücke
schreiben, Sie können dort also zum Beispiel keine Konstrukte wie if ... else oder mit einem Semikolon
abgeschlossene Anweisungen einfügen.
Sie können jedoch die Extension RawPhpExtension registrieren, die den Tag {php ...} hinzufügt.
Damit lässt sich beliebiger PHP-Code einfügen. Er unterliegt keinen Regeln des Sandbox-Modus, die Verwendung liegt also in der
Verantwortung des Template-Autors.
$latte->addExtension(new Latte\Essential\RawPhpExtension);
Prüfung des generierten Codes
Latte kompiliert Templates in PHP-Code. Es sorgt selbstverständlich dafür, dass der erzeugte Code syntaktisch gültig ist.
Bei der Verwendung von Erweiterungen Dritter oder von RawPhpExtension kann Latte die Korrektheit der erzeugten Datei
jedoch nicht garantieren. Außerdem lässt sich in PHP Code schreiben, der syntaktisch korrekt, aber verboten ist (zum Beispiel
das Zuweisen eines Werts an die Variable $this) und einen PHP Compile Error verursacht. Schreiben Sie eine solche
Operation in ein Template, landet sie auch im erzeugten PHP-Code. Da es in PHP über zweihundert verschiedene verbotene
Operationen gibt, strebt Latte nicht an, sie zu erkennen. PHP selbst meldet sie beim Rendern, was üblicherweise kein
Problem ist.
Es gibt jedoch Situationen, in denen Sie schon beim Kompilieren des Templates wissen wollen, dass es keine PHP Compile Errors
enthält. Vor allem, wenn Templates von Benutzern bearbeitet werden können oder wenn Sie die Sandbox verwenden. Lassen Sie die Templates in einem solchen Fall beim Kompilieren
prüfen. Diese Funktion aktivieren Sie mit der Methode Engine::enablePhpLinter(). Da sie für die Prüfung das
PHP-Binary aufrufen muss, übergeben Sie dessen Pfad als Parameter:
$latte = new Latte\Engine;
$latte->enablePhpLinter('/path/to/php');
try {
$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
// fängt Fehler von Latte und auch Compile Error in PHP ab
echo 'Error: ' . $e->getMessage();
}
Locale
Latte erlaubt es, das Locale zu setzen, was die Formatierung von Zahlen und Datumsangaben sowie die Sortierung beeinflusst.
Gesetzt wird es mit der Methode setLocale(). Der Bezeichner des Locale folgt dem Standard IETF language tag, den die
PHP-Erweiterung intl verwendet. Er besteht aus einem Sprachcode und gegebenenfalls einem Ländercode, zum Beispiel
en_US für Englisch in den Vereinigten Staaten, de_DE für Deutsch in Deutschland usw.
$latte = new Latte\Engine;
$latte->setLocale('en_US');
Die Einstellung des Locale wirkt sich auf die Filter localDate, sort, number und bytes aus.
Erfordert die PHP-Erweiterung intl. Die Einstellung in Latte beeinflusst die globale
Locale-Einstellung in PHP nicht.
Strikter Modus
Im strikten Parsing-Modus prüft Latte auf fehlende schließende HTML-Tags und verbietet außerdem die Verwendung der Variablen
$this. Zum Einschalten:
$latte = new Latte\Engine;
$latte->setFeature(Latte\Feature::StrictParsing);
Um Templates mit dem Header declare(strict_types=1) zu erzeugen, gehen Sie so vor:
$latte = new Latte\Engine;
$latte->setFeature(Latte\Feature::StrictTypes);
Seit Latte 3.1 sind strikte Typen standardmäßig aktiviert. Sie können sie mit
$latte->setFeature(Latte\Feature::StrictTypes, false) abschalten.
Migrationswarnungen
Latte 3.1 ändert das Verhalten einiger HTML-Attribute. So lassen
null-Werte das Attribut nun entfallen, statt einen leeren String auszugeben. Um die Stellen leicht zu finden, an
denen sich diese Änderung auf Ihre Templates auswirkt, können Sie Migrationswarnungen aktivieren:
$latte->setFeature(Latte\Feature::MigrationWarnings);
Ist das aktiviert, prüft Latte die gerenderten Attribute und löst eine Benutzerwarnung (E_USER_WARNING) aus,
wenn sich die Ausgabe von der unterscheidet, die Latte 3.0 erzeugt hätte. Wenn Sie auf eine Warnung stoßen, wenden Sie eine der
Lösungen an:
- Ist die neue Ausgabe für Ihren Anwendungsfall richtig (Sie ziehen es z. B. vor, dass das Attribut bei
nullverschwindet), unterdrücken Sie die Warnung durch Ergänzen des Filters|accept - Wollen Sie, dass das Attribut als leer gerendert wird (z. B.
title=""), statt beinullzu entfallen, geben Sie einen leeren String als Rückfall an:title={$val ?? ''} - Brauchen Sie unbedingt das alte Verhalten (z. B. die Ausgabe
"1"fürtruestatt"true"), wandeln Sie den Wert ausdrücklich in einen String um:data-foo={(string) $val}
Sind alle Warnungen aufgelöst, schalten Sie die Migrationswarnungen ab und entfernen Sie alle
|accept-Filter aus Ihren Templates, denn sie werden nicht mehr gebraucht.
Gültigkeitsbereich der Schleifenvariablen
Standardmäßig bleiben Variablen, die in einer {foreach}-Schleife definiert werden (etwa $key und
$value), auch nach dem Ende der Schleife zugänglich – genau wie in PHP selbst. Das kann zum ungewollten
Überschreiben von Variablen führen, wenn eine Schleifenvariable denselben Namen hat wie eine bestehende Template-Variable.
Die Funktion ScopedLoopVariables beschränkt den Gültigkeitsbereich der Schleifenvariablen auf den
Schleifenkörper. Nach dem Ende der Schleife wird der ursprüngliche Wert der Variablen wiederhergestellt (sofern sie vorher
existierte) oder die Variable aufgehoben:
$latte = new Latte\Engine;
$latte->setFeature(Latte\Feature::ScopedLoopVariables);
Beispiel für den Unterschied:
{var $item = 'original'}
{foreach [1, 2] as $item}{$item}, {/foreach}
{$item}
Ohne ScopedLoopVariables: gibt 1, 2, 2 aus (die Variable wird überschrieben) Mit
ScopedLoopVariables: gibt 1, 2, original aus (die Variable wird wiederhergestellt)
Das funktioniert auch mit Destructuring-Syntax, z. B. {foreach $array as [$a, $b]}.
Schleifenvariablen, die Referenzen verwenden ({foreach $array as &$value}) oder Properties
zuweisen ({foreach $array as $obj->prop}), werden nicht auf den Schleifenkörper beschränkt, weil das ihren Zweck
zunichtemachen würde.
Automatisches Entfernen der Einrückung
Bei der Verwendung von Paar-Tags wie {if}, {foreach} oder {block} rücken Sie den
verschachtelten Inhalt der Lesbarkeit halber oft ein. Diese Einrückung landet standardmäßig jedoch in der erzeugten Ausgabe.
Die Funktion Dedent entfernt sie automatisch, sodass die Ausgabe sauber bleibt, egal wie tief Sie Ihre Latte-Tags
verschachteln:
$latte = new Latte\Engine;
$latte->setFeature(Latte\Feature::Dedent);
Beispiel:
{if true}
Hello
World
{/if}
Ohne Dedent enthielte die Ausgabe die Einrückung (\tHello\n\tWorld\n). Mit Dedent wird
die Einrückung entfernt und die Ausgabe lautet Hello\nWorld\n.
Eine tiefere Einrückung innerhalb eines Blocks bleibt relativ zur Grundeinrückung erhalten:
{if true}
Hello
Indented
{/if}
Ausgabe: Hello\n\tIndented\n.
Die Einrückung innerhalb eines Blocks muss einheitlich sein (entweder Tabulatoren oder Leerzeichen). Werden sie gemischt,
wirft Latte eine Exception Inconsistent indentation.
Übersetzen in Templates
Verwenden Sie die Extension TranslatorExtension, um dem Template {_...}, {translate} und den Filter translate hinzuzufügen. Sie dienen dazu, Werte oder
Teile des Templates in andere Sprachen zu übersetzen. Der Parameter ist das Callable, das die Übersetzung durchführt, oder ein
Objekt vom Typ Nette\Localization\Translator (übergeben Sie null, um Übersetzungen abzuschalten):
class MyTranslator
{
public function __construct(private string $lang)
{}
public function translate(string $original): string
{
// $translated aus $original gemäß $this->lang erzeugen
return $translated;
}
}
$translator = new MyTranslator($lang);
$extension = new Latte\Essential\TranslatorExtension(
$translator->translate(...), // [$translator, 'translate'] in PHP 8.0
);
$latte->addExtension($extension);
Der Translator wird zur Laufzeit beim Rendern des Templates aufgerufen. Latte kann jedoch alle statischen Texte während der Kompilierung des Templates übersetzen. Das spart Leistung, denn jeder String wird nur einmal übersetzt und die entstandene Übersetzung wird in die kompilierte Datei geschrieben. Im Cache-Verzeichnis entstehen dadurch mehrere kompilierte Versionen des Templates, eine für jede Sprache. Dazu müssen Sie lediglich die Sprache als zweiten Parameter angeben:
$extension = new Latte\Essential\TranslatorExtension(
$translator->translate(...),
$lang,
);
Mit statischem Text meinen wir zum Beispiel {_'hello'} oder {translate}hello{/translate}. Nicht
statischer Text wie {_$foo} wird weiterhin zur Laufzeit übersetzt.
Das Template kann dem Translator über {_$original, foo: bar} oder {translate foo: bar} auch weitere
Parameter übergeben, die er als Array $params erhält:
public function translate(string $original, ...$params): string
{
// $params['foo'] === 'bar'
}
Debugging und Tracy
Latte versucht, die Entwicklung so angenehm wie möglich zu machen. Zum Debuggen gibt es die drei Tags {dump}, {debugbreak} und {trace}.
Am bequemsten wird es, wenn Sie das großartige Debugging-Tool Tracy installieren und das Latte-Plugin aktivieren:
// aktiviert Tracy
Tracy\Debugger::enable();
$latte = new Latte\Engine;
// aktiviert die Extension von Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
Sie sehen nun alle Fehler auf einer übersichtlichen roten Seite, einschließlich der Fehler in Templates mit Hervorhebung von Zeile und Spalte (Video). Zugleich erscheint unten rechts in der sogenannten Tracy Bar ein Reiter für Latte, in dem Sie übersichtlich alle gerenderten Templates und ihre Beziehungen sehen (samt der Möglichkeit, in das Template oder den kompilierten Code zu klicken) sowie die Variablen:

Da Latte Templates in lesbaren PHP-Code kompiliert, können Sie sie in Ihrer IDE bequem durchsteppen.
Linter: Prüfung der Syntax von Templates
Das Werkzeug Linter dient dazu, alle Templates zu prüfen. Sein Zweck ist es, die angegebenen Dateien zu durchsuchen und sicherzustellen, dass sie keine Syntaxfehler und keine Verweise auf nicht existierende Tags, Filter, Funktionen, Klassen oder ähnliche Konstrukte enthalten.
Der Linter wird von der Kommandozeile aus ausgeführt:
vendor/bin/latte-lint <path>
Mit dem Parameter --strict aktivieren Sie den Strikter Modus. Der Parameter
--debug gibt den Namen jeder verarbeiteten Datei und vollständige Details der Exception aus, was bei der
Fehlersuche hilft.
Wenn Sie eigene Tags, Filter oder andere Latte-Extensions verwenden, müssen Sie eine eigene Variante des Linters erstellen,
zum Beispiel custom-latte-lint. In diesem Skript registrieren Sie alle nötigen Extensions, bevor die eigentliche
Prüfung der Templates stattfindet:
#!/usr/bin/env php
<?php
// hier den tatsächlichen Pfad zur Datei autoload.php eintragen
require __DIR__ . '/vendor/autoload.php';
$path = $argv[1] ?? '.';
$linter = new Latte\Tools\Linter;
$latte = $linter->getEngine();
// hier Ihre einzelnen Extensions hinzufügen
$latte->addExtension(/* ... */);
$ok = $linter->scanDirectory($path);
exit($ok ? 0 : 1);
Alternativ können Sie dem Linter Ihr eigenes Objekt Latte\Engine übergeben:
$latte = new Latte\Engine;
// hier konfigurieren wir das Objekt $latte
$linter = new Latte\Tools\Linter(engine: $latte);
Der so angepasste Linter lässt sich dann genauso verwenden wie das Standardwerkzeug, nur mit vollständiger Kenntnis all Ihrer eigenen Extensions.
Templates aus einem String laden
Müssen Sie Templates statt aus Dateien aus Strings laden, etwa zu Testzwecken? Dabei hilft Ihnen der StringLoader:
$latte->setLoader(new Latte\Loaders\StringLoader([
'main.file' => '{include other.file}',
'other.file' => '{if true} {$var} {/if}',
]));
$latte->render('main.file', $params);
Exception-Handler
Sie können einen eigenen Handler für erwartete Exceptions definieren. An ihn werden Exceptions übergeben, die innerhalb von
{try} und in der Sandbox entstehen.
$loggingHandler = function (Throwable $e, Latte\Runtime\Template $template) use ($logger) {
$logger->log($e);
};
$latte = new Latte\Engine;
$latte->setExceptionHandler($loggingHandler);
Automatische Suche nach dem Layout
Mit dem Tag {layout} bestimmt
das Template sein Eltern-Template. Es ist auch möglich, das Layout automatisch suchen zu lassen, was das Schreiben von Templates
vereinfacht, weil sie den Tag {layout} nicht mehr enthalten müssen.
Das erreichen Sie so:
// gibt den Pfad zur Datei des Eltern-Templates zurück
$finder = fn(Latte\Runtime\Template $template) => 'automatic.layout.latte';
$latte = new Latte\Engine;
$latte->addProvider('coreParentFinder', $finder);
Soll ein Template kein Layout haben, zeigt es das mit dem Tag {layout none} an.