Practices for Developers
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 2.11 | PHP 7.1 – 8.2 |
Latte 2.8 – 2.10 | PHP 7.1 – 8.1 |
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.
You can also find usage examples in the repository Latte examples.
Performance and Caching
Latte templates are extremely fast, because Latte compiles them directly into PHP code and caches them on disk. Thus, they have no extra overhead compared to templates written in pure PHP.
The cache is automatically regenerated every time you change the source file. So you can conveniently edit your Latte templates during development and see the changes immediately in the browser. You can disable this feature in a production environment and 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.
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();
$latte = new Latte\Engine;
// activates Tracy's extension
Latte\Bridges\Tracy\LattePanel::initialize($latte);
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:
Since Latte compiles templates into readable PHP code, you can conveniently step through them in your IDE.
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';
$engine = new Latte\Engine;
// here registers individual filters a others
$engine->addFilter(/* ... */);
$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}
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.