Twig does not make templating your enemy!

Twig does not make templating your enemy!
Twig does not make
templating your enemy!
Hugo Hamon
@hhamon
Head of training at SensioLabs
PHP fan for 10 years
Travel addict
Speaker at many conferences
Book author
How many of you are…
… web designers?
… web developers?
So you really think
PHP is a good template
engine?
You’re totally wrong!
J
<?php include 'header.php' ?>
Contextual content
Manual escaping
<div class="posts">
<?php foreach ($posts as $post) : ?>
<h2>
<?php echo htmlspecialchars($post['title']) ?>
</h2>
No isolation
<?php include 'post.php' ?>
<?php echo substr($post['body'], 255).'...' ?>
<?php endforeach ?>
No separation of
</div>
concerns
<?php include 'footer.php' ?>
Are you kidding me?
Why and when using
a template engine?
What about existing
template engines?
Twig
1.
Installation & bootstrap
2.
Twig for web designers
3.
Twig for developers
4.
Frameworks & CMS
1.
Installation & bootstrap
Packages Installation
https://github.com/fabpot/twig
twig/twig
Extension Installation
$
$
$
$
$
cd ext/twig
phpize
./configure
make
make install
extension = twig.so
Bootstrapping
require __DIR__.'/vendor/autoload.php';
$loader = new Twig_Loader_Filesystem('/path/to/views');
$twig = new Twig_Environment($loader, array(
'cache' => '/path/to/cache',
));
echo $twig->render('hello.twig', array(
'name' => 'Hugo'
));
2.
Twig for web designers
Concise templating
syntax
Concise Syntax
{# ... comment something ... #}
{% ... do something ... %}
{{ ... display something ... }}
PHP compilation
and cache
PHP Compilation and Cache
{# just say hello #}
Hello {{ name }}!
PHP Compilation and Cache
class __TwigTemplate_df6deba4655fee022981430 extends Twig_Template
{
protected function doDisplay(array $context, array $blocks = array())
{
// line 1
echo "Hello ";
echo twig_escape_filter($this->env, (isset($context["name"]) ?
$context["name"] : null), "html", null, true);
echo "!";
}
// ...
}
Escaping is enabled
Debugging
Accurate Debugging
Hello {{ rand(['John', 'Tom', 'Paul']) }}!
Twig_Error_Syntax: The function
"rand" does not exist. Did you mean
"random" in "hello.twig" at line 3
Dumping a Variable
{% set names = ['John', 'Tom', 'Paul'] %}
{{ dump(names) }}
Strict Variables Enabled
Hello {{ nam }}!
Twig_Error_Runtime: Variable "nam"
does not exist in "hello.twig" at
line 1
Output escaping
Automatic Output Escaping
Hello {{ name }}!
The variable is automatically escaped if it
contains a string
Escaping Strategies
{{ name|raw }}
{{ name|escape }}
{{ name|e }}
{{
{{
{{
{{
{{
name|e('html') }}
name|e('html_attr') }}
name|e('js') }}
name|e('css') }}
name|e('url') }}
Output Escaping
{% autoescape %}
Everything will be automatically escaped in this block
using the HTML strategy
{% endautoescape %}
{% autoescape 'js' %}
Everything will be automatically escaped in this block
using the js escaping strategy
{% endautoescape %}
{% autoescape false %}
Everything will be outputted as is in this block
{% endautoescape %}x
Variables
Variables Abstraction
{{ article.title }}
The article can be an
array or an object. The title can be a key of
the array, a public
property, a regular
method or a getter
method.
Variables Abstraction
echo
echo
echo
echo
echo
$article['title'];
$article->title;
$article->title();
$article->getTitle();
$article->isTitle();
Loops & Conditions
Making Decisions
{% if product.stock > 10 %}
Available
{% elseif product.stock > 0 %}
Only {{ product.stock }} left!
{% else %}
Sold-out!
{% endif %}
Iterating Over a Collection
<div class="posts">
{% for post in posts if post.active %}
<h2>{{ post.title }}</h2>
{{ post.body }}
{% else %}
No published posts yet.
{% endfor %}
</div>
Get the Loop Context
Variable
Description
loop.index
The current iteration of the loop. (1 indexed)
loop.index0
The current iteration of the loop. (0 indexed)
loop.revindex
The number of iterations from the end of the loop (1 indexed)
loop.revindex0
The number of iterations from the end of the loop (0 indexed)
loop.first
True if first iteration
loop.last
True if last iteration
loop.length
The number of items in the sequence
loop.parent
The parent context
Operators
Operators
o  Math: +, -, /, *, **, %
o  Logical: or, and, xor
o  Concatenation: ~
o  Comparison: <, >, <=, >=, ==
o  Containment: in, not in
Functions
Generating Contents
Hello {{ random(['John', 'Tom', 'Paul']) }}!
{% for i in range(0,10) %}
{{ cycle(['odd', 'even'], i) }}<br/>
{% endfor %}
Built-in Functions
o  attribute
o  date
o  constant
o  random
o  block
o  cycle
o  parent
o  range
Filters
Formatting Contents
{{ post.publishedAt|date('d/m/Y') }}
{{
{{
{{
{{
post.title|lower }}
post.title|upper }}
post.title|capitalize }}
post.title|title }}
{{ post.tags|sort|join(', ') }}
{{ post.author|default('Anonymous') }}
Built-in Filters
o  abs
o  json_encode
o  reverse
o  convert_encoding
o  length
o  sort
o  capitalize
o  date
o  date_modify
o  default
o  escape
o  format
o  join
o  keys
o  lower
o  merge
o  nl2br
o  number_format
o  raw
o  replace
o  slice
o  striptags
o  title
o  trim
o  upper
o  url_encode
Whitespace Control
Whitespace Control
{% spaceless %}
<p>
Hello <strong>{{ name }}</strong>!
</p>
{% endspaceless %}
<p>Hello <strong>Hugo</strong>!</p>
Whitespace Control
<p>
Hello <strong>
</p>
{{- name }} </strong>!
Value is trimmed on the left.
<p>Hello <strong>Hugo
</strong>!</p>
Template inclusion
Template Inclusion
{%
include "list.twig" with {
"section": "blog",
"posts": articles
} only
%}
Isolation
The included template is
rendered with its own
context.
Template inheritance
layout.twig
blog.twig
{%block
extends
"layout.twig"
{%
breadcrumb
%} ...%}{% endblock %}
{% block breadcrumb %}
{{ parent() }} - Blog
{% endblock breadcrumb %}
{% block body %}
{%
body %}body %}
{%block
endblock
<h1>Latest posts</h1>
{% include "posts.twig" %}
{% endblock body %}
Template Inheritance
Extend the parent
template.
{% extends "layout.twig" %}
Reuse the parent
block default value.
{% block breadcrumb %}
{{ parent() }} - Blog
{% endblock breadcrumb %}
Fill the parent body
block.
{% block body %}
<h1>Latest posts</h1>
{% include "posts.twig" %}
{% endblock body %}
Macros
Defining Macros
{% macro input(name, value, type, size) %}
<input type="{{ type|default('text') }}"
name="{{ name }}"
value="{{ value|e }}"
size="{{ size|default(20) }}" />
{% endmacro %}
Default argument values are
defined with the default filter
Using Macros
Macros are imported under
a specific namespace
{% import "forms.html" as forms %}
<p>
{{ forms.input('username') }}
</p>
The macro is called with the
prefixed namespace
Sandboxed
environment
Whitelist Filtering
$tags = array('if');
$functions = array('range');
$filters = array('upper');
$methods = array(
'Article' => array('getTitle', 'getBody'),
'Tag' => array('getName'),
);
$properties = array(
'Article' => array('title', 'body'),
'Tag' => array('name'),
);
Enabling Sandbox Mode
$policy = new Twig_Sandbox_SecurityPolicy(
$tags,
$filters,
$methods,
$properties,
$functions
);
$sandbox = new Twig_Extension_Sandbox($policy);
$twig = new Twig_Environment();
$twig->addExtension($sandbox);
Using the Sandbox Mode
{% sandbox %}
{% include 'users.html' %}
{% endsandbox %}
Enabling Sandbox Mode
Sandbox mode is
automatically enabled for all
template
$sandbox = new Twig_Extension_Sandbox($policy, true);
Enabling Sandbox Mode
{% block foo 'bar' %}
{% include 'users.html' %}
Twig_Sandbox_SecurityError:
Tag "block" is not allowed in
"hello.twig" at line 2
3.
Twig for developers
I want to customize
Twig!
Internal Architecture
Compiler
Parser
Extension
Extension
Extensions
Lexer
Twig_Environment
The Twig Lexer
$template = <<<EOT
Hello {{ name }}!
EOT;
$twig = new Twig_Environment();
$stream = $twig->tokenize($template);
The Twig Parser
$ast = $twig->parse($stream);
Twig_Node_Module(
Twig_Node_Text(Hello )
Twig_Node_Print(
Twig_Node_Expression_Name(name)
)
Twig_Node_Text(!)
)
The Twig Compiler
$php = $twig->compile($ast);
class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712b extends Twig_Template
{
protected function doDisplay(array $context, array $blocks = array())
{
// line 1
echo "Hello ";
echo twig_escape_filter($this->env, $this->getContext($context,
"name"), "ndex", null, true);
echo "!";
}
// some more code
}
Configuring Twig
$twig = new Twig_Environment(array(
'strict_variables' => true,
'debug'
=> true,
'cache' => '/path/to/cache',
'base_template_class' => 'MyTemplate',
'autoescape' => 'js',
'optimizations' => -1,
));
Customizing Twig Tags
$twig = new Twig_Environment();
$lexer = new Twig_Lexer($twig, array(
'tag_comment' => array('{*', '*}'),
'tag_block'
=> array('{', '}'),
'tag_variable' => array('{$', '}'),
));
$twig->setLexer($lexer);
Smarty tags
Twig extensions
Extending the Twig Gramar
o  Global variables
o  Global functions
o  Filters
o  Tests
o  Operators
o  Tags
Built-in Extensions
Exension name
Description
Core
Provides the core features
Debug
Provides debugging tools like the dump() function
Escaper
Provides escaping tools
Optimizer
Provides optimization tools
Sandbox
Provides the sandboxed environment capabilities
The Twig Extension Class
abstract class Twig_Extension implements
Twig_ExtensionInterface
{
public function initRuntime(Twig_Environment $environment);
public function getTokenParsers();
public function getNodeVisitors();
public function getFilters();
public function getTests();
public function getFunctions();
public function getOperators();
public function getGlobals();
}
Creating a Twig Extension
class GravatarExtension extends \Twig_Extension
{
public function getName()
{
return 'gravatar';
The abstract getName()
}
method must be
}
implemented.
Registering the Extension
$extension = new GravatarExtension();
$twig = new Twig_Environment();
$twig->addExtension($extension);
Easy uh?!
Adding a new Global Function
class GravatarExtension extends \Twig_Extension
{
public function getFunctions()
{
return array(
'gravatar' => new \Twig_Function_Method($this, 'getGravatar'),
);
}
public function getGravatar($email, $size = '80', $rating = 'g')
{
$url = 'http://www.gravatar.com/avatar/%s?size=%u&rating=%s';
$hash = md5(strtolower(trim($email)));
return sprintf($url, $hash, (int) $size, $rating);
}
}
Using the Global Function
Twig now knows this
function.
{% set uri = gravatar("[email protected]") %}
<img src="{{ uri }}" alt="hhamon"/>
4.
Frameworks & CMS
I want to use Twig
with my favorite
framework or CMS!
Symfony 1.4
sfTwigPlugin
http://www.symfony-project.org/plugins/sfTwigPlugin
Symfony2
Built-in
Forms themes
Render tag
Trans tag
Routing functions
Yaml filters
…
Zend Framework 1
Ano_ZFTwig
Automatic escaping
Layout inheritance
Javascripts helpers
Stylesheets helpers
Routing helpers
https://github.com/benjamindulau/Ano_ZFTwig
Zend Framework 2
Zfc_Twig
Automatic escaping
Layout inheritance
Action rendering
Forms helpers
Events triggering
https://github.com/ZF-Commons/ZfcTwig
Drupal 7
Drupal themes
Switch/Case tag
I18n capabilities
Extra filters
Extra functions
http://drupal.org/sandbox/ReneB/1075966
WordPress
o  http://wordpress.org/extend/plugins/ap-twig-bridge/
o  http://inchoo.net/wordpress/twig-wordpress-part2/
o  https://github.com/cordoval/Twig-for-WordPress
o  https://github.com/maxcal/Twigpress
?
Questions
Join us in Berlin!
Symfony Live
November 22nd – 23rd
Community Feedbacks
Thank You!
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF

advertisement