Practices for Developers

How to Render a Template

How to render a template? Just use this simple code:

$latte = new Latte\Engine;
// cache directory
$latte->setTempDirectory('/path/to/tempdir');

$params = [ /* template variables */ ];
// or $params = new TemplateParameters(/* ... */);

// render to output
$latte->render('template.latte', $params);
// or render to variable
$output = $latte->renderToString('template.latte', $params);

Parameters can be arrays or even better object, which will provide type checking and suggestion in the editor.

Installation

The best way how to install Latte is to use a Composer:

composer require latte/latte

Supported PHP versions (applies to the latest patch Latte versions):

version compatible with PHP
Latte 3.0 PHP 8.0 – 8.2
Latte 2.8 – 2.11 PHP 7.1 – 8.1

Performance and Caching

Latte templates are fast, because Latte compiles them into PHP code and caches them on disk. The cache is automatically regenerated every time you change the source file. Which you can turn off in a production environment to save a little performance:

$latte->setAutoRefresh(false);

When deployed on a production server, the initial cache generation, especially for larger applications, can understandably take a while. Latte has built-in prevention against cache stampede. This is a situation where server receives a large number of concurrent requests and because Latte's cache does not yet exist, they would all generate it at the same time. Which spikes CPU. Latte is smart, and when there are multiple concurrent requests, only the first thread generates the cache, the others wait and then use it.

Parameters as a Class

Better than passing variables to the template as arrays is to create a class. You get type-safe notation, nice suggestion in IDE and a way to register filters and functions.

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

Disabling Auto-escaping of Variable

If the variable contains an HTML string, you can mark it so that Latte does not automatically (and therefore double) escape it. This avoids the need to specify |noescape in the template.

The easiest way is to wrap the string in a Latte\Runtime\Html object:

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

Latte also does not escape all objects that implement the Latte\HtmlStringable interface. So you can create your own class whose __toString() method will return HTML code that will not be escaped automatically:

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

The __toString method must return correct HTML and provide parameter escaping, otherwise an XSS vulnerability may occur!

How to Extend Latte with Filters, Tags, etc.

How to add a custom filter, function, tag, etc. to Latte? Find out in the chapter extending Latte. If you want to reuse your changes in different projects or if you want to share them with others, you should then create an extension.

Any Code in Template {php ...}

Only PHP expressions can be written inside the {do} tag, so you can't, for example, insert constructs like if ... else or semicolon-terminated statements.

However, you can register the RawPhpExtension extension, which adds the {php ...} tag, which can be used to insert any PHP code at the template author's risk.

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

Translation in Templates

Use the TranslatorExtension extension to add {_...}, {translate} and filter translate to the template. They are used to translate values or parts of the template into other languages. The translator is any PHP callable:

$translator = function (string $original): string {
	// here we create $translated from $original
	return $translated;
};
$latte->addExtension(new Latte\Essential\TranslatorExtension($translator));

Latte can even translate all static texts during template compilation. This creates multiple compiled versions of the same template in the cache directory, one for each language. To do this, you only need to specify the language as the second parameter:

$latte->addExtension(new Latte\Essential\TranslatorExtension($translator, $lang));

Debugging and Tracy

Latte tries to make the development as pleasant as possible. For debugging purposes, there are three tags {dump}, {debugbreak} and {trace}.

You'll get the most comfort if you install the great debugging tool Tracy and activate the Latte plugin:

// enables Tracy
Tracy\Debugger::enable();
// activates plugin
Latte\Bridges\Tracy\BlueScreenPanel::initialize();

You will now see all errors in a neat red screen, including errors in templates with row and column highlighting (video). At the same time, in the bottom right corner in the so-called Tracy Bar, a tab for Latte appears, where you can clearly see all rendered templates and their relationships (including the possibility to click into the template or compiled code), as well as variables:

Linter: Validating the Template Syntax

The Linter tool will help you go through all templates and check for syntax errors. It is launched from the console:

vendor/bin/latte-lint <path>

If you use custom tags, also create your customized Linter, e.g. custom-latte-lint:

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

// enter the actual path to the autoload.php file
require __DIR__ . '/vendor/autoload.php';

$linter = new Latte\Tools\Linter($engine);
$linter->scanDirectory($path);

$engine = new Latte\Engine;
// registers individual extensions here
$engine->addExtension(/* ... */);

$path = $argv[1];
$linter = new Latte\Tools\Linter(engine: $engine);
$ok = $linter->scanDirectory($path);
exit($ok ? 0 : 1);

Loading Templates from a String

Need to load templates from strings instead of files, perhaps for testing purposes? StringLoader will help you:

$latte->setLoader(new Latte\Loaders\StringLoader([
	'main.file' => '{include other.file}',
	'other.file' => '{if true} {$var} {/if}',
]));

$latte->render('main.file', $params);

Exception Handler

You can define your own handler for expected exceptions. Exceptions raised inside {try} and in the sandbox are passed to it.

$loggingHandler = function (Throwable $e, Latte\Runtime\Template $template) use ($logger) {
	$logger->log($e);
};

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

Automatic Layout Lookup

Using the tag {layout}, the template determines its parent template. It's also possible to have the layout searched automatically, which will simplify writing templates since they won't need to include the {layout} tag.

This is achieved as follows:

$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// it returns the path to the parent template file
		return 'automatic.layout.latte';
	}
};

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

If the template should not have a layout, it will indicate this with the {layout none} tag.