Twig tips and tricks
Upcoming SlideShare
Loading in...5
×
 

Twig tips and tricks

on

  • 52,518 views

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

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

Statistics

Views

Total Views
52,518
Views on SlideShare
49,269
Embed Views
3,249

Actions

Likes
72
Downloads
392
Comments
5

23 Embeds 3,249

http://www.symfony.es 1561
http://symfony.es 1109
https://twitter.com 265
http://yosymfony.com 134
http://byhbt.com 105
http://symfony2developer.com 14
http://www.linkedin.com 13
http://glimmering.eu 11
https://www.linkedin.com 6
http://logbook.test.spotman.ru 5
http://feeds.feedburner.com 5
http://symfony72.rssing.com 4
http://127.0.0.1 3
http://librosweb.es 3
http://devi-vm.local 2
http://work 2
http://ws88.design.amis.com 1
http://webtalks.dev 1
http://www.waffleme.com 1
http://tweetedtimes.com 1
http://scrambles10.minolito.com 1
http://www.twylah.com 1
http://pinterest.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

15 of 5 Post a comment

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • Hola,
    Soy nueva en symfony2 y twig y tengo un problema a la hora de utilizar 'split http://twig.sensiolabs.org/doc/filters/split.html en twig . Creo que es por la versión que tengo de twig. No he encontrado alguna manera para actualizarlo. Por favor alguien me puede ayudar.
    Aqui os paso el contenido de composer.json que uso:

    {

    'name': 'symfony/framework-standard-edition',

    'license': 'MIT',

    'type': 'project',

    'description': 'The \'Symfony Standard Edition\' distribution',

    'autoload': {

    'psr-0': { '': 'src/' }

    },

    'require': {

    'php': '>=5.3.3',

    'symfony/symfony': '~2.4',

    'doctrine/orm': '~2.2,>=2.2.3',

    'doctrine/doctrine-bundle': '~1.2',

    'twig/extensions': '~1.0',

    'symfony/assetic-bundle': '~2.3',

    'symfony/swiftmailer-bundle': '~2.3',

    'symfony/monolog-bundle': '~2.4',

    'sensio/distribution-bundle': '~2.3',

    'sensio/framework-extra-bundle': '~3.0',

    'sensio/generator-bundle': '~2.3',

    'incenteev/composer-parameter-handler': '~2.0',

    'doctrine/mongodb-odm': '1.0.*@dev',

    'doctrine/mongodb-odm-bundle': '3.0.*@dev'

    },

    'scripts': {

    'post-install-cmd': [

    'Incenteev\\ParameterHandler\\ScriptHandler::buildParameters',

    'Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap',

    'Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache',

    'Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets',

    'Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile'

    ],

    'post-update-cmd': [

    'Incenteev\\ParameterHandler\\ScriptHandler::buildParameters',

    'Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap',

    'Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache',

    'Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets',

    'Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile'

    ]

    },

    'config': {

    'bin-dir': 'bin'

    },

    'extra': {

    'symfony-app-dir': 'app',

    'symfony-web-dir': 'web',

    'incenteev-parameters': {

    'file': 'app/config/parameters.yml'

    },

    'branch-alias': {

    'dev-master': '2.4-dev'

    }

    },

    'require': {

    'raulfraile/ladybug-bundle': '~1.0'

    }



    }
    Are you sure you want to
    Your message goes here
    Processing…
  • Very useful and great tricks. Thanks a lot.
    But it will be much better if to provide filepaths for every trick like in symfony docs. Something like '#/app/config/config.yml' at the beginning.
    Are you sure you want to
    Your message goes here
    Processing…
  • How to refresh the modified template in TWIG. The local files are showing the modification but when the files are uploaded on the live server, the changes doesn't show up. The changes were made in the files contained inside the cache folder.
    Are you sure you want to
    Your message goes here
    Processing…
  • Very Nice Slide
    Are you sure you want to
    Your message goes here
    Processing…
  • Hola Javier, excelente Slide, lástima no haber podido participar en la conferencia.
    Saludos desde Cuba
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Twig tips and tricks Twig tips and tricks Presentation Transcript

    • TWIG tips & tricksSUNSHINEPHP JAVIER EGUILUZFEBRUARY 8TH 2013
    • Thanks to sponsors and organizers Adam Pablo Culp Godel
    • About me Javier Eguiluz I’m a programmer and trainer from Spain.
    • ISYMFONY
    • I’m a long-term Symfony enthusiast
    • My Symfony2 book Agile web development with Symfony2
    • My Symfony website WINNER 2011 Best Symfony Blog
    • I’m the « A week of Symfony» guy
    • I’m the « A week of Symfony» guy this is me!
    • ITWIG
    • 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
    • AGENDA
    • Agenda• Tips and tricks about Twig.• Advanced features.• Defensive template design.• Best practices.• New and noteworthy Twig features.
    • NEW &NOTEWORTHY
    • Twig development activity is crazy!(last 12 months) Twig Jinja2 (PHP, Symfony) (Python, Django)Commits 525 11Authors 35 5Versions released 23 0
    • New and noteworthy 1 2 3 4 5
    • «template_from_string» function{% set user_template = "{{ description[0:80] }}<br/> Price: {{ product.price }}" %}{% includetemplate_from_string(user_template) %}
    • New and noteworthy 1 2 3 4 5
    • «include» function{% include template.twig %}{{ include(template.twig) }} equivalent
    • «include» function WRONG{% set content = include(index.twig) %} OK{% set content %}{{ include(index.twig) }}{% endset %}
    • «include» function WRONG{{ include(index.twig)|striptags(<a>)[0:80] }}{% set content %} OK{{ include(index.twig) }}{% endset %}{{ content|striptags(<a>)[0:80] }}
    • New and noteworthy 1 2 3 4 5
    • «first» and «last» filters{% set firstElement = array|first %}{% set lastElement = array|last %}
    • «first» and «last» filters{% set first = array[0] %}{% set last = array[array|length - 1] %}
    • «first» and «last» filters{% set first = array[0] %}{% set last = array[array|length - 1] %} only works for zero- indexed numeric arrays
    • «first» and «last» filters{{ [1, 2, 3, 4]|first }} 1{{ { a: 1, b: 2, c: 3, d: 4 }|first }} 1{{ 1234|first }} 1 result
    • New and noteworthy 1 2 3 4 5
    • 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) }}
    • Named arguments{{ text|convert_encoding(UTF-8, ISO-8859-1) }}{{ text|convert_encoding( to=UTF-8, from=ISO-8859-1) }} equivalent{{ text|convert_encoding( from=ISO-8859-1, to=UTF-8) }}
    • Named arguments{{ text|convert_encoding(UTF-8, ISO-8859-1) }}{{ text|convert_encoding( to=UTF-8, from=ISO-8859-1) }} equivalent{{ text|convert_encoding( from=ISO-8859-1, to=UTF-8) }} argument order changed!
    • Named arguments{{ "now"|date("Y-m-d", "America/New_York") }} mandatory to set the second argument{{ "now"|date(timezone="America/New_York") }} just set the argument you need
    • New and noteworthy 1 2 3 4 5
    • Functions and filters before 1.12$twig->addFunction(functionName, new Twig_Function_Function(someFunction));$twig->addFunction(otherFunction, new Twig_Function_Method($this, someMethod));
    • 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() { ... }));
    • 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() { ... }));
    • 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
    • OVERRIDING FILTERS
    • USE WITHCAUTION
    • {% for i in array|sort %} {# ... #}{% endfor %}
    • How is the array sorted?{% for i in array|sort %} {# ... #}{% endfor %}
    • PHP defines 15 sorting functions• asort() • array_multisort()• arsort() • natcasesort()• krsort() • natsort()• ksort() • rsort()• rsort() • shuffle()• shuffle() • uasort()• sort() • uksort()• usort()
    • Overriding filters• Where can I find the PHP function used by Twig?• How can I override it with my own implementation?
    • Where Twig defines everythinglib/twig/Extension/Core.phpclass Twig_Extension_Core extends Twig_Extension { public function getTokenParsers() { return array( +1,300 lines new Twig_TokenParser_For(), new Twig_TokenParser_If(), class! 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), // ... );
    • Where Twig defines everythinglib/twig/Extension/Core.phpclass Twig_Extension_Core extends Twig_Extension { public function getTokenParsers() { return array( • Filters new Twig_TokenParser_For(), new Twig_TokenParser_If(), new Twig_TokenParser_Extends(), • Functions new Twig_TokenParser_Include(), new Twig_TokenParser_Block(), // ... • Tags • Operators ); } public function getFilters() { $filters = array( • Tests format => new Twig_Filter_Function(sprintf), replace => new Twig_Filter_Function(strtr), abs => new Twig_Filter_Function(abs), // ... );
    • «sort» filter uses «asort» functionnew Twig_SimpleFilter(sort, twig_sort_filter),// ...function twig_sort_filter($array){ asort($array); return $array;}
    • Overriding filters✔ Where can I find the PHP function• used by Twig?• How can I override it with my own implementation?
    • Replace «asort» with «natcasesort»// asortA1, a0, a10, a2// natcasesorta0, A1, a2, a10
    • 1. Define a new Twig extensionclass MyCoreExtension extends Twig_Extension_Core { // ...}
    • 2. Define the new «sort» filterclass MyCoreExtension extends Twig_Extension_Core { public function getFilters() { // ... }}
    • 2. Define the new «sort» filterclass MyCoreExtension extends Twig_Extension_Core { public function getFilters() { return array_merge( parent::getFilters(), array( ... ) ); }}
    • 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; }}
    • 3. Register the new extension$twig = new Twig_Environment( ... );$twig->addExtension( new MyCoreExtension());
    • This is now natcasesort{% for i in array|sort %} {# ... #}{% endfor %}
    • DYNAMICFUNCTIONS
    • WordPress template tagsthe_ID()the_title()the_time()the_content()the_category()the_shortlink()
    • WordPress template tagsthe_ID() class Post { $id = ...the_title() $title = ...the_time() $time = ...the_content() $content = ... $category = ...the_category() $shortlink = ...the_shortlink() // ... }
    • the_*()
    • Variable functions$twig->addFunction( the_*, new Twig_Function_Function(wordpress));function wordpress($property, $options){ // ...}
    • Variable functions$twig->addFunction( the_*, new Twig_Function_Function(wordpress));function wordpress($property, $options){ // ...}
    • Variable functions$twig->addFunction( the_*, new Twig_Function_Function(wordpress)); don’t use regexps, just asterisksfunction wordpress($property, $options){ // ...}
    • Variable functions in practice{{ the_ID() }}function wordpress(ID) { ... }{{ the_content() }}function wordpress(content) { ... }
    • Variable functions in practice{{ the_title(<h3>, </h3>) }}function wordpress( title, array(<h3>, </h3>)) { ... }
    • WordPress template tagsnext_image_link()next_post_link()next_posts_link()previous_image_link()previous_post_link()previous_posts_link()
    • WordPress template tagsnext_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()
    • WordPress template tagsnext_image_link() *_*_link()next_post_link() *_*_link()next_posts_link() *_*_link()previous_image_link() *_*_link()previous_post_link() *_*_link()previous_posts_link() *_*_link()
    • USE WITHCAUTION
    • php_*()
    • 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)));
    • Exposing PHP functions{{ php_print_r([value1, value2]) }}{{ php_crypt(mypassword) }}{{ php_memory_get_usage() }}{{ php_uniqid() }}Referencehttp://github.com/lidaa/LidaaTwigBundle
    • CUSTOM TAGS
    • {% source ‘...’ %}
    • {% source ‘...’ %} file_get_contents()
    • The new «source» tag{% source home.twig %}{% source ../../../composer.json %}
    • How does Twig work internally{% sourcesimple.twig %} Twig PHP class __TwigTemplate_06dff1ec7c2c ceb3f45ac76fc059b730 template file extends Twig_Template { public function{# ... #} __construct(Twig_Environment $env) { parent::__construct($env); $this->parent = $this- >env- >loadTemplate("layout.twig"); $this->blocks = array( Lexer Parser Compiler
    • How does Twig work internally{% sourcesimple.twig %} Twig PHP class __TwigTemplate_06dff1ec7c2c ceb3f45ac76fc059b730 template file extends Twig_Template { public function{# ... #} __construct(Twig_Environment you must $env) { parent::__construct($env); provide these $this->parent = $this- >env- >loadTemplate("layout.twig"); $this->blocks = array( Lexer Parser Compiler
    • 1. Create a new token parserclass SourceTokenParser extends Twig_TokenParser{ public function getTag() { return source; }}
    • 2. Register the new token parser$loader = new Twig_Loader_Filesystem(...);$twig = new Twig_Environment($loader, array(...));$twig->addTokenParser( new SourceTokenParser());
    • 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()); }}
    • 3. Fill in the «parse» methodclass SourceTokenParser extends Twig_TokenParser{ public function parse(Twig_Token $token) { this is hard $lineno = $token->getLine(); $value = $this->parser->getExpressionParser() ->parseExpression(); $this->parser->getStream() ->expect(Twig_Token::BLOCK_END_TYPE); return new SourceNode($value, $lineno, $this->getTag()); }}
    • 4. Define the node class that compiles tagsclass 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();) ; }}
    • 4. Define the node class that compiles tagsclass 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 -> // ... this is ->write(echo file_get_contents() very hard ->subcompile($this->getNode(file)) ->raw();) ; }}
    • The compiled PHP template// line 3echo file_get_contents("simple.twig");// ... {% source simple.twig %}// line 5echo file_get_contents("../../../composer.json");
    • TEMPLATE LOADERS
    • 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);
    • 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());// ...
    • 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));// ...
    • 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!// ...
    • 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) );}// ...
    • Storing templates in several folders$loader = new Twig_Loader_Filesystem(array( __DIR__./default, __DIR__./templates, __DIR__./themes));$twig = new Twig_Environment($loader, array());// ...
    • Storing templates in several folders$loader = new Twig_Loader_Filesystem(array( __DIR__./default, __DIR__./templates, __DIR__./themes));$twig = new Twig_Environment($loader,went wrong. Whoops, looks like something array());// ... Twig_Error_Loader: The "_DIR_/templates" directory does not exist.
    • 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());// ...
    • 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());// ...
    • Prioritizing template folders$loader = new Twig_Loader_Filesystem(/templates);if(...) { $loader->addPath($user_slug./templates); $loader->prependPath($user_slug./themes);} path is added before any other path$twig = new Twig_Environment($loader, array());// ...
    • It’s difficult to prioritize folders$loader ->addPath(...) ->addPath(...) ->prependPath(...) ->addPath(...) ->prependPath(...);
    • NAMESPACES
    • Namespaces are better than folders templates/ themes/index.twig admin/index.twig frontend/index.twig
    • 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);
    • App doesn’t work if folders change templates/ themes/index.twig frontend/index.twig admin/
    • App doesn’t work if folders change templates/ themes/index.twig Whoops, looks like something went wrong. frontend/index.twig Twig_Error_Loader: The "admin/index.twig" admin/ template does not exist.
    • 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);
    • 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);
    • 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);
    • 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); logical path physical path
    • 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);
    • TWIG.JS
    • Twig.js = Twig inside JavaScript
    • Twig.js = Twig inside JavaScriptTWIG TWIG
    • Twig.js = Twig inside JavaScriptTWIG TWIGHTML HTML
    • Twig.js = Twig inside JavaScriptTWIG TWIGHTML HTML JSJS
    • Twig.js = Twig inside JavaScriptTWIG TWIGHTML HTML JSJS TWIGTWIG
    • «A tale of two twig.js» twig.js by Johannes Schmitt http://github.com/schmittjoh/twig.js twig.js by John Roepke https://github.com/justjohn/twig.js
    • «A tale of two twig.js» twig.js by Johannes Schmitt http://github.com/schmittjoh/twig.js twig.js by John Roepke https://github.com/justjohn/twig.js
    • 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).
    • 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>
    • Defining the template important!{# tweet.twig #}{% twig_js name="tweet" %}<p> {{ message }} <span>by {{ author }}</span> <time datetime="{{ published_at|date }}"> {{ published_at|date }} </time></p>
    • 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>
    • 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" %}
    • «A tale of two twig.js» twig.js by Johannes Schmitt http://github.com/schmittjoh/twig.js twig.js by John Roepke http://github.com/justjohn/twig.js
    • 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.
    • Defining the template{# tweet.twig #} not necessary{% twig_js name="tweet" %}<p> {{ message }} <span>by {{ author }}</span> <time datetime="{{ published_at|date }}"> {{ published_at|date }} </time></p>
    • 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> template’s }); source code var html = template.render({ message: "...", author: "...", published_at: "..." });</script>
    • 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>
    • SANDBOX
    • A simple object$offer = new Offer();$offer->title = "Lorem Ipsum Dolor Sit Amet";$offer->description = "Ut enim ad minim veniam ...";$offer->commission = 20;
    • 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
    • Templates can show any propertyOffer data----------Title: {{ offer.title }}Description: {{ offer.description }}Commission: {{ offer.commission }}
    • 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.
    • 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());
    • Define a new security policy$loader = new Twig_Loader_Filesystem(...); commission is$twig = new Twig_Environment($loader, array());$properties = array( not included Offer => array(title, description));$policy = new Twig_Sandbox_SecurityPolicy( array(), array(), array(), $properties, array());
    • 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);
    • 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); ALL templates are sandboxed$twig->addExtension($sandbox);
    • The template now displays an errorOffer data----------Title: {{ offer.title }}Description: {{ offer.description }}Commission: {{ offer.commission }}
    • The template now displays an errorOffer data----------Title: {{ offer.title }}Description: {{ offer.description }} Whoops, looks like something went wrong.Commission: {{ offer.commission }} Calling "comission" property on a "Offer" object is not allowed in ... at line 6.
    • Security policy arguments$policy = new Twig_Sandbox_SecurityPolicy( $tags, $filters, $methods, $properties, $functions);
    • Allow to use just 3 filters$policy = new Twig_Sandbox_SecurityPolicy( $tags, array(escape, upper, lower), $methods, $properties, $functions);
    • Allow to use just 2 tags$policy = new Twig_Sandbox_SecurityPolicy( array(include, extends), $filters, $methods, $properties, $functions);
    • 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);
    • THE BASETEMPLATE
    • 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>
    • Twig cache
    • 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) { ... } // ...}
    • 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) { ... } // ...}
    • The base template classclass __TwigTemplate_09f...2f2 extends Twig_Template{ // ...}
    • lib/Twig/Template.phpabstract class Twig_Template { public function render(array $context) { // ... } // ...}
    • lib/Twig/Template.phpabstract class Twig_Template { public function render(array $context) { tweak this method // ... to tweak templates } // ...}
    • Use a different base template$loader = new Twig_Loader_Filesystem(...);$twig = new Twig_Environment($loader, array( base_template_class => ACMEMyTwigTemplate,));# if you use Symfony2# app/config/config.ymltwig: base_template_class: "ACMEMyTwigTemplate"
    • 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) ); }}
    • 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)); }
    • 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>
    • DEFENSIVE DESIGN
    • Errors will happen ERROR 500 ERROR
    • Errors will happen ERROR 500 ERROR users prefer this
    • Default values{{ variable|default("value") }}(discount {{ discount|default(0) }}%)Hi {{ user_name|default("") }}!
    • The use_strict_variables option$loader = new Twig_Loader_Filesystem(...);$twig = new Twig_Environment($loader, array( use_strict_variables’ => false));
    • 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
    • Ignore missing templates{% include section_ ~ slug ~ .twig ignore missing %}
    • Define fallback templates{% extends [ layout_ ~ locale ~ .html.twig, layout.html.twig] %}
    • Use fallbacks and ignore errors{% include [ layout_ ~ locale ~ .html.twig, layout.html.twig] ignore missing %}
    • Twig linter• A linter detects syntax errors automatically.• Use it as a preventive measure to detect errors before serving pages to users.
    • 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";}
    • INTEGRATING TWITTER BOOTSTRAP
    • Twitter Bootstrap
    • Twitter Bootstrap grid model 3
    • Each row adds up to 12 columns 3 12 = 3 + 9
    • Each row adds up to 12 columns 3 12 = 3 + 4 + 5
    • Each row adds up to 12 columns 3 12 = 3 + 2 + 3 + 4
    • Most grids are based on 2 or 3 columns 3 2 columns
    • Most grids are based on 2 or 3 columns 3 2 columns
    • Most grids are based on 2 or 3 columns 3 3 columns
    • extends + include = embed
    • reuse a fixed structureextends + include = embed
    • reuse a fixed reuse structure contentsextends + include = embed
    • reuse a fixed reuse structure contentsextends + include = embed reuse contents and a flexible structure
    • 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>
    • 2-column grid in practice{% embed grid_2.twig with { col1_span: span9, col2_span: span3 } %} {% block column1 %} {# ... #} {% endblock %} {% block column2 %} {# ... #} {% endblock %}{% endembed %}
    • 2-column grid in practice{% embed grid_2.twig with { col1_span:Magic Twig span9, in progress... col2_span: span3 } %} {% block column1 %} {# ... #} {% endblock %} {% block column2 %} {# ... #} {% endblock %}{% endembed %}
    • 2-column grid in practice{% embed grid_2.twig with { layout: 9_3 } %} {% block column1 %} {# ... #} {% endblock %} {% block column2 %} {# ... #} {% endblock %}{% endembed %}
    • 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 %}
    • 3-column grid{% embed grid_3.twig with { layout: 3_6_3 } %} {% block column1 %} {# ... #} {% endblock %} {% block column2 %} {# ... #} {% endblock %} {% block column3 %} {# ... #} {% endblock %}{% endembed %}
    • 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 %}
    • Recap• New and noteworthy • Sandbox• Overriding filters • Base template• Dynamic functions • Defensive design• Custom tags • Embed tag• Template loaders• Namespaces• Twig.js
    • http://twig.sensiolabs.org
    • THANK YOU.
    • CONTACT ME
    • Contact me• javier.eguiluz@gmail.com• linkedin.com/in/javiereguiluz• twitter.com/javiereguiluz• github.com/javiereguiluz