Successfully reported this slideshow.
Your SlideShare is downloading. ×

Twig tips and tricks

Ad

TWIG
            tips & tricks
SUNSHINEPHP             JAVIER EGUILUZ
FEBRUARY 8TH 2013

Ad

Thanks to sponsors and organizers




      Adam            Pablo
      Culp            Godel

Ad

About me


           Javier Eguiluz
           I’m a programmer
           and trainer from Spain.

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Loading in …3
×

Check these out next

1 of 185 Ad
1 of 185 Ad
Advertisement

More Related Content

Advertisement
Advertisement

Twig tips and tricks

  1. TWIG tips & tricks SUNSHINEPHP JAVIER EGUILUZ FEBRUARY 8TH 2013
  2. Thanks to sponsors and organizers Adam Pablo Culp Godel
  3. About me Javier Eguiluz I’m a programmer and trainer from Spain.
  4. I SYMFONY
  5. I’m a long-term Symfony enthusiast
  6. My Symfony2 book Agile web development with Symfony2
  7. My Symfony website WINNER 2011 Best Symfony Blog
  8. I’m the « A week of Symfony» guy
  9. I’m the « A week of Symfony» guy this is me!
  10. I TWIG
  11. 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
  12. AGENDA
  13. Agenda • Tips and tricks about Twig. • Advanced features. • Defensive template design. • Best practices. • New and noteworthy Twig features.
  14. NEW & NOTEWORTHY
  15. Twig development activity is crazy! (last 12 months) Twig Jinja2 (PHP, Symfony) (Python, Django) Commits 525 11 Authors 35 5 Versions released 23 0
  16. New and noteworthy 1 2 3 4 5
  17. «template_from_string» function {% set user_template = "{{ description[0:80] }} <br/> Price: {{ product.price }}" %} {% include template_from_string(user_template) %}
  18. New and noteworthy 1 2 3 4 5
  19. «include» function {% include 'template.twig' %} {{ include('template.twig') }} equivalent
  20. «include» function WRONG {% set content = include('index.twig') %} OK {% set content %} {{ include('index.twig') }} {% endset %}
  21. «include» function WRONG {{ include('index.twig')|striptags('<a>')[0:80] }} {% set content %} OK {{ include('index.twig') }} {% endset %} {{ content|striptags('<a>')[0:80] }}
  22. New and noteworthy 1 2 3 4 5
  23. «first» and «last» filters {% set firstElement = array|first %} {% set lastElement = array|last %}
  24. «first» and «last» filters {% set first = array[0] %} {% set last = array[array|length - 1] %}
  25. «first» and «last» filters {% set first = array[0] %} {% set last = array[array|length - 1] %} only works for zero- indexed numeric arrays
  26. «first» and «last» filters {{ [1, 2, 3, 4]|first }} 1 {{ { a: 1, b: 2, c: 3, d: 4 }|first }} 1 {{ '1234'|first }} 1 result
  27. New and noteworthy 1 2 3 4 5
  28. 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') }}
  29. 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') }}
  30. 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!
  31. 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
  32. New and noteworthy 1 2 3 4 5
  33. Functions and filters before 1.12 $twig->addFunction('functionName', new Twig_Function_Function('someFunction') ); $twig->addFunction('otherFunction', new Twig_Function_Method($this, 'someMethod') );
  34. 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() { ... } ));
  35. 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() { ... } ));
  36. 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
  37. OVERRIDING FILTERS
  38. USE WITH CAUTION
  39. {% for i in array|sort %} {# ... #} {% endfor %}
  40. How is the array sorted? {% for i in array|sort %} {# ... #} {% endfor %}
  41. PHP defines 15 sorting functions • asort() • array_multisort() • arsort() • natcasesort() • krsort() • natsort() • ksort() • rsort() • rsort() • shuffle() • shuffle() • uasort() • sort() • uksort() • usort()
  42. Overriding filters • Where can I find the PHP function used by Twig? • How can I override it with my own implementation?
  43. Where Twig defines everything lib/twig/Extension/Core.php class 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'), // ... );
  44. Where Twig defines everything lib/twig/Extension/Core.php class 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'), // ... );
  45. «sort» filter uses «asort» function new Twig_SimpleFilter('sort', 'twig_sort_filter'), // ... function twig_sort_filter($array) { asort($array); return $array; }
  46. Overriding filters ✔ Where can I find the PHP function • used by Twig? • How can I override it with my own implementation?
  47. Replace «asort» with «natcasesort» // asort A1, a0, a10, a2 // natcasesort a0, A1, a2, a10
  48. 1. Define a new Twig extension class MyCoreExtension extends Twig_Extension_Core { // ... }
  49. 2. Define the new «sort» filter class MyCoreExtension extends Twig_Extension_Core { public function getFilters() { // ... } }
  50. 2. Define the new «sort» filter class MyCoreExtension extends Twig_Extension_Core { public function getFilters() { return array_merge( parent::getFilters(), array( ... ) ); } }
  51. 2. Define the new «sort» filter class 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; } }
  52. 3. Register the new extension $twig = new Twig_Environment( ... ); $twig->addExtension( new MyCoreExtension() );
  53. This is now natcasesort {% for i in array|sort %} {# ... #} {% endfor %}
  54. DYNAMIC FUNCTIONS
  55. WordPress template tags the_ID() the_title() the_time() the_content() the_category() the_shortlink()
  56. WordPress template tags the_ID() class Post { $id = ... the_title() $title = ... the_time() $time = ... the_content() $content = ... $category = ... the_category() $shortlink = ... the_shortlink() // ... }
  57. the_*()
  58. Variable functions $twig->addFunction( 'the_*', new Twig_Function_Function('wordpress') ); function wordpress($property, $options) { // ... }
  59. Variable functions $twig->addFunction( 'the_*', new Twig_Function_Function('wordpress') ); function wordpress($property, $options) { // ... }
  60. Variable functions $twig->addFunction( 'the_*', new Twig_Function_Function('wordpress') ); don’t use regexps, just asterisks function wordpress($property, $options) { // ... }
  61. Variable functions in practice {{ the_ID() }} function wordpress('ID') { ... } {{ the_content() }} function wordpress('content') { ... }
  62. Variable functions in practice {{ the_title('<h3>', '</h3>') }} function wordpress( 'title', array('<h3>', '</h3>') ) { ... }
  63. WordPress template tags next_image_link() next_post_link() next_posts_link() previous_image_link() previous_post_link() previous_posts_link()
  64. 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()
  65. 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()
  66. USE WITH CAUTION
  67. php_*()
  68. 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') ));
  69. Exposing PHP functions {{ php_print_r(['value1', 'value2']) }} {{ php_crypt('mypassword') }} {{ php_memory_get_usage() }} {{ php_uniqid() }} Reference http://github.com/lidaa/LidaaTwigBundle
  70. CUSTOM TAGS
  71. {% source ‘...’ %}
  72. {% source ‘...’ %} file_get_contents()
  73. The new «source» tag {% source 'home.twig' %} {% source '../../../composer.json' %}
  74. How does Twig work internally {% source 'simple.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
  75. How does Twig work internally {% source 'simple.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
  76. 1. Create a new token parser class SourceTokenParser extends Twig_TokenParser { public function getTag() { return 'source'; } }
  77. 2. Register the new token parser $loader = new Twig_Loader_Filesystem(...); $twig = new Twig_Environment($loader, array(...)); $twig->addTokenParser( new SourceTokenParser() );
  78. 3. Fill in the «parse» method class 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()); } }
  79. 3. Fill in the «parse» method class 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()); } }
  80. 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(');') ; } }
  81. 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 -> // ... this is ->write('echo file_get_contents(') very hard ->subcompile($this->getNode('file')) ->raw(');') ; } }
  82. The compiled PHP template // line 3 echo file_get_contents("simple.twig"); // ... {% source 'simple.twig' %} // line 5 echo file_get_contents("../../../composer.json");
  83. TEMPLATE LOADERS
  84. 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');
  85. 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()); // ...
  86. 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 )); // ...
  87. 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! // ...
  88. 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') ); } // ...
  89. Storing templates in several folders $loader = new Twig_Loader_Filesystem(array( __DIR__.'/default', __DIR__.'/templates', __DIR__.'/themes' )); $twig = new Twig_Environment($loader, array()); // ...
  90. 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.
  91. 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()); // ...
  92. 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()); // ...
  93. 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()); // ...
  94. It’s difficult to prioritize folders $loader ->addPath(...) ->addPath(...) ->prependPath(...) ->addPath(...) ->prependPath(...) ;
  95. NAMESPACES
  96. Namespaces are better than folders templates/ themes/index.twig admin/index.twig frontend/index.twig
  97. 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');
  98. App doesn’t work if folders change templates/ themes/index.twig frontend/index.twig admin/
  99. 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.
  100. 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');
  101. 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');
  102. 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');
  103. 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
  104. 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');
  105. TWIG.JS
  106. Twig.js = Twig inside JavaScript
  107. Twig.js = Twig inside JavaScript TWIG TWIG
  108. Twig.js = Twig inside JavaScript TWIG TWIG HTML HTML
  109. Twig.js = Twig inside JavaScript TWIG TWIG HTML HTML JS JS
  110. Twig.js = Twig inside JavaScript TWIG TWIG HTML HTML JS JS TWIG TWIG
  111. «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
  112. «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
  113. 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).
  114. 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>
  115. 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>
  116. 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>
  117. 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" %}
  118. «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
  119. 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.
  120. 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>
  121. 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>
  122. 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>
  123. SANDBOX
  124. A simple object $offer = new Offer(); $offer->title = "Lorem Ipsum Dolor Sit Amet"; $offer->description = "Ut enim ad minim veniam ..."; $offer->commission = 20;
  125. 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
  126. Templates can show any property Offer data ---------- Title: {{ offer.title }} Description: {{ offer.description }} Commission: {{ offer.commission }}
  127. 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.
  128. 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() );
  129. 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() );
  130. 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);
  131. 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);
  132. The template now displays an error Offer data ---------- Title: {{ offer.title }} Description: {{ offer.description }} Commission: {{ offer.commission }}
  133. The template now displays an error Offer 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.
  134. Security policy arguments $policy = new Twig_Sandbox_SecurityPolicy( $tags, $filters, $methods, $properties, $functions );
  135. Allow to use just 3 filters $policy = new Twig_Sandbox_SecurityPolicy( $tags, array('escape', 'upper', 'lower'), $methods, $properties, $functions );
  136. Allow to use just 2 tags $policy = new Twig_Sandbox_SecurityPolicy( array('include', 'extends'), $filters, $methods, $properties, $functions );
  137. 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 );
  138. THE BASE TEMPLATE
  139. 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>
  140. Twig cache
  141. 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) { ... } // ... }
  142. 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) { ... } // ... }
  143. The base template class class __TwigTemplate_09f...2f2 extends Twig_Template { // ... }
  144. lib/Twig/Template.php abstract class Twig_Template { public function render(array $context) { // ... } // ... }
  145. lib/Twig/Template.php abstract class Twig_Template { public function render(array $context) { tweak this method // ... to tweak templates } // ... }
  146. 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.yml twig: base_template_class: "ACMEMyTwigTemplate"
  147. The new base template class class MyTwigTemplate extends Twig_Template { public function render(array $context) { $trace = ... return str_replace( "</body>", $trace."n</body>", parent::render($context) ); } }
  148. The new base template class abstract 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)); }
  149. 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>
  150. DEFENSIVE DESIGN
  151. Errors will happen ERROR 500 ERROR
  152. Errors will happen ERROR 500 ERROR users prefer this
  153. Default values {{ variable|default("value") }} (discount {{ discount|default(0) }}%) Hi {{ user_name|default("") }}!
  154. The use_strict_variables option $loader = new Twig_Loader_Filesystem('...'); $twig = new Twig_Environment($loader, array( 'use_strict_variables’ => false ));
  155. 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
  156. Ignore missing templates {% include 'section_' ~ slug ~ '.twig' ignore missing %}
  157. Define fallback templates {% extends [ 'layout_' ~ locale ~ '.html.twig', 'layout.html.twig' ] %}
  158. Use fallbacks and ignore errors {% include [ 'layout_' ~ locale ~ '.html.twig', 'layout.html.twig' ] ignore missing %}
  159. Twig linter • A linter detects syntax errors automatically. • Use it as a preventive measure to detect errors before serving pages to users.
  160. 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"; }
  161. INTEGRATING TWITTER BOOTSTRAP
  162. Twitter Bootstrap
  163. Twitter Bootstrap grid model 3
  164. Each row adds up to 12 columns 3 12 = 3 + 9
  165. Each row adds up to 12 columns 3 12 = 3 + 4 + 5
  166. Each row adds up to 12 columns 3 12 = 3 + 2 + 3 + 4
  167. Most grids are based on 2 or 3 columns 3 2 columns
  168. Most grids are based on 2 or 3 columns 3 2 columns
  169. Most grids are based on 2 or 3 columns 3 3 columns
  170. extends + include = embed
  171. reuse a fixed structure extends + include = embed
  172. reuse a fixed reuse structure contents extends + include = embed
  173. reuse a fixed reuse structure contents extends + include = embed reuse contents and a flexible structure
  174. 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>
  175. 2-column grid in practice {% embed 'grid_2.twig' with { 'col1_span': 'span9', 'col2_span': 'span3' } %} {% block column1 %} {# ... #} {% endblock %} {% block column2 %} {# ... #} {% endblock %} {% endembed %}
  176. 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 %}
  177. 2-column grid in practice {% embed 'grid_2.twig' with { 'layout': '9_3' } %} {% block column1 %} {# ... #} {% endblock %} {% block column2 %} {# ... #} {% endblock %} {% endembed %}
  178. 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 %}
  179. 3-column grid {% embed 'grid_3.twig' with { 'layout': '3_6_3' } %} {% block column1 %} {# ... #} {% endblock %} {% block column2 %} {# ... #} {% endblock %} {% block column3 %} {# ... #} {% endblock %} {% endembed %}
  180. 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 %}
  181. Recap • New and noteworthy • Sandbox • Overriding filters • Base template • Dynamic functions • Defensive design • Custom tags • Embed tag • Template loaders • Namespaces • Twig.js
  182. http://twig.sensiolabs.org
  183. THANK YOU.
  184. CONTACT ME
  185. Contact me • javier.eguiluz@gmail.com • linkedin.com/in/javiereguiluz • twitter.com/javiereguiluz • github.com/javiereguiluz

×