Slim 4 - Latte Template Engine

Daniel Opitz
Daniel Opitz
06 Apr 2022

Table of contents

Requirements

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:

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(&quot;Amarcord &amp; 8 1\/2&quot;)">Amarcord &amp; 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.

Read more