TWIG: the flexible, fast and secure template language for PHP

cesaredamico 4,652 views 38 slides Nov 10, 2010
Slide 1
Slide 1 of 38
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38

About This Presentation

TWIG is a template engine created by Fabien Potencier, the father of Symfony. In this talk you'll learn why he wrote it, its main features and strengths.


Slide Content

TWIG
The flexible, fast, and secure template
language for PHP
Cesare D'Amico
[email protected]

Cesare D'Amico
freelance php dev
Hi, nice to meet you!
[email protected]
Twitter: __ce (double underscore)
http://cesaredami.co/

A bit of history
“So, you think PHP is a templating engine?”
Fabien Potencier - October 07, 2009
http://j.mp/lOnJU
In his opinion, php lacks:

concision (<?php echo $var ?>)

template-oriented syntax (foreach())

reusability

security + sandbox mode

A bit of history
Available solutions (October 2009):

Smarty3: slow

PHPTAL: HTML only

ez Components: slowest, no
inheritance

Dwoo: no inheritance

Calypso: broken (in his author's
words)

TWIG

Fast: Twig compiles templates to plain
optimized PHP code

Secure: sandbox mode for untrusted
template code

Flexible: powered by a flexible lexer
and parser, this allows to define
custom tags and filters

Assignments
{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = ['foo': 'bar'] %}
{% set foo = 'foo' ~ 'bar' %}
{% set foo, bar = 'foo', 'bar' %}
{% set foo %}
  <div id="pagination">
    ...
  </div>
{% endset %}

Control structures – for
{% for user in users %}
{% for i in 0..10 %}
{% for l in 'a'..'z' %}
{% for l in 'a'|upper..'z'|upper %}
{% for i in 0|range(10, 2) %}

Control structures – for
loop.index
loop.index0
loop.revindex *
loop.revindex0 *
loop.first (boolean)
loop.last *  (boolean)
loop.length *
loop.parent
* Only for arrays and objects implementing
the Countable interface

Control structures – for
<ul>
  {% for user in users %}
    <li>{{ user.username|e }}</li>
  {% else %}
    <li><em>no user found</em></li>
  {% endfor %}
</ul>

Control structures – for
<ul>
  {% for id in users|keys %}
    <li>{{ id }}</li>
  {% endfor %}
</ul>

Control structures – for
<ul>
  {% for id, user in users %}
    <li>{{ id }}: {{ user.username|e }}</li>
  {% endfor %}
</ul>

Control structures – if
{% if users %}
  <ul>
    {% for user in users %}
      <li>{{ user.username|e }}</li>
    {% endfor %}
  </ul>
{% endif %}

Control structures – if/else
{% if kenny.sick %}
  Kenny is sick.
{% elseif kenny.dead %}
  You killed Kenny! You bastard!!!
{% else %}
  Kenny looks okay ­­­ so far
{% endif %}

Expressions: literals & math
"Hello World"
42 / 42.23
[foo, bar]
true / false
none
+ ­ * / %
//
**

Expressions: logic &
comparison
and or not (expression)
< > <= >= == !=
range comparisons:
{% if 1 < a < 4 %}

Even more operators!
in
.. (range)
| (apply filter)
~ (string concatenation)
. [] (attribute access: a­la object/array)
?: (ternary operator)

Filters: built-in
date: {{ post.published_at|date("m/d/Y") }}
format: “I like %s and %s”
{{ string|format(foo, "bar") }}
replace: “I like %this% and %that%”
{{ string|format(['%this%': foo, '%that%': "bar"]) }}
url_encode, json_encode, title, capitalize, upper, lower,
striptags, join, reverse, length, sort, default, keys, escape / e,
raw

Tests: built-in
divisibleby: {% if loop.index is divisibleby(3) %}
none: {{ var is none }}
even, odd

Everyone still awake?
(the best is coming...)

Inheritance – base file
<html>
<head>
{% block head %}
  <link rel="stylesheet" href="style.css" />
  <title>{% block title %}{% endblock 
%}</title>
{% endblock %}
</head>

Inheritance – base file
<body>
  <div id="content">
    {% block content %}{% endblock %}
  </div>
  <div id="footer">
    {% block footer %}
    &copy; Copyright 2010 by you.
    {% endblock %}
  </div>
</body>
</html>

Inheritance – child template
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
  {% parent %}
  <style type="text/css">
    .important { color: #336699; }
  </style>
{% endblock %}

Inheritance – child template
{% block content %}
  <h1>Index</h1>
  <p class="important">
    Welcome on my awesome homepage.
  </p>
{% endblock %}

Dynamic & conditional
inheritance
{% extends some_var %}
{% extends ajax_output ? "ajax.html"
                        : "layout.html" %}

include
{% include 'header.html' %}
...Body...
{% include 'footer.html' %}
{% include 'foo' with ['foo': 'bar'] %}
{% set vars = ['foo': 'bar'] %}
{% include 'foo' with vars %}
{% include some_var %}
{% include ajax ? 'ajax.html':'base.html' %}

Macros
{% macro input(name, value, type, size) %}
  <input type="{{ type|default('text') }}"
     name="{{ name }}" value="{{ value|e }}"
     size="{{ size|default(20) }}" />
{% endmacro %}
{% macro textarea(name, value, rows) %}
  <textarea name="{{ name }}" 
     rows="{{ rows|default(10) }}"
     cols="{{ cols|default(40) }}">
     {{ value|e }}
  </textarea>
{% endmacro %}

Macros – calling
<p>{{ _self.input('username') }}</p>
<p>{{ _self.textarea(
         'description',
         '...',
         8
      ) }}</p>

import
{% import 'forms.html' as forms %}
<dl>
  <dt>Username</dt>
  <dd>{{ forms.input('username') }}</dd>
  <dt>Password</dt>
  <dd>{{ forms.input('password', none, 
'password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>

import – hang yourself...
{% macro input(name, value, type, size) %}
  <input type="{{ type|default('text') }}"
     name="{{ name }}" value="{{ value|e }}"
     size="{{ size|default(20) }}" />
{% endmacro %}
{% import _self as forms %}
 
<p>{{ forms.input('text','username') }}</p>

i18n – needs gettext!
{% trans "Hello World!" %}
 
{% trans string_var %}
{% trans %}Hello World!{% endtrans %}
{% trans %}Hello {{ name }}!{% endtrans %}
{% trans Hello {{ name }}! %}

i18n – plurals
{% trans %}
    Hey {{ name }}, I have one apple.
{% plural apple_count %}
    Hey {{ name }}, I have {{ count }} 
apples.
{% endtrans %}

Let's use it: environment
<?php
require_once 
'/path/to/lib/Twig/Autoloader.php';
Twig_Autoloader::register();
 
$loader = new 
Twig_Loader_Filesystem('/path/to/templates');
$twig = new Twig_Environment($loader, array(
  'cache' => '/path/to/compilation_cache',
));

Let's use it: render
<?php
$template = $twig­>loadTemplate('tpl.html');
echo $template­>render(array(
  'the' => 'variables', 
  'go' => 'here'
));
Same as:
$template­>display(...)

Built-in loaders
$l = new Twig_Loader_Filesystem($tplDir);
$l = new Twig_Loader_Filesystem(
  array($tplDir1, $tplDir2)
);
Dummy:
$loader = new Twig_Loader_String("...");
For unit testing:
$loader = new Twig_Loader_Array($templates);

Built-in extensions
Core (automatically registered)
Escaper
Sandbox
I18n
You can create your own extensions if you need so:
- implement interface Twig_ExtensionInterface
- extend Twig_Extension so you need only implement
needed methods
$twig­>addExtension(
  new Twig_Extension_Escaper()
);

Thanks everyone!
?

12-14 Maggio 2011
http://www.phpday.it/

Please, rate this talk!
Feel like bashing?
Urge saying you fell in love
with this presentation?
Now you can!
http://joind.in/2151