Entwicklerpraktiken

Installation

Der beste Weg, Latte zu installieren, ist über Composer:

composer require latte/latte

Unterstützte PHP-Versionen (gilt für die letzten Patch-Versionen von Latte):

Version Kompatibel mit PHP
Latte 3.0 PHP 8.0 – 8.2

Wie rendert man ein Template?

Wie rendert man ein Template? Dafür genügt dieser einfache Code:

$latte = new Latte\Engine;
// Verzeichnis für den Cache
$latte->setTempDirectory('/path/to/tempdir');

$params = [ /* Template-Variablen */ ];
// oder $params = new TemplateParameters(/* ... */);

// in die Ausgabe rendern
$latte->render('template.latte', $params);
// in eine Variable rendern
$output = $latte->renderToString('template.latte', $params);

Parameter können Arrays sein oder noch besser ein Objekt, das Typprüfung und Autovervollständigung in Editoren sicherstellt.

Anwendungsbeispiele finden Sie auch im Repository Latte examples.

Leistung und Cache

Templates in Latte sind extrem schnell, da Latte sie direkt in PHP-Code kompiliert und auf der Festplatte zwischenspeichert. Sie haben also keinen zusätzlichen Overhead gegenüber Templates, die in reinem PHP geschrieben sind.

Der Cache wird automatisch jedes Mal neu generiert, wenn Sie die Quelldatei ändern. Während der Entwicklung können Sie also bequem Ihre Latte-Templates bearbeiten und die Änderungen sofort im Browser sehen. Sie können diese Funktion in der Produktionsumgebung deaktivieren, um ein wenig Leistung zu sparen:

$latte->setAutoRefresh(false);

Bei der Bereitstellung auf einem Produktionsserver kann die anfängliche Generierung des Caches, insbesondere bei größeren Anwendungen, natürlich einen Moment dauern. Latte verfügt über eine integrierte Prävention gegen Cache Stampede. Dies ist eine Situation, in der eine größere Anzahl gleichzeitiger Anfragen eintrifft, die Latte starten, und da der Cache noch nicht existiert, würden sie alle gleichzeitig mit der Generierung beginnen. Dies würde den Server unverhältnismäßig belasten. Latte ist intelligent und bei mehreren gleichzeitigen Anfragen generiert nur der erste Thread den Cache, die anderen warten und nutzen ihn anschließend.

Parameter als Klasse

Besser als Variablen als Array an das Template zu übergeben, ist es, eine Klasse zu erstellen. Sie erhalten so eine typsichere Schreibweise, angenehme Autovervollständigung in IDEs und einen Weg zur Registrierung von Filtern und Funktionen.

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,
));

Deaktivieren des automatischen Escapings einer Variablen

Wenn eine Variable eine Zeichenkette in HTML enthält, können Sie sie so markieren, dass Latte sie nicht automatisch (und somit doppelt) escapet. Sie vermeiden so die Notwendigkeit, |noescape im Template anzugeben.

Der einfachste Weg ist, die Zeichenkette in ein Latte\Runtime\Html-Objekt zu verpacken:

$params = [
	'articleBody' => new Latte\Runtime\Html($article->htmlBody),
];

Latte escapet außerdem keine Objekte, die das Interface Latte\HtmlStringable implementieren. Sie können also eine eigene Klasse erstellen, deren __toString()-Methode HTML-Code zurückgibt, der nicht automatisch escapet wird:

class Emphasis extends Latte\HtmlStringable
{
	public function __construct(
		private string $str,
	) {
	}

	public function __toString(): string
	{
		return '<em>' . htmlspecialchars($this->str) . '</em>';
	}
}

$params = [
	'foo' => new Emphasis('hello'),
];

Die __toString-Methode muss korrektes HTML zurückgeben und das Escaping von Parametern sicherstellen, andernfalls kann eine XSS-Schwachstelle entstehen!

Wie erweitert man Latte um Filter, Tags usw.?

Wie fügt man Latte eigene Filter, Funktionen, Tags usw. hinzu? Dies wird im Kapitel Latte erweitern behandelt. Wenn Sie Ihre Anpassungen in verschiedenen Projekten wiederverwenden oder mit anderen teilen möchten, sollten Sie eine Erweiterung erstellen.

Beliebiger Code im Template {php ...}

Innerhalb des Tags {do} können nur PHP-Ausdrücke geschrieben werden, Sie können also keine Konstrukte wie if ... else oder Anweisungen, die mit einem Semikolon enden, einfügen.

Sie können jedoch die Erweiterung RawPhpExtension registrieren, die das Tag {php ...} hinzufügt. Damit können Sie beliebigen PHP-Code einfügen. Für ihn gelten keine Sandbox-Regeln, die Verwendung liegt also in der Verantwortung des Template-Autors.

$latte->addExtension(new Latte\Essential\RawPhpExtension);

Überprüfung des generierten Codes

Latte kompiliert Templates in PHP-Code. Natürlich achtet es darauf, dass der generierte Code syntaktisch valide ist. Bei Verwendung von Drittanbieter-Erweiterungen oder RawPhpExtension kann Latte jedoch die Korrektheit der generierten Datei nicht garantieren. Außerdem kann man in PHP Code schreiben, der zwar syntaktisch korrekt ist, aber verboten ist (z. B. Zuweisung eines Wertes zur Variablen $this) und einen PHP Compile Error verursacht. Wenn Sie eine solche Operation im Template schreiben, gelangt sie auch in den generierten PHP-Code. Da es in PHP über zweihundert verschiedene verbotene Operationen gibt, hat Latte nicht den Ehrgeiz, sie alle aufzudecken. PHP selbst weist erst beim Rendern darauf hin, was normalerweise kein Problem darstellt.

Es gibt jedoch Situationen, in denen Sie bereits zur Kompilierungszeit des Templates wissen möchten, dass es keinen PHP Compile Error enthält. Insbesondere dann, wenn Templates von Benutzern bearbeitet werden können oder Sie die Sandbox verwenden. In diesem Fall lassen Sie die Templates bereits zur Kompilierungszeit überprüfen. Diese Funktionalität aktivieren Sie mit der Methode Engine::enablePhpLint(). Da zur Überprüfung die PHP-Binärdatei aufgerufen werden muss, übergeben Sie den Pfad dorthin als Parameter:

$latte = new Latte\Engine;
$latte->enablePhpLinter('/path/to/php');

try {
	$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
	// fängt Fehler in Latte und auch Compile Errors in PHP ab
	echo 'Error: ' . $e->getMessage();
}

Spracheinstellung

Latte ermöglicht die Einstellung der Spracheinstellung, die die Formatierung von Zahlen, Daten und die Sortierung beeinflusst. Sie wird mit der Methode setLocale() eingestellt. Der Sprachbezeichner folgt dem IETF-Sprach-Tag-Standard, der von der PHP-Erweiterung intl verwendet wird. Er besteht aus dem Sprachcode und optional dem Ländercode, z. B. en_US für Englisch in den Vereinigten Staaten, de_DE für Deutsch in Deutschland usw.

$latte = new Latte\Engine;
$latte->setLocale('de'); // Deutsch

Die Spracheinstellung beeinflusst die Filter localDate, sort, number und bytes.

Erfordert die PHP-Erweiterung intl. Die Einstellung in Latte beeinflusst nicht die globale Locale-Einstellung in PHP.

Strikter Modus

Im strikten Parsing-Modus prüft Latte, ob schließende HTML-Tags fehlen und verbietet außerdem die Verwendung der Variablen $this. Sie aktivieren ihn so:

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

Die Generierung von Templates mit dem Header declare(strict_types=1) aktivieren Sie so:

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

Übersetzung in Templates

Mit der Erweiterung TranslatorExtension fügen Sie dem Template die Tags {_...}, {translate} und den Filter translate hinzu. Sie dienen zur Übersetzung von Werten oder Teilen des Templates in andere Sprachen. Als Parameter geben wir eine Methode (PHP callable) an, die die Übersetzung durchführt:

class MyTranslator
{
	public function __construct(private string $lang)
	{}

	public function translate(string $original): string
	{
		// aus $original erstellen wir $translated gemäß $this->lang
		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 bereits während der Kompilierung des Templates übersetzen. Dadurch wird Leistung gespart, da jede Zeichenkette nur einmal übersetzt wird und die resultierende Übersetzung in die kompilierte Form geschrieben wird. Im Cache-Verzeichnis entstehen so mehrere kompilierte Versionen des Templates, eine für jede Sprache. Dazu genügt es, die Sprache als zweiten Parameter anzugeben:

$extension = new Latte\Essential\TranslatorExtension(
	$translator->translate(...),
	$lang,
);

Statischer Text bedeutet z. B. {_'hello'} oder {translate}hello{/translate}. Nicht-statische Texte, wie z. B. {_$foo}, werden weiterhin zur Laufzeit übersetzt.

Dem Übersetzer können aus dem Template auch zusätzliche Parameter übergeben werden, mittels {_$original, foo: bar} oder {translate foo: bar}, die er als Array $params erhält:

public function translate(string $original, ...$params): string
{
	// $params['foo'] === 'bar'
}

Debugging und Tracy

Latte versucht Ihnen die Entwicklung so angenehm wie möglich zu gestalten. Direkt für Debugging-Zwecke gibt es drei Tags: {dump}, {debugbreak} und {trace}.

Den größten Komfort erhalten Sie, wenn Sie zusätzlich das hervorragende Debugging-Werkzeug Tracy installieren und das Add-on für Latte aktivieren:

// schaltet Tracy ein
Tracy\Debugger::enable();

$latte = new Latte\Engine;
// aktiviert die Erweiterung für Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);

Nun werden Ihnen alle Fehler in einem übersichtlichen roten Bildschirm angezeigt, einschließlich Fehlern in Templates mit Hervorhebung von Zeile und Spalte (Video). Gleichzeitig erscheint unten rechts in der sogenannten Tracy Bar ein Tab für Latte, wo alle gerenderten Templates und ihre gegenseitigen Beziehungen übersichtlich dargestellt werden (einschließlich der Möglichkeit, zum Template oder zum kompilierten Code durchzuklicken) sowie die Variablen:

Da Latte Templates in übersichtlichen PHP-Code kompiliert, können Sie sie bequem in Ihrer IDE schrittweise debuggen.

Linter: Syntaxvalidierung von Templates

Um alle Templates durchzugehen und zu überprüfen, ob sie Syntaxfehler enthalten, hilft Ihnen das Werkzeug Linter. Es wird von der Konsole aus gestartet:

vendor/bin/latte-lint <pfad>

Mit dem Parameter --strict aktivieren Sie den strikten Modus.

Wenn Sie eigene Tags verwenden, erstellen Sie sich auch eine eigene Version des Linters, z. B. custom-latte-lint:

#!/usr/bin/env php
<?php

// Geben Sie den tatsächlichen Pfad zur Datei autoload.php an
require __DIR__ . '/vendor/autoload.php';

$path = $argv[1] ?? '.';

$linter = new Latte\Tools\Linter;
$latte = $linter->getEngine();
// hier fügen Sie Ihre einzelnen Erweiterungen hinzu
$latte->addExtension(/* ... */);

$ok = $linter->scanDirectory($path);
exit($ok ? 0 : 1);

Alternativ können Sie ein eigenes Latte\Engine-Objekt an den Linter übergeben:

$latte = new Latte\Engine;
// hier konfigurieren wir das Objekt $latte
$linter = new Latte\Tools\Linter(engine: $latte);

Laden von Templates aus einer Zeichenkette

Müssen Sie Templates aus Zeichenketten anstelle von Dateien laden, z. B. zu Testzwecken? Der StringLoader hilft Ihnen dabei:

$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 Ausnahmen definieren. Ihm werden Ausnahmen ü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);

Automatisches Suchen des Layouts

Mit dem Tag {layout} bestimmt ein Template sein übergeordnetes Template. Es ist auch möglich, das Layout automatisch suchen zu lassen, was das Schreiben von Templates vereinfacht, da das Tag {layout} darin nicht notwendig ist.

Dies wird auf folgende Weise erreicht:

$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// gibt den Pfad zur Layout-Datei zurück
		return 'automatic.layout.latte';
	}
};

$latte = new Latte\Engine;
$latte->addProvider('coreParentFinder', $finder);

Wenn ein Template kein Layout haben soll, teilt es dies mit dem Tag {layout none} mit.

Version: 3.0