A la découverte de Twig

Grégoire Pineau

2015-10-06

Grégoire Pineau

DevOps @ Blackfire.io

Sommaire

  1. Qu'est-ce que twig ?
  2. Comment ça marche ?
  3. Comment organiser ses templates ?

Qu'est-ce que Twig?

C'est ca:

Twig
Twig

Mais c'est aussi

  • Un moteur de template open source
  • 2011-03-27: v1.0.0
  • 2015-10-06: v1.22.2
  • BC depuis la v1.0.0

Que beaucoup de monde utilise

  • Symfony
  • Drupal
  • eZ Publish
  • Sylius
  • phpBB

Car c'est bien !

  • syntaxe simple et claire
  • auto-escaping
  • erreurs facile à debugger
  • très fléxible

Comment ça marche?

Installation

composer require twig/twig:^1.0

Configuration

require __DIR__.'/vendor/autoload.php';

$loader = new Twig_Loader_Filesystem([__DIR__.'/templates']);

$twig = new Twig_Environment($loader);

Utilisation

Template

{# hello.html.twig ; ceci est un commentaire #}

{% if 'gr' in name %}"gr" est bien dans "{{ name }}"{% endif %}

{% for i in [1, 2, 5, 10] %}
    i vaut {{ i }}
{% else %}
    il n'y a pas d'élement dans la liste
{% endfor %}

PHP

echo $twig->render('hello.html.twig', ['name' => 'greg']);

Résultat

"gr" est bien dans "greg"
    i vaut 1
    i vaut 2
    i vaut 5
    i vaut 10

Variables

Twig traite les objets et les tableaux de la meme facon:

{{ user.name }}
$user['name'];

$user->name;
$user->name();
$user->getName();

Filtres / Functions

{% set var = 'my first car' %}

Filtres:

{{ var|upper }} {# MY FIRST CAR #}
{{ var|lower }} {# my first car #}
{{ var|title }} {# My First Car #}
{{ var|capitalize }} {# My first car #}
{{ another_var|default('default value') }} {# default value #}

Fonctions:

{{ date('yesterday 12:00')|date('c') }} {# 2015-10-05T12:00:00+02:00 #}

Auto escaping

Build-in

hello {{ name }}
echo $twig->render('index.html.twig', [
    'name' => '<script>alert("coucou")</script>',
]);
hello &lt;script&gt;alert(&quot;coucou&quot;)&lt;/script&gt;

Mais débrayable:

{{ user.username|raw }}
{{ user.username|e('js') }}
{{ user.username|e('css') }}
{{ user.username|e('url') }}
{{ user.username|e('html_attr') }}

Gestion des erreurs

Undefined vars

hello {{ name }}
try {
    $twig->render('var.html.twig', array('prénom' => 'greg'));
} catch (\Exception $e) {
    echo $e->getMessage();
}
Twig_Error_Runtime: Variable "name" does not exist in "vars.html.twig"
at line 1

Undefined functions

rand: {{ rand() }}
try {
    $twig->render('funct.html.twig');
} catch (\Exception $e) {
    echo $e->getMessage();
}
The function "rand" does not exist. Did you mean "random" in "funct.html.twig"
at line 1

Tools

Extensible

Vous pouvez ajouter facilement des:

macro, global, function, filter, test, operator

et plus difficilement des:

tag

Shuffle

{{ word|shuffle }}
class ShuffleExtension extends Twig_Extension
{
    public function getFilters()
    {
        return [
            new Twig_SimpleFilter('shuffle', 'str_shuffle'),
        ];
    }

    public function getName()
    {
        return 'shuffle';
    }
}

$twig->addExtension(new ShuffleExtension());

echo $twig->render('shuffle.html.twig', array('word' => 'shuffle'));
lsuffhe

Built-in profiler

$profile = new Twig_Profiler_Profile();
$twig->addExtension(new Twig_Extension_Profiler($profile));

$twig->render('homepage.html.twig', [
    'name' => 'greg',
]);

// $dumper = new Twig_Profiler_Dumper_Blackfire();
$dumper = new Twig_Profiler_Dumper_Text();
echo $dumper->dump($profile);
main 1.94ms/100%
└ homepage.html.twig 1.94ms/100%
  └ qui-sommes-nous.html.twig

Comment organiser ses templates ?

Extends

Pourquoi ? Quand ?

Extends - Layout

Extends - Homepage

Extends - Layout - Code

{# layout.html.twig #}

<html>
    <head>
        <title>{% block title '' %}</title>
        {% block meta '' %}
        {% block css %}
            <style type="text/css" src="/css/styles.css"></style>
        {% endblock %}
    </head>
    <body>
        Navigation
        Fil d'arianne
        {% block body '' %}
        {% block javascripts '' %}
    </body>
</html>

Extends - Homepage - Code

{# homepage.html.twig #}

{% extends 'layout.html.twig' %}

{% block title 'Ma page' %}

{% block css %}
    {{ parent() }}
    <style type="text/css" src="/css/homepage.css"></style>
{% endblock %}

{% block body %}
    Coverflow

    Boutons
{% endblock %}

Résultat

<html>
    <head>
        <title>Ma page</title>
        <style type="text/css" src="/css/styles.css"></style>
        <style type="text/css" src="/css/homepage.css"></style>
    </head>
    <body>
        Navigation
        Fil d'arrianne
        Coverflow
        Boutons
    </body>
</html>

Include

Pourquoi ? Quand ?

Include statique #1

Include statique #2

{# qui-sommes-nous.html.twig #}

<div>
    Qui sommes nous ?
</div>

::

{# homepage.html.twig #}

<div>
    New
</div>

<div>
    Derniers Commentaires
</div>

{{ include('qui-sommes-nous.html.twig') }}

Include dynamique #1

Include dynamique #2

{# news.html.twig #}

{% for post in posts %}
    {{ include('post.html.twig', { post: post }, with_context = false) }}
{% endfor %}

::

{# post.html.twig #}

<div>
    <h3>{{ post.title}}</h3>
    <div>{{ post.content }}</div>
</div>

Include

  • Re-utilisation de code HTML
  • Découpage fonctionnel (header / footer)
  • Dynamique
  • Structure HTML non flexible
  • Ne passer pas des flags pour ajouter de la flexibilité

Tips

  • N'utiliser plus le tag, mais la fonction
  • Pensez à isoler vos include

Use

Pourquoi ? Quand ?

Use #1

Use #2

{# blocks.html.twig #}

{% block qui_sommes_nous %}
    <div>
        Qui sommes nous ?
    </div>
{% endblock %}

::

{# homepage.html.twig #}

{# ... #}

{% use "blocks.html.twig" %}

{{ block('qui_sommes_nous') }}

{# OU #}

{% block qui_sommes_nous %}
    <div class="container">
        {{ parent() }}
    </div>
{% endblock %}

Use

  • Ré-utilisation de code HTML
  • Découpage fonctionnel (header / footer)
  • Non dynamique
  • Structure HTML non flexible
  • Permet de partager plusieurs morceaux d'HTML dans le même fichier
  • => Peu utilisé

Embed

Pourquoi ? Quand ?

Embed #1

Embed #2

Embed - liste

{# news.html.twig #}

{% for post in posts %}
    {% embed "post.html.twig" with { post: post } only %}{% endembed %}
{% endfor %}

Embed - Post

{# post.html.twig #}

<div>
    {% block title %}<h3>{{ post.title}}</h3>{% endblock %}

    {% block content%}<div>{{ post.content }}</div>{% endblock %}

    {% block metas %}
        <p>Par {{ post.author }} le {{ post.date }}</p>
    {% endblock %}

    {% block comments %}
        <p>il y a {{ post.comment|length }} commentaires</p>
    {% endblock %}
</div>

Embed - home

{# homepage.html.twig #}

<div>
    {% embed "embed/post.html.twig" with { post: posts|last } only %}

        {% block title %}
            <h1>{{ post.title}}</h1>
        {% endblock %}

        {% block comments %}
            {{ parent() }}<p>Lire les commentaire</p>
        {% endblock %}

    {% endembed %}
</div>

<div>
    Derniers Commentaires
</div>

{{ include('include/qui-sommes-nous.html.twig') }}

Embed

  • Ré-utilisation de code HTML
  • Découpage fonctionnel (header / footer)
  • Dynamique
  • Flexible

Embed - Tips

  • Ajouter autant de blocks que possible
  • Pensez à isoler vos embed

Macros

Pourquoi ? Quand ?

Macros - Code

{% macro input(name, type = 'text', value = '',  size = '20') %}
    {% spaceless %}
        <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}"
            size="{{ size }}" />
    {% endspaceless %}
{% endmacro %}

{% import _self as forms %}
{# ou #}
{% import 'forms.html.twig' as forms %}

{{ forms.input('user') }}
{{ forms.input('email', 'email') }}

Macros

  • Génération de code HTML
  • Dynamique
  • Flexible

Include / Use / Embed / Macro

Vous voulez:

  • Génerer du code HTML dynamiquement : macros
  • Découper votre template en (sous) templates (fonctionnel) : include
  • Mutualiser du code HTML entre plusieurs templates : include
  • Mutualiser et customiser du code HTML entre plusieurs templates : embed

On ne parlera pas

Enfin, juste sur cette slide

  • des arguments nommés
  • de mode sandbox
  • du lexer / parser / compiler
  • des nodes traverseur
  • de l'optimiseur de code
  • des fonctions/filter wildcard (title_*)
  • des namespaces
  • des audits de sécurité
  • que c'est du twig, donc pas de PHP dans la vue
  • de l'extension en C pour de meilleur perf
  • des autres moteurs de templates

Merci ! Des questions ?

Les slides:

Sinon:

Et: