Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Twig doesn’t make templating your enemy!

Hugo Hamon
October 06, 2012

Twig doesn’t make templating your enemy!

Twig is a modern templating engine, which takes inspiration on the Smarty PHP project and Django Jinja templates in Python. Twig comes with an easy-to-use syntax and large modern features set for building web application. This talk will first introduce Twig and most of its native templating API including output escaping, data formatting, template inheritance, caching… In a second part, you will learn how you can easily extend Twig with third party extensions or by creating your own extensions. Finally, you will discover how you can integrate Twig with major PHP frameworks such as Symfony2, Zend Framework and even WordPress.

Hugo Hamon

October 06, 2012
Tweet

More Decks by Hugo Hamon

Other Decks in Technology

Transcript

  1. Hugo Hamon @hhamon Head of training at SensioLabs PHP fan

    for 10 years Travel addict Speaker at many conferences Book author
  2. <?php include 'header.php' ?> <?php include 'footer.php' ?> <div class="posts">

    <?php foreach ($posts as $post) : ?> <h2> <?php echo htmlspecialchars($post['title']) ?> </h2> <?php include 'post.php' ?> <?php echo substr($post['body'], 255).'...' ?> <?php endforeach ?> </div> Contextual content Manual escaping No isolation No separation of concerns
  3. 1. Installation & bootstrap 2. Twig for web designers 3.

    Twig for developers 4. Frameworks & CMS
  4. Extension Installation $ cd ext/twig $ phpize $ ./configure $

    make $ make install extension = twig.so
  5. Bootstrapping require __DIR__.'/vendor/autoload.php'; $loader = new Twig_Loader_Filesystem('/path/to/views'); $twig = new

    Twig_Environment($loader, array( 'cache' => '/path/to/cache', )); echo $twig->render('hello.twig', array( 'name' => 'Hugo' ));
  6. Concise Syntax {# ... comment something ... #} {% ...

    do something ... %} {{ ... display something ... }}
  7. PHP Compilation and Cache class __TwigTemplate_df6deba4655fee022981430 extends Twig_Template { protected

    function doDisplay(array $context, array $blocks = array()) { // line 1 echo "Hello "; echo twig_escape_filter($this->env, (isset($context["name"]) ? $context["name"] : null), "html", null, true); echo "!"; } // ... } Escaping is enabled
  8. Accurate Debugging Hello {{ rand(['John', 'Tom', 'Paul']) }}! Twig_Error_Syntax: The

    function "rand" does not exist. Did you mean "random" in "hello.twig" at line 3
  9. Automatic Output Escaping Hello {{ name }}! The variable is

    automatically escaped if it contains a string
  10. Escaping Strategies {{ name|raw }} {{ name|escape }} {{ name|e

    }} {{ name|e('html') }} {{ name|e('html_attr') }} {{ name|e('js') }} {{ name|e('css') }} {{ name|e('url') }}
  11. Output Escaping {% autoescape %} Everything will be automatically escaped

    in this block using the HTML strategy {% endautoescape %} {% autoescape 'js' %} Everything will be automatically escaped in this block using the js escaping strategy {% endautoescape %} {% autoescape false %} Everything will be outputted as is in this block {% endautoescape %}x
  12. Variables Abstraction {{ article.title }} The article can be an

    array or an object. The title can be a key of the array, a public property, a regular method or a getter method.
  13. Making Decisions {% if product.stock > 10 %} Available {%

    elseif product.stock > 0 %} Only {{ product.stock }} left! {% else %} Sold-out! {% endif %}
  14. Iterating Over a Collection <div class="posts"> {% for post in

    posts if post.active %} <h2>{{ post.title }}</h2> {{ post.body }} {% else %} No published posts yet. {% endfor %} </div>
  15. Get the Loop Context Variable Description loop.index The current iteration

    of the loop. (1 indexed) loop.index0 The current iteration of the loop. (0 indexed) loop.revindex The number of iterations from the end of the loop (1 indexed) loop.revindex0 The number of iterations from the end of the loop (0 indexed) loop.first True if rst iteration loop.last True if last iteration loop.length The number of items in the sequence loop.parent The parent context
  16. Operators o  Math: +, -, /, *, **, % o 

    Logical: or, and, xor o  Concatenation: ~ o  Comparison: <, >, <=, >=, == o  Containment: in, not in
  17. Generating Contents Hello {{ random(['John', 'Tom', 'Paul']) }}! {% for

    i in range(0,10) %} {{ cycle(['odd', 'even'], i) }}<br/> {% endfor %}
  18. Built-in Functions o  attribute o  block o  constant o  cycle

    o  date o  parent o  random o  range
  19. Formatting Contents {{ post.publishedAt|date('d/m/Y') }} {{ post.title|lower }} {{ post.title|upper

    }} {{ post.title|capitalize }} {{ post.title|title }} {{ post.tags|sort|join(', ') }} {{ post.author|default('Anonymous') }}
  20. Built-in Filters o  abs o  capitalize o  convert_encoding o  date

    o  date_modify o  default o  escape o  format o  join o  json_encode o  keys o  length o  lower o  merge o  nl2br o  number_format o  raw o  replace o  reverse o  slice o  sort o  striptags o  title o  trim o  upper o  url_encode
  21. Whitespace Control {% spaceless %} <p> Hello <strong>{{ name }}</strong>!

    </p> {% endspaceless %} <p>Hello <strong>Hugo</strong>!</p>
  22. Whitespace Control <p> Hello <strong> {{- name }} </strong>! </p>

    <p>Hello <strong>Hugo </strong>!</p> Value is trimmed on the left.
  23. Template Inclusion {% include "list.twig" with { "section": "blog", "posts":

    articles } only %} The included template is rendered with its own context. Isolation
  24. layout.twig {% block body %} {% endblock body %} {%

    block breadcrumb %} ... {% endblock %} blog.twig {% extends "layout.twig" %} {% block breadcrumb %} {{ parent() }} - Blog {% endblock breadcrumb %} {% block body %} <h1>Latest posts</h1> {% include "posts.twig" %} {% endblock body %}
  25. {% extends "layout.twig" %} {% block breadcrumb %} {{ parent()

    }} - Blog {% endblock breadcrumb %} {% block body %} <h1>Latest posts</h1> {% include "posts.twig" %} {% endblock body %} Template Inheritance Extend the parent template. Reuse the parent block default value. Fill the parent body block.
  26. Defining Macros {% macro input(name, value, type, size) %} <input

    type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" /> {% endmacro %} Default argument values are defined with the default filter
  27. Using Macros {% import "forms.html" as forms %} <p> {{

    forms.input('username') }} </p> Macros are imported under a specific namespace The macro is called with the prefixed namespace
  28. Whitelist Filtering $tags = array('if'); $functions = array('range'); $filters =

    array('upper'); $methods = array( 'Article' => array('getTitle', 'getBody'), 'Tag' => array('getName'), ); $properties = array( 'Article' => array('title', 'body'), 'Tag' => array('name'), );
  29. Enabling Sandbox Mode $policy = new Twig_Sandbox_SecurityPolicy( $tags, $filters, $methods,

    $properties, $functions ); $sandbox = new Twig_Extension_Sandbox($policy); $twig = new Twig_Environment(); $twig->addExtension($sandbox);
  30. Enabling Sandbox Mode {% block foo 'bar' %} {% include

    'users.html' %} Twig_Sandbox_SecurityError: Tag "block" is not allowed in "hello.twig" at line 2
  31. The Twig Lexer $template = <<<EOT Hello {{ name }}!

    EOT; $twig = new Twig_Environment(); $stream = $twig->tokenize($template);
  32. The Twig Compiler $php = $twig->compile($ast); class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712b extends Twig_Template

    { protected function doDisplay(array $context, array $blocks = array()) { // line 1 echo "Hello "; echo twig_escape_filter($this->env, $this->getContext($context, "name"), "ndex", null, true); echo "!"; } // some more code }
  33. Configuring Twig $twig = new Twig_Environment(array( 'strict_variables' => true, 'debug'

    => true, 'cache' => '/path/to/cache', 'base_template_class' => 'MyTemplate', 'autoescape' => 'js', 'optimizations' => -1, ));
  34. Customizing Twig T ags $twig = new Twig_Environment(); $lexer =

    new Twig_Lexer($twig, array( 'tag_comment' => array('{*', '*}'), 'tag_block' => array('{', '}'), 'tag_variable' => array('{$', '}'), )); $twig->setLexer($lexer); Smarty tags
  35. Extending the Twig Gramar o  Global variables o  Global functions

    o  Filters o  Tests o  Operators o  T ags
  36. Built-in Extensions Exension name Description Core Provides the core features

    Debug Provides debugging tools like the dump() function Escaper Provides escaping tools Optimizer Provides optimization tools Sandbox Provides the sandboxed environment capabilities
  37. The Twig Extension Class abstract class Twig_Extension implements Twig_ExtensionInterface {

    public function initRuntime(Twig_Environment $environment); public function getTokenParsers(); public function getNodeVisitors(); public function getFilters(); public function getTests(); public function getFunctions(); public function getOperators(); public function getGlobals(); }
  38. Creating a Twig Extension class GravatarExtension extends \Twig_Extension { public

    function getName() { return 'gravatar'; } } The abstract getName() method must be implemented.
  39. Registering the Extension $extension = new GravatarExtension(); $twig = new

    Twig_Environment(); $twig->addExtension($extension); Easy uh?!
  40. Adding a new Global Function class GravatarExtension extends \Twig_Extension {

    public function getFunctions() { return array( 'gravatar' => new \Twig_Function_Method($this, 'getGravatar'), ); } public function getGravatar($email, $size = '80', $rating = 'g') { $url = 'http://www.gravatar.com/avatar/%s?size=%u&rating=%s'; $hash = md5(strtolower(trim($email))); return sprintf($url, $hash, (int) $size, $rating); } }
  41. Using the Global Function {% set uri = gravatar("[email protected]") %}

    <img src="{{ uri }}" alt="hhamon"/> Twig now knows this function.
  42. Zend Framework 1 Ano_ZFTwig Automatic escaping Layout inheritance Javascripts helpers

    Stylesheets helpers Routing helpers https://github.com/benjamindulau/Ano_ZFTwig
  43. Zend Framework 2 Zfc_Twig Automatic escaping Layout inheritance Action rendering

    Forms helpers Events triggering https://github.com/ZF-Commons/ZfcTwig
  44. Drupal 7 Drupal themes Switch/Case tag I18n capabilities Extra filters

    Extra functions http://drupal.org/sandbox/ReneB/1075966