Slim 4 - Latte Template Engine
Daniel Opitz
06 Apr 2022
Table of contents
Requirements
- PHP 7.4+
- A Slim 4 application
Introduction
Latte is a template engine for PHP that comes with context-aware escaping to protect against XSS vulnerabilities. I would like to take a closer look at this engine and see how this interesting feature works.
Installation
You can download the package using Composer:
composer require latte/latte
Configuration
Create a new directory templates
in your project root directory.
All templates will be placed there later.
Add a new template
configuration key in your config/defaults.php
file:
$settings['template'] = __DIR__ . '/../templates';
Latte “compiles” the templates to native PHP code and stores them in a cache on the disk. So they are as fast as if they had been written in native PHP.
Add a new template_temp
configuration key in your config/defaults.php
file:
Make sure the directory {project}/tmp/templates
exists and has read and write access permissions.
$settings['template_temp'] = __DIR__ . '/../tmp/templates';
Latte automatically regenerates the cache every time you change the template, which can be turned off in the production environment to save a little performance:
// change to false in the production environment
$settings['template_auto_refresh'] = true;
Next, add a DI container definitions for the Latte\Engine
class.
<?php
use Latte\Engine;
use Latte\Loaders\FileLoader;
use Psr\Container\ContainerInterface;
// ...
return [
// ...
Engine::class => function (ContainerInterface $container) {
$latte = new Engine();
$settings = $container->get('settings');
$latte->setLoader(new FileLoader($settings['template']));
$latte->setTempDirectory($settings['template_temp']);
$latte->setAutoRefresh($settings['template_auto_refresh']);
return $latte;
},
];
This alone would technically work to render a latte template, but we also need to make it work with the PSR-7 response object.
For this purpose we create a special TemplateRenderer
class which does this work for us.
So next create a file in src/Renderer/TemplateRenderer.php
and copy/paste this code:
<?php
namespace App\Renderer;
use Latte\Engine;
use Psr\Http\Message\ResponseInterface;
final class TemplateRenderer
{
private Engine $engine;
public function __construct(Engine $engine)
{
$this->engine = $engine;
}
public function template(
ResponseInterface $response,
string $template,
array $data = []
): ResponseInterface {
$string = $this->engine->renderToString($template, $data);
$response->getBody()->write($string);
return $response;
}
}
Usage
Instead of using the Latte Engine object directly we use the TemplateRenderer
object
to render the template into a PSR-7 compatible object.
A typical Action handler class might look like this to render a template with the name home.latte
:
<?php
namespace App\Action\Home;
use App\Renderer\TemplateRenderer;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
final class HomeAction
{
private TemplateRenderer $renderer;
public function __construct(TemplateRenderer $renderer)
{
$this->renderer = $renderer;
}
public function __invoke(
ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$viewData = [
'items' => ['one', 'two', 'three'],
];
return $this->renderer->template($response, 'home.latte', $viewData);
}
}
To make it work, create a template file in templates/home.latte
with this content:
<ul n:if="$items">
{foreach $items as $item}
<li id="item-{$iterator->counter}">{$item|capitalize}</li>
{/foreach}
</ul>
If everything is configured correctly you should see the following output:
- One
- Two
- Three
Escaping Output
The most important task of a template system is to avoid security vulnerabilities.
The Latte Context-Aware Escaping
technologie recognizes
the context in which the tag is placed and chooses the right escaping mode.
The output varies depending on whether you output the data within an HTML tag, an HTML attribute, or within JavaScript.
Let’s take for example this template:
<p onclick="alert({$movie})">{$movie}</p>
<script>var movie = {$movie};</script>
If the variable $movie
contains Amarcord & 8 1/2
, the generated
result looks like this:
<p onclick="alert("Amarcord & 8 1\/2")">Amarcord & 8 1/2</p>
<script>var movie = "Amarcord & 8 1\/2";</script>
Thanks to Context-Aware Escaping the template is simple and your protected against Cross-Site Scripting.
Conclusion
Compared to Twig, the syntax looks more lightweight, cleaner and is easier to learn. I guess more designers are familiar with Twig then with Latte templates. Latte is flexible enough for most use cases, but Twig might be more extensible and has bigger community and support behind it.
When it comes to security, Latte has some very good features enabled. Twig also provides a so-called “Automatic output escaping” feature, but this however, must be manually written in a block of code.
To achieve the best speed possible, Latte compiles templates down to native PHP code. So performance wise Latte should be at the same level as Twig.
If a Latte file contains an error, the error message may not be clear in all cases. Therefore, debugging can sometimes be a bit time-consuming, but the community is very polite and helpful.