Top Banner
JAVIER EGUILUZ SUNSHINEPHP FEBRUARY 8TH 2013 TWIG tips & tricks
185

Twig tips and tricks

Sep 08, 2014

Download

Technology

Javier Eguiluz

Twig tips, tricks, advanced stuff and new and noteworthy features.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Twig tips and tricks

JAVIER EGUILUZSUNSHINEPHPFEBRUARY 8TH 2013

TWIGtips & tricks

Page 2: Twig tips and tricks

Thanks to sponsors and organizers

AdamCulp

PabloGodel

Page 3: Twig tips and tricks

About me

Javier EguiluzI’m a programmerand trainer from Spain.

Page 4: Twig tips and tricks

I SYMFONY

Page 5: Twig tips and tricks

I’m a long-term Symfony enthusiast

Page 6: Twig tips and tricks

My Symfony2 book

Agile web developmentwith Symfony2

Page 7: Twig tips and tricks

My Symfony website

WINNER 2011Best Symfony Blog

Page 8: Twig tips and tricks

I’m the « A week of Symfony» guy

Page 9: Twig tips and tricks

I’m the « A week of Symfony» guy

this is me!

Page 10: Twig tips and tricks

I TWIG

Page 11: Twig tips and tricks

Twig is...• The best template engine for PHP.

• Fast, secure and modern.

• Easy to learn, to read and to write.

• If you don’t know Twig yet, read the official docs at:http://twig.sensiolabs.org/documentation

Page 12: Twig tips and tricks

AGENDA

Page 13: Twig tips and tricks

Agenda• Tips and tricks about Twig.

• Advanced features.

• Defensive template design.

• Best practices.

• New and noteworthy Twig features.

Page 14: Twig tips and tricks

NEW & NOTEWORTHY

Page 15: Twig tips and tricks

Twig development activity is crazy!

(last 12 months) Twig(PHP, Symfony)

Jinja2(Python, Django)

Commits 525 11

Authors 35 5

Versions released 23 0

Page 16: Twig tips and tricks

New and noteworthy

1 2 3 4 5

Page 17: Twig tips and tricks

«template_from_string» function

{% set user_template = "{{ description[0:80] }}<br/> Price: {{ product.price }}" %}

{% include template_from_string(user_template) %}

Page 18: Twig tips and tricks

New and noteworthy

1 2 3 4 5

Page 19: Twig tips and tricks

«include» function

{% include 'template.twig' %}

{{ include('template.twig') }}equivalent

Page 20: Twig tips and tricks

«include» function

{% set content = include('index.twig') %}

{% set content %}{{ include('index.twig') }}

{% endset %}

WRONG

OK

Page 21: Twig tips and tricks

«include» function{{ include('index.twig')|striptags('<a>')[0:80] }}

{% set content %}{{ include('index.twig') }}

{% endset %}

{{ content|striptags('<a>')[0:80] }}

WRONG

OK

Page 22: Twig tips and tricks

New and noteworthy

1 2 3 4 5

Page 23: Twig tips and tricks

«first» and «last» filters

{% set firstElement = array|first %}

{% set lastElement = array|last %}

Page 24: Twig tips and tricks

«first» and «last» filters

{% set first = array[0] %}{% set last = array[array|length - 1] %}

Page 25: Twig tips and tricks

«first» and «last» filters

{% set first = array[0] %}{% set last = array[array|length - 1] %}

only works for zero-indexed numeric arrays

Page 26: Twig tips and tricks

«first» and «last» filters

{{ [1, 2, 3, 4]|first }} 1

{{ { a: 1, b: 2, c: 3, d: 4 }|first }} 1

{{ '1234'|first }} 1

result

Page 27: Twig tips and tricks

New and noteworthy

1 2 3 4 5

Page 28: Twig tips and tricks

Named arguments{{ text|convert_encoding('UTF-8', 'ISO-8859-1') }}

{{ text|convert_encoding( to='UTF-8', from='ISO-8859-1') }}

{{ text|convert_encoding( from='ISO-8859-1', to='UTF-8') }}

Page 29: Twig tips and tricks

Named arguments{{ text|convert_encoding('UTF-8', 'ISO-8859-1') }}

{{ text|convert_encoding( to='UTF-8', from='ISO-8859-1') }}

{{ text|convert_encoding( from='ISO-8859-1', to='UTF-8') }}

equivalent

Page 30: Twig tips and tricks

Named arguments{{ text|convert_encoding('UTF-8', 'ISO-8859-1') }}

{{ text|convert_encoding( to='UTF-8', from='ISO-8859-1') }}

{{ text|convert_encoding( from='ISO-8859-1', to='UTF-8') }}

equivalent

argument order changed!

Page 31: Twig tips and tricks

Named arguments{{ "now"|date("Y-m-d", "America/New_York") }}

{{ "now"|date(timezone="America/New_York") }}

mandatory to set the second argument

just set the argument you need

Page 32: Twig tips and tricks

New and noteworthy

1 2 3 4 5

Page 33: Twig tips and tricks

Functions and filters before 1.12$twig->addFunction('functionName', new Twig_Function_Function('someFunction'));

$twig->addFunction('otherFunction', new Twig_Function_Method($this, 'someMethod'));

Page 34: Twig tips and tricks

Functions and filters in Twig 1.12$twig->addFunction(new Twig_SimpleFunction( 'twig_function', 'some_php_function'));

$twig->addFunction(new Twig_SimpleFunction( 'twig_function', array($object, 'method_name')));

$twig->addFunction(new Twig_SimpleFunction( 'twig_function', function() { ... }));

Page 35: Twig tips and tricks

Functions and filters in Twig 1.12$twig->addFunction(new Twig_SimpleFunction( 'twig_function', 'some_php_function'));

$twig->addFunction(new Twig_SimpleFunction( 'twig_function', array($object, 'method_name')));

$twig->addFunction(new Twig_SimpleFunction( 'twig_function', function() { ... }));

Page 36: Twig tips and tricks

Functions and filters in Twig 1.12$twig->addFunction(new Twig_SimpleFunction( 'twig_function', 'some_php_function'));

$twig->addFunction(new Twig_SimpleFunction( 'twig_function', array($object, 'method_name')));

$twig->addFunction(new Twig_SimpleFunction( 'twig_function', function() { ... })); super cool

Page 37: Twig tips and tricks

OVERRIDING FILTERS

Page 38: Twig tips and tricks

USE WITH CAUTION

Page 39: Twig tips and tricks

{% for i in array|sort %} {# ... #}

{% endfor %}

Page 40: Twig tips and tricks

{% for i in array|sort %} {# ... #}

{% endfor %}

How is the array sorted?

Page 41: Twig tips and tricks

PHP defines 15 sorting functions• asort()• arsort()• krsort()• ksort()• rsort()• shuffle()• sort()• usort()

• array_multisort()• natcasesort()• natsort()• rsort()• shuffle()• uasort()• uksort()

Page 42: Twig tips and tricks

Overriding filters• Where can I find the PHP function

used by Twig?

• How can I override it with my own implementation?

Page 43: Twig tips and tricks

Where Twig defines everything

lib/twig/Extension/Core.phpclass Twig_Extension_Core extends Twig_Extension { public function getTokenParsers() { return array( new Twig_TokenParser_For(), new Twig_TokenParser_If(), new Twig_TokenParser_Extends(), new Twig_TokenParser_Include(), new Twig_TokenParser_Block(), // ... ); }

public function getFilters() { $filters = array( 'format' => new Twig_Filter_Function('sprintf'), 'replace' => new Twig_Filter_Function('strtr'), 'abs' => new Twig_Filter_Function('abs'), // ... ); }

+1,300 lines class!

Page 44: Twig tips and tricks

Where Twig defines everything

lib/twig/Extension/Core.phpclass Twig_Extension_Core extends Twig_Extension { public function getTokenParsers() { return array( new Twig_TokenParser_For(), new Twig_TokenParser_If(), new Twig_TokenParser_Extends(), new Twig_TokenParser_Include(), new Twig_TokenParser_Block(), // ... ); }

public function getFilters() { $filters = array( 'format' => new Twig_Filter_Function('sprintf'), 'replace' => new Twig_Filter_Function('strtr'), 'abs' => new Twig_Filter_Function('abs'), // ... ); }

• Filters• Functions• Tags• Operators• Tests

Page 45: Twig tips and tricks

«sort» filter uses «asort» functionnew Twig_SimpleFilter('sort', 'twig_sort_filter'),

// ...

function twig_sort_filter($array){ asort($array);

return $array;}

Page 46: Twig tips and tricks

Overriding filters• Where can I find the PHP function

used by Twig?

• How can I override it with my own implementation?

Page 47: Twig tips and tricks

Replace «asort» with «natcasesort»

// asortA1, a0, a10, a2

// natcasesorta0, A1, a2, a10

Page 48: Twig tips and tricks

1. Define a new Twig extension

class MyCoreExtension extends Twig_Extension_Core { // ...}

Page 49: Twig tips and tricks

2. Define the new «sort» filterclass MyCoreExtension extends Twig_Extension_Core {

public function getFilters() { // ... }}

Page 50: Twig tips and tricks

2. Define the new «sort» filterclass MyCoreExtension extends Twig_Extension_Core { public function getFilters() {

return array_merge( parent::getFilters(), array( ... ) ); }}

Page 51: Twig tips and tricks

2. Define the new «sort» filterclass MyCoreExtension extends Twig_Extension_Core{ public function getFilters() { return array_merge(parent::getFilters(), array( 'sort' => new Twig_Filter_Method($this, 'sortFilter') )); }

public function sortFilter($array) { natcasesort($array); return $array; }}

Page 52: Twig tips and tricks

3. Register the new extension

$twig = new Twig_Environment( ... );

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

Page 53: Twig tips and tricks

{% for i in array|sort %} {# ... #}

{% endfor %}

This is now natcasesort

Page 54: Twig tips and tricks

DYNAMIC FUNCTIONS

Page 55: Twig tips and tricks

WordPress template tags

the_ID()the_title()the_time()the_content()the_category()the_shortlink()

Page 56: Twig tips and tricks

WordPress template tags

the_ID()the_title()the_time()the_content()the_category()the_shortlink()

class Post { $id = ... $title = ... $time = ... $content = ... $category = ... $shortlink = ... // ...}

Page 57: Twig tips and tricks

the_*()

Page 58: Twig tips and tricks

$twig->addFunction(

'the_*', new Twig_Function_Function('wordpress'));

function wordpress($property, $options){ // ...}

Variable functions

Page 59: Twig tips and tricks

$twig->addFunction(

'the_*', new Twig_Function_Function('wordpress'));

function wordpress($property, $options){ // ...}

Variable functions

Page 60: Twig tips and tricks

$twig->addFunction(

'the_*', new Twig_Function_Function('wordpress'));

function wordpress($property, $options){ // ...}

Variable functions

don’t use regexps,just asterisks

Page 61: Twig tips and tricks

Variable functions in practice

{{ the_ID() }}function wordpress('ID') { ... }

{{ the_content() }}function wordpress('content') { ... }

Page 62: Twig tips and tricks

Variable functions in practice

{{ the_title('<h3>', '</h3>') }}

function wordpress( 'title', array('<h3>', '</h3>')) { ... }

Page 63: Twig tips and tricks

WordPress template tags

next_image_link()next_post_link()next_posts_link()previous_image_link()previous_post_link()previous_posts_link()

Page 64: Twig tips and tricks

WordPress template tags

next_image_link() next_*_link()next_post_link() next_*_link()next_posts_link() next_*_link()previous_image_link() previous_*_link()previous_post_link() previous_*_link()previous_posts_link() previous_*_link()

Page 65: Twig tips and tricks

WordPress template tags

next_image_link() *_*_link()next_post_link() *_*_link()next_posts_link() *_*_link()previous_image_link() *_*_link()previous_post_link() *_*_link()previous_posts_link() *_*_link()

Page 66: Twig tips and tricks

USE WITH CAUTION

Page 67: Twig tips and tricks

php_*()

Page 68: Twig tips and tricks

php_* dynamic function$twig->addFunction(new Twig_SimpleFunction('php_*',

function() { $arg_list = func_get_args(); $function = array_shift($arg_list); return call_user_func_array($function, $arg_list); }, array('pre_escape' => 'html', 'is_safe' => array('html')));

Page 69: Twig tips and tricks

Exposing PHP functions

{{ php_print_r(['value1', 'value2']) }}{{ php_crypt('mypassword') }}{{ php_memory_get_usage() }}{{ php_uniqid() }}

Referencehttp://github.com/lidaa/LidaaTwigBundle

Page 70: Twig tips and tricks

CUSTOM TAGS

Page 71: Twig tips and tricks

{% source ‘...’ %}

Page 72: Twig tips and tricks

{% source ‘...’ %}file_get_contents()

Page 73: Twig tips and tricks

The new «source» tag

{% source 'home.twig' %}

{% source '../../../composer.json' %}

Page 74: Twig tips and tricks

How does Twig work internallyclass __TwigTemplate_06dff1ec7c2cceb3f45ac76fc059b730 extends Twig_Template{ public function __construct(Twig_Environment $env) { parent::__construct($env);

$this->parent = $this->env->loadTemplate("layout.twig");

$this->blocks = array(

PHP file

Lexer Parser Compiler

Twigtemplate

{% source

'simple.twig' %}

{# ... #}

Page 75: Twig tips and tricks

How does Twig work internallyclass __TwigTemplate_06dff1ec7c2cceb3f45ac76fc059b730 extends Twig_Template{ public function __construct(Twig_Environment $env) { parent::__construct($env);

$this->parent = $this->env->loadTemplate("layout.twig");

$this->blocks = array(

PHP file

Lexer Parser Compiler

Twigtemplate

{% source

'simple.twig' %}

{# ... #}

you must provide these

Page 76: Twig tips and tricks

1. Create a new token parserclass SourceTokenParser extends Twig_TokenParser{ public function getTag() { return 'source'; }}

Page 77: Twig tips and tricks

2. Register the new token parser$loader = new Twig_Loader_Filesystem(...);$twig = new Twig_Environment($loader, array(...));

$twig->addTokenParser( new SourceTokenParser());

Page 78: Twig tips and tricks

3. Fill in the «parse» methodclass SourceTokenParser extends Twig_TokenParser{ public function parse(Twig_Token $token) { $lineno = $token->getLine(); $value = $this->parser->getExpressionParser() ->parseExpression();

$this->parser->getStream() ->expect(Twig_Token::BLOCK_END_TYPE);

return new SourceNode($value, $lineno, $this->getTag()); }}

Page 79: Twig tips and tricks

3. Fill in the «parse» methodclass SourceTokenParser extends Twig_TokenParser{ public function parse(Twig_Token $token) { $lineno = $token->getLine(); $value = $this->parser->getExpressionParser() ->parseExpression();

$this->parser->getStream() ->expect(Twig_Token::BLOCK_END_TYPE);

return new SourceNode($value, $lineno, $this->getTag()); }}

this is hard

Page 80: Twig tips and tricks

4. Define the node class that compiles tags

class SourceNode extends Twig_Node { public function __construct(Twig_Node_Expression $value, $lineno, $tag = null) {

parent::__construct(array('file' => $value), array(), $lineno, $tag);

}

public function compile(Twig_Compiler $compiler) { $compiler -> // ... ->write('echo file_get_contents(') ->subcompile($this->getNode('file')) ->raw(');') ; }}

Page 81: Twig tips and tricks

4. Define the node class that compiles tags

class SourceNode extends Twig_Node { public function __construct(Twig_Node_Expression $value, $lineno, $tag = null) {

parent::__construct(array('file' => $value), array(), $lineno, $tag);

}

public function compile(Twig_Compiler $compiler) { $compiler -> // ... ->write('echo file_get_contents(') ->subcompile($this->getNode('file')) ->raw(');') ; }}

this is very hard

Page 82: Twig tips and tricks

The compiled PHP template// line 3echo file_get_contents("simple.twig");// ...

// line 5echo file_get_contents("../../../composer.json");

{% source 'simple.twig' %}

Page 83: Twig tips and tricks

TEMPLATE LOADERS

Page 84: Twig tips and tricks

Most apps use a single loader$loader = new Twig_Loader_Filesystem( __DIR__.'/templates');$twig = new Twig_Environment($loader, array());

$html = $twig->render('home.html.twig');

Page 85: Twig tips and tricks

Chaining several loaders$loader1 = new Twig_Loader_Filesystem(...);$loader2 = new Twig_Loader_Filesystem(...);$loader = new Twig_Loader_Chain(array( $loader1, $loader2));

$twig = new Twig_Environment($loader, array());

// ...

Page 86: Twig tips and tricks

Chaining different loaders$loader1 = new Twig_Loader_Filesystem(...);$loader2 = new Twig_Loader_Array(...);$loader3 = new Twig_Loader_String(...);

$loader = new Twig_Loader_Chain(array( $loader1, $loader2, $loader3));

// ...

Page 87: Twig tips and tricks

Chaining different loaders$loader1 = new Twig_Loader_Filesystem(...);$loader2 = new Twig_Loader_Array(...);$loader3 = new Twig_Loader_String(...);

$loader = new Twig_Loader_Chain(array( $loader1, $loader2, $loader3));

// ...

order matters!

Page 88: Twig tips and tricks

Adding loaders at runtime$loader = new Twig_Loader_Filesystem('/templates');$twig = new Twig_Environment($loader, array());

if ( ... ) { $twig->addLoader( new Twig_Loader_Filesystem('/special_templates') );}

// ...

Page 89: Twig tips and tricks

Storing templates in several folders

$loader = new Twig_Loader_Filesystem(array( __DIR__.'/default', __DIR__.'/templates', __DIR__.'/themes'));$twig = new Twig_Environment($loader, array());

// ...

Page 90: Twig tips and tricks

Storing templates in several folders

$loader = new Twig_Loader_Filesystem(array( __DIR__.'/default', __DIR__.'/templates', __DIR__.'/themes'));$twig = new Twig_Environment($loader, array());

// ...

Whoops, looks like something went wrong.

Twig_Error_Loader: The "_DIR_/templates" directory does not exist.

Page 91: Twig tips and tricks

Adding folders at runtime$loader = new Twig_Loader_Filesystem('/templates');

$path = $user_slug.'/templates';if (file_exists($path)) { $loader->addPath($path);}

$twig = new Twig_Environment($loader, array());

// ...

Page 92: Twig tips and tricks

Prioritizing template folders$loader = new Twig_Loader_Filesystem('/templates');

if(...) { $loader->addPath($user_slug.'/templates'); $loader->prependPath($user_slug.'/themes');}

$twig = new Twig_Environment($loader, array());

// ...

Page 93: Twig tips and tricks

Prioritizing template folders$loader = new Twig_Loader_Filesystem('/templates');

if(...) { $loader->addPath($user_slug.'/templates'); $loader->prependPath($user_slug.'/themes');}

$twig = new Twig_Environment($loader, array());

// ...

path is added before any other path

Page 94: Twig tips and tricks

It’s difficult to prioritize folders

$loader ->addPath(...) ->addPath(...) ->prependPath(...) ->addPath(...) ->prependPath(...);

Page 95: Twig tips and tricks

NAMESPACES

Page 96: Twig tips and tricks

Namespaces are better than folders

templates/

themes/index.twig

admin/index.twig

frontend/index.twig

Page 97: Twig tips and tricks

Namespaces are better than folders$loader = new Twig_Loader_Filesystem(__DIR__.'/templates');$twig = new Twig_Environment($loader, array());

$html = $twig->render('admin/index.twig');$html = $twig->render('themes/index.twig');$html = $twig->render('frontend/index.twig');

Page 98: Twig tips and tricks

App doesn’t work if folders change

templates/

themes/index.twig

frontend/index.twig

admin/

Page 99: Twig tips and tricks

App doesn’t work if folders change

templates/

themes/index.twig

frontend/index.twig

admin/

Whoops, looks like something went wrong.

Twig_Error_Loader: The "admin/index.twig" template does not exist.

Page 100: Twig tips and tricks

Registering and using namespaces$loader = new Twig_Loader_Filesystem(__DIR__.'/templates');

$loader->addPath(__DIR__.'/admin', 'admin');$twig = new Twig_Environment($loader, array());

$html = $twig->render('@admin/index.twig');$html = $twig->render('admin/index.twig');

Page 101: Twig tips and tricks

Registering and using namespaces$loader = new Twig_Loader_Filesystem(__DIR__.'/templates');

$loader->addPath(__DIR__.'/admin', 'admin');$twig = new Twig_Environment($loader, array());

$html = $twig->render('@admin/index.twig');$html = $twig->render('admin/index.twig');

Page 102: Twig tips and tricks

Registering and using namespaces$loader = new Twig_Loader_Filesystem(__DIR__.'/templates');

$loader->addPath(__DIR__.'/admin', 'admin');$twig = new Twig_Environment($loader, array());

$html = $twig->render('@admin/index.twig');$html = $twig->render('admin/index.twig');

Page 103: Twig tips and tricks

Registering and using namespaces$loader = new Twig_Loader_Filesystem(__DIR__.'/templates');

$loader->addPath(__DIR__.'/admin', 'admin');$twig = new Twig_Environment($loader, array());

$html = $twig->render('@admin/index.twig');$html = $twig->render('admin/index.twig');

physical pathlogical path

Page 104: Twig tips and tricks

A practical use case$loader->addPath( __DIR__.'/themes/default/admin', 'backend');

// with namespaces$html = $twig->render('@backend/edit.twig');

// with physical paths$html = $twig->render('themes/default/admin/edit.twig');

Page 105: Twig tips and tricks

TWIG.JS

Page 106: Twig tips and tricks

Twig.js = Twig inside JavaScript

Page 107: Twig tips and tricks

TWIG

Twig.js = Twig inside JavaScriptTWIG

Page 108: Twig tips and tricks

TWIG

Twig.js = Twig inside JavaScriptTWIG

HTML HTML

Page 109: Twig tips and tricks

TWIG

Twig.js = Twig inside JavaScriptTWIG

HTML

JS

HTML

JS

Page 110: Twig tips and tricks

TWIG

Twig.js = Twig inside JavaScriptTWIG

HTML

JS

TWIG

HTML

JS

TWIG

Page 111: Twig tips and tricks

«A tale of two twig.js»

twig.js by

Johannes Schmitt

twig.js by

John Roepke

http://github.com/schmittjoh/twig.js

https://github.com/justjohn/twig.js

Page 112: Twig tips and tricks

«A tale of two twig.js»

twig.js by

Johannes Schmitt

twig.js by

John Roepke

http://github.com/schmittjoh/twig.js

https://github.com/justjohn/twig.js

Page 113: Twig tips and tricks

twig.js by Johannes Schmitt• A templating engine for Javascript

using Jinja/Twig style syntax.

• It compiles your Twig templates to raw Javascript (to use the same templates for both server and client).

Page 114: Twig tips and tricks

Defining the template{# tweet.twig #}

{% twig_js name="tweet" %}<p> {{ message }} <span>by {{ author }}</span> <time datetime="{{ published_at|date }}"> {{ published_at|date }} </time></p>

Page 115: Twig tips and tricks

Defining the template{# tweet.twig #}

{% twig_js name="tweet" %}<p> {{ message }} <span>by {{ author }}</span> <time datetime="{{ published_at|date }}"> {{ published_at|date }} </time></p>

important!

Page 116: Twig tips and tricks

Rendering the template in the browser

<script type="text/javascript" src="twig.js"></script>

<script type="text/javascript" src="tweet.js"></script>

<script language="javascript" type="text/javascript">

var html = Twig.render(tweet, { message: "...", author: "...", published_at: "..." }));</script>

Page 117: Twig tips and tricks

Rendering the template in the browser

<script type="text/javascript" src="twig.js"></script>

<script type="text/javascript" src="tweet.js"></script>

<script language="javascript" type="text/javascript">

var html = Twig.render(tweet, { message: "...", author: "...", published_at: "..." }));</script>

{% twig_js name="tweet" %}

Page 118: Twig tips and tricks

«A tale of two twig.js»

twig.js by

Johannes Schmitt

twig.js by

John Roepke

http://github.com/schmittjoh/twig.js

http://github.com/justjohn/twig.js

Page 119: Twig tips and tricks

twig.js by John Roepke• A pure JavaScript implementation of

the Twig PHP templating language.

• The goal is to provide a library that is compatible with both browsers and server side node.js.

Page 120: Twig tips and tricks

Defining the template{# tweet.twig #}

{% twig_js name="tweet" %}<p> {{ message }} <span>by {{ author }}</span> <time datetime="{{ published_at|date }}"> {{ published_at|date }} </time></p>

not necessary

Page 121: Twig tips and tricks

Rendering the template in the browser

<script type="text/javascript" src="twig.js"></script>

<script language="javascript" type="text/javascript">

var template = twig({data: '<p> {{ message }} ... </p>' });

var html = template.render({ message: "...", author: "...", published_at: "..." });

</script>

template’s source code

Page 122: Twig tips and tricks

Rendering the template in node.js

<script type="text/javascript">var Twig = require("twig"), express = require('express'), app = express();

app.set("twig options", { strict_variables: false });

app.get('/tweet', function(req, res){ res.render('tweet.twig', { message: "...", author: "...", published_at: "..." });});

app.listen(80);</script>

Page 123: Twig tips and tricks

SANDBOX

Page 124: Twig tips and tricks

A simple object$offer = new Offer();$offer->title = "Lorem Ipsum Dolor Sit Amet";$offer->description = "Ut enim ad minim veniam ...";$offer->commission = 20;

Page 125: Twig tips and tricks

A simple object$offer = new Offer();$offer->title = "Lorem Ipsum Dolor Sit Amet";$offer->description = "Ut enim ad minim veniam ...";$offer->commission = 20;

TOP-SECRET information

Page 126: Twig tips and tricks

Templates can show any property

Offer data----------Title: {{ offer.title }}Description: {{ offer.description }}Commission: {{ offer.commission }}

Page 127: Twig tips and tricks

Twitter Sandbox• It’s a regular Twig extension.

• Disabled by default.

• It allows to restrict the functions, filters, tags and object properties used in the templates.

• It’s based on security policies.

Page 128: Twig tips and tricks

Define a new security policy$loader = new Twig_Loader_Filesystem('...');$twig = new Twig_Environment($loader, array());

$properties = array( 'Offer' => array('title', 'description'));

$policy = new Twig_Sandbox_SecurityPolicy( array(), array(), array(), $properties, array());

Page 129: Twig tips and tricks

Define a new security policy$loader = new Twig_Loader_Filesystem('...');$twig = new Twig_Environment($loader, array());

$properties = array( 'Offer' => array('title', 'description'));

$policy = new Twig_Sandbox_SecurityPolicy( array(), array(), array(), $properties, array());

commission is not included

Page 130: Twig tips and tricks

Define a new security policy$loader = new Twig_Loader_Filesystem('...');$twig = new Twig_Environment($loader, array());

$properties = array('Offer' => array('title', 'description'));$policy = new Twig_Sandbox_SecurityPolicy( array(), array(), array(), $properties, array());

$sandbox = new Twig_Extension_Sandbox( $policy, true);$twig->addExtension($sandbox);

Page 131: Twig tips and tricks

Define a new security policy$loader = new Twig_Loader_Filesystem('...');$twig = new Twig_Environment($loader, array());

$properties = array('Offer' => array('title', 'description'));$policy = new Twig_Sandbox_SecurityPolicy( array(), array(), array(), $properties, array());

$sandbox = new Twig_Extension_Sandbox( $policy, true);$twig->addExtension($sandbox);

ALL templates are sandboxed

Page 132: Twig tips and tricks

The template now displays an error

Offer data----------Title: {{ offer.title }}Description: {{ offer.description }}Commission: {{ offer.commission }}

Page 133: Twig tips and tricks

The template now displays an error

Offer data----------Title: {{ offer.title }}Description: {{ offer.description }}Commission: {{ offer.commission }}

Whoops, looks like something went wrong.

Calling "comission" property on a "Offer" object is not allowed in ... at line 6.

Page 134: Twig tips and tricks

Security policy arguments$policy = new Twig_Sandbox_SecurityPolicy(

$tags, $filters, $methods, $properties, $functions);

Page 135: Twig tips and tricks

Allow to use just 3 filters$policy = new Twig_Sandbox_SecurityPolicy(

$tags, array('escape', 'upper', 'lower'), $methods, $properties, $functions);

Page 136: Twig tips and tricks

Allow to use just 2 tags$policy = new Twig_Sandbox_SecurityPolicy(

array('include', 'extends'), $filters, $methods, $properties, $functions);

Page 137: Twig tips and tricks

Use any tag except include and extends

$policy = new Twig_Sandbox_SecurityPolicy( array_diff( array_keys($twig->getTags()), array('include', 'extends') ), $filters, $methods, $properties, $functions);

Page 138: Twig tips and tricks

THE BASE TEMPLATE

Page 139: Twig tips and tricks

Adding a trace to all web pages<html><head> ... </head> <body> ...

<span data-host="Darwin 10.8.0 ..." data-elapsed="0.97804594039 sec." data-timestamp="1339609672.9781"> </span>

</body></html>

Page 140: Twig tips and tricks

Twig cache

Page 141: Twig tips and tricks

cache/09/fc/2d8a188dda8245d295e6324582f2.php

/* homepage.html.twig */

class __TwigTemplate_09f8a...582f2 extends Twig_Template {

public function __construct(Twig_Environment $env) { ... }

protected function doGetParent(array $context) { ... }

protected function doDisplay(array $context, array $blocks) { ... }

// ...}

Page 142: Twig tips and tricks

cache/09/fc/2d8a188dda8245d295e6324582f2.php

/* homepage.html.twig */

class __TwigTemplate_09f8a...582f2 extends Twig_Template {

public function __construct(Twig_Environment $env) { ... }

protected function doGetParent(array $context) { ... }

protected function doDisplay(array $context, array $blocks) { ... }

// ...}

Page 143: Twig tips and tricks

The base template class

class __TwigTemplate_09f...2f2 extends Twig_Template{ // ...}

Page 144: Twig tips and tricks

lib/Twig/Template.php

abstract class Twig_Template { public function render(array $context) { // ... }

// ...

}

Page 145: Twig tips and tricks

lib/Twig/Template.php

abstract class Twig_Template { public function render(array $context) { // ... }

// ...

}

tweak this method to tweak templates

Page 146: Twig tips and tricks

Use a different base template$loader = new Twig_Loader_Filesystem('...');$twig = new Twig_Environment($loader, array( 'base_template_class' => '\ACME\MyTwigTemplate',));

# if you use Symfony2# app/config/config.yml

twig: base_template_class: "\ACME\MyTwigTemplate"

Page 147: Twig tips and tricks

The new base template classclass MyTwigTemplate extends Twig_Template{

public function render(array $context) { $trace = ...

return str_replace( "</body>", $trace."\n</body>",

parent::render($context) ); }}

Page 148: Twig tips and tricks

The new base template classabstract class MyTwigTemplate extends Twig_Template{

public function render(array $context) {

$trace = sprintf('<span data-host="%s" data-elapsed="%s sec."

data-timestamp="%s"></span>', php_uname(), microtime(true) - $_SERVER['REQUEST_TIME'], microtime(true) );

return str_replace("</body>", $trace."\n</body>", parent::render($context));

}

Page 149: Twig tips and tricks

Adding a trace to all web pages<html><head> ... </head> <body> ...

<span data-host="Darwin 10.8.0 ..." data-elapsed="0.97804594039 sec." data-timestamp="1339609672.9781"> </span>

</body></html>

Page 150: Twig tips and tricks

DEFENSIVE DESIGN

Page 151: Twig tips and tricks

Errors will happen

ERROR 500

ERROR

Page 152: Twig tips and tricks

Errors will happen

ERROR 500

ERROR

users prefer this

Page 153: Twig tips and tricks

Default values

{{ variable|default("value") }}

(discount {{ discount|default(0) }}%)Hi {{ user_name|default("") }}!

Page 154: Twig tips and tricks

The use_strict_variables option$loader = new Twig_Loader_Filesystem('...');$twig = new Twig_Environment($loader, array(

'use_strict_variables’ => false));

Page 155: Twig tips and tricks

The use_strict_variables option$loader = new Twig_Loader_Filesystem('...');$twig = new Twig_Environment($loader, array(

'use_strict_variables’ => false)); inexistent variables won’t

produce a Twig error page

Page 156: Twig tips and tricks

Ignore missing templates

{% include 'section_' ~ slug ~ '.twig' ignore missing %}

Page 157: Twig tips and tricks

Define fallback templates

{% extends [ 'layout_' ~ locale ~ '.html.twig', 'layout.html.twig'

] %}

Page 158: Twig tips and tricks

Use fallbacks and ignore errors

{% include [ 'layout_' ~ locale ~ '.html.twig', 'layout.html.twig'

] ignore missing %}

Page 159: Twig tips and tricks

Twig linter• A linter detects syntax errors

automatically.

• Use it as a preventive measure to detect errors before serving pages to users.

Page 160: Twig tips and tricks

Twig linter in practice$twig = new Twig_Environment($loader, array(..));

try { $twig->parse($twig->tokenize($plantilla)); echo "[OK]";} catch (Twig_Error_Syntax $e) { echo "[ERROR] There are some syntax errors";}

Page 161: Twig tips and tricks

INTEGRATING TWITTER

BOOTSTRAP

Page 162: Twig tips and tricks

Twitter Bootstrap

Page 163: Twig tips and tricks

Twitter Bootstrap grid model

3

Page 164: Twig tips and tricks

Each row adds up to 12 columns

3

12 = 3 + 9

Page 165: Twig tips and tricks

Each row adds up to 12 columns

3

12 = 3 + 4 + 5

Page 166: Twig tips and tricks

Each row adds up to 12 columns

3

12 = 3 + 2 + 3 + 4

Page 167: Twig tips and tricks

Most grids are based on 2 or 3 columns

3

2 columns

Page 168: Twig tips and tricks

Most grids are based on 2 or 3 columns

3

2 columns

Page 169: Twig tips and tricks

Most grids are based on 2 or 3 columns

3

3 columns

Page 170: Twig tips and tricks

extends + include = embed

Page 171: Twig tips and tricks

extends + include = embed

reuse a fixed structure

Page 172: Twig tips and tricks

extends + include = embed

reuse a fixed structure

reuse contents

Page 173: Twig tips and tricks

extends + include = embed

reuse a fixed structure

reuse contents and a flexible structure

reuse contents

Page 174: Twig tips and tricks

Reusable 2-column grid{# grid_2.twig #}<div class="row"> <div class="{{ col1_span }} {{ col1_offset }}">

{% block column1 %}{% endblock %} </div>

<div class="{{ col2_span }} {{ col2_offset }}">

{% block column2 %}{% endblock %} </div></div>

Page 175: Twig tips and tricks

2-column grid in practice

{% embed 'grid_2.twig' with { 'col1_span': 'span9',

'col2_span': 'span3' } %}

{% block column1 %} {# ... #} {% endblock %}

{% block column2 %} {# ... #} {% endblock %}

{% endembed %}

Page 176: Twig tips and tricks

2-column grid in practice

{% embed 'grid_2.twig' with { 'col1_span': 'span9',

'col2_span': 'span3' } %}

{% block column1 %} {# ... #} {% endblock %}

{% block column2 %} {# ... #} {% endblock %}

{% endembed %}

Twig Magic in progress...

Page 177: Twig tips and tricks

2-column grid in practice

{% embed 'grid_2.twig' with { 'layout': '9_3' } %}

{% block column1 %} {# ... #} {% endblock %}

{% block column2 %} {# ... #} {% endblock %}

{% endembed %}

Page 178: Twig tips and tricks

2-column grid in practice

{% embed 'grid_2.twig' with { 'layout': '9_3' } %}

{% set col1_span = layout|split('_')[0:]|join %}

{% set col2_span = layout|split('_')[1:]|join %}

Page 179: Twig tips and tricks

3-column grid

{% embed 'grid_3.twig' with { 'layout': '3_6_3' } %}

{% block column1 %} {# ... #} {% endblock %}

{% block column2 %} {# ... #} {% endblock %}

{% block column3 %} {# ... #} {% endblock %}

{% endembed %}

Page 180: Twig tips and tricks

3-column grid

{% embed 'grid_3.twig' with { 'layout': '3_6_3' } %}

{% set col1_span = layout|split('_')[0:]|join %}

{% set col2_span = layout|split('_')[1:]|join %}

{% set col3_span = layout|split('_')[2:]|join %}

Page 181: Twig tips and tricks

Recap• New and noteworthy

• Overriding filters

• Dynamic functions

• Custom tags

• Template loaders

• Namespaces

• Twig.js

• Sandbox

• Base template

• Defensive design

• Embed tag

Page 182: Twig tips and tricks

http://twig.sensiolabs.org

Page 183: Twig tips and tricks

THANK YOU.

Page 184: Twig tips and tricks

CONTACT ME