Twig tips and tricks

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

description

Twig tips, tricks, advanced stuff and new and noteworthy features.

Transcript of Twig tips and tricks

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