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. Twig does not make
    templating your enemy!

    View full-size slide

  2. Hugo Hamon
    @hhamon


    Head of training at SensioLabs

    PHP fan for 10 years

    Travel addict

    Speaker at many conferences

    Book author

    View full-size slide

  3. How many of you are…

    … web designers?
    … web developers?

    View full-size slide

  4. So you really think
    PHP is a good template
    engine?

    View full-size slide

  5. You’re totally wrong!
    J

    View full-size slide












  6. Contextual content
    Manual escaping
    No isolation
    No separation of
    concerns

    View full-size slide

  7. Are you kidding me?

    View full-size slide

  8. Why and when using
    a template engine?

    View full-size slide

  9. What about existing
    template engines?

    View full-size slide

  10. 1. Installation & bootstrap
    2. Twig for web designers
    3. Twig for developers
    4. Frameworks & CMS

    View full-size slide

  11. 1. Installation & bootstrap

    View full-size slide

  12. Packages Installation
    https://github.com/fabpot/twig twig/twig

    View full-size slide

  13. Extension Installation
    $ cd ext/twig
    $ phpize
    $ ./configure
    $ make
    $ make install
    extension = twig.so

    View full-size slide

  14. 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'
    ));

    View full-size slide

  15. 2. Twig for web designers

    View full-size slide

  16. Concise templating
    syntax

    View full-size slide

  17. Concise Syntax
    {# ... comment something ... #}
    {% ... do something ... %}
    {{ ... display something ... }}

    View full-size slide

  18. PHP compilation
    and cache

    View full-size slide

  19. PHP Compilation and Cache
    {# just say hello #}
    Hello {{ name }}!

    View full-size slide

  20. 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

    View full-size slide

  21. 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

    View full-size slide

  22. Dumping a Variable
    {% set names = ['John', 'Tom', 'Paul'] %}
    {{ dump(names) }}

    View full-size slide

  23. Strict Variables Enabled
    Hello {{ nam }}!
    Twig_Error_Runtime: Variable "nam"
    does not exist in "hello.twig" at
    line 1

    View full-size slide

  24. Output escaping

    View full-size slide

  25. Automatic Output Escaping
    Hello {{ name }}!
    The variable is automatically escaped if it
    contains a string

    View full-size slide

  26. Escaping Strategies
    {{ name|raw }}
    {{ name|escape }}
    {{ name|e }}
    {{ name|e('html') }}
    {{ name|e('html_attr') }}
    {{ name|e('js') }}
    {{ name|e('css') }}
    {{ name|e('url') }}

    View full-size slide

  27. 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

    View full-size slide

  28. 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.

    View full-size slide

  29. Variables Abstraction
    echo $article['title'];
    echo $article->title;
    echo $article->title();
    echo $article->getTitle();
    echo $article->isTitle();

    View full-size slide

  30. Loops & Conditions

    View full-size slide

  31. Making Decisions
    {% if product.stock > 10 %}
    Available
    {% elseif product.stock > 0 %}
    Only {{ product.stock }} left!
    {% else %}
    Sold-out!
    {% endif %}

    View full-size slide

  32. Iterating Over a Collection

    {% for post in posts if post.active %}
    {{ post.title }}
    {{ post.body }}
    {% else %}
    No published posts yet.
    {% endfor %}

    View full-size slide

  33. 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

    View full-size slide

  34. Operators
    o  Math: +, -, /, *, **, %
    o  Logical: or, and, xor
    o  Concatenation: ~
    o  Comparison: <, >, <=, >=, ==
    o  Containment: in, not in

    View full-size slide

  35. Generating Contents
    Hello {{ random(['John', 'Tom', 'Paul']) }}!
    {% for i in range(0,10) %}
    {{ cycle(['odd', 'even'], i) }}

    {% endfor %}

    View full-size slide

  36. Built-in Functions
    o  attribute
    o  block
    o  constant
    o  cycle
    o  date
    o  parent
    o  random
    o  range

    View full-size slide

  37. 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') }}

    View full-size slide

  38. 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

    View full-size slide

  39. Whitespace Control

    View full-size slide

  40. Whitespace Control
    {% spaceless %}

    Hello {{ name }}!

    {% endspaceless %}
    Hello Hugo!

    View full-size slide

  41. Whitespace Control

    Hello {{- name }} !

    Hello Hugo !
    Value is trimmed on the left.

    View full-size slide

  42. Template inclusion

    View full-size slide

  43. Template Inclusion
    {%
    include "list.twig" with {
    "section": "blog",
    "posts": articles
    } only
    %}
    The included template is
    rendered with its own
    context.
    Isolation

    View full-size slide

  44. Template inheritance

    View full-size slide

  45. layout.twig
    {% block body %}
    {% endblock body %}
    {% block breadcrumb %} ... {% endblock %}
    blog.twig
    {% extends "layout.twig" %}
    {% block breadcrumb %}
    {{ parent() }} - Blog
    {% endblock breadcrumb %}
    {% block body %}
    Latest posts
    {% include "posts.twig" %}
    {% endblock body %}

    View full-size slide

  46. {% extends "layout.twig" %}
    {% block breadcrumb %}
    {{ parent() }} - Blog
    {% endblock breadcrumb %}
    {% block body %}
    Latest posts
    {% include "posts.twig" %}
    {% endblock body %}
    Template Inheritance
    Extend the parent
    template.
    Reuse the parent
    block default value.
    Fill the parent body
    block.

    View full-size slide

  47. Defining Macros
    {% macro input(name, value, type, size) %}
    name="{{ name }}"
    value="{{ value|e }}"
    size="{{ size|default(20) }}" />
    {% endmacro %}
    Default argument values are
    defined with the default filter

    View full-size slide

  48. Using Macros
    {% import "forms.html" as forms %}

    {{ forms.input('username') }}

    Macros are imported under
    a specific namespace
    The macro is called with the
    prefixed namespace

    View full-size slide

  49. Sandboxed
    environment

    View full-size slide

  50. 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'),
    );

    View full-size slide

  51. 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);

    View full-size slide

  52. Using the Sandbox Mode
    {% sandbox %}
    {% include 'users.html' %}
    {% endsandbox %}

    View full-size slide

  53. Enabling Sandbox Mode
    $sandbox = new Twig_Extension_Sandbox($policy, true);
    Sandbox mode is
    automatically enabled for all
    template

    View full-size slide

  54. Enabling Sandbox Mode
    {% block foo 'bar' %}
    {% include 'users.html' %}
    Twig_Sandbox_SecurityError:
    Tag "block" is not allowed in
    "hello.twig" at line 2

    View full-size slide

  55. 3. Twig for developers

    View full-size slide

  56. I want to customize
    Twig!

    View full-size slide

  57. Internal Architecture
    Lexer
    Parser
    Compiler
    Extension
    Extension
    Extensions
    Twig_Environment

    View full-size slide

  58. The Twig Lexer
    $template = <<Hello {{ name }}!
    EOT;
    $twig = new Twig_Environment();
    $stream = $twig->tokenize($template);

    View full-size slide

  59. The Twig Parser
    $ast = $twig->parse($stream);
    Twig_Node_Module(
    Twig_Node_Text(Hello )
    Twig_Node_Print(
    Twig_Node_Expression_Name(name)
    )
    Twig_Node_Text(!)
    )

    View full-size slide

  60. 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
    }

    View full-size slide

  61. Configuring Twig
    $twig = new Twig_Environment(array(
    'strict_variables' => true,
    'debug' => true,
    'cache' => '/path/to/cache',
    'base_template_class' => 'MyTemplate',
    'autoescape' => 'js',
    'optimizations' => -1,
    ));

    View full-size slide

  62. 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

    View full-size slide

  63. Twig extensions

    View full-size slide

  64. Extending the Twig Gramar
    o  Global variables
    o  Global functions
    o  Filters
    o  Tests
    o  Operators
    o  T
    ags

    View full-size slide

  65. 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

    View full-size slide

  66. 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();
    }

    View full-size slide

  67. Creating a Twig Extension
    class GravatarExtension extends \Twig_Extension
    {
    public function getName()
    {
    return 'gravatar';
    }
    }
    The abstract getName()
    method must be
    implemented.

    View full-size slide

  68. Registering the Extension
    $extension = new GravatarExtension();
    $twig = new Twig_Environment();
    $twig->addExtension($extension);
    Easy uh?!

    View full-size slide

  69. 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);
    }
    }

    View full-size slide

  70. Using the Global Function
    {% set uri = gravatar("[email protected]") %}

    Twig now knows this
    function.

    View full-size slide

  71. 4. Frameworks & CMS

    View full-size slide

  72. I want to use Twig
    with my favorite
    framework or CMS!

    View full-size slide

  73. Symfony 1.4
    sfTwigPlugin
    http://www.symfony-project.org/plugins/sfTwigPlugin

    View full-size slide

  74. Symfony2
    Built-in
    Forms themes
    Render tag
    Trans tag
    Routing functions
    Yaml filters

    View full-size slide

  75. Zend Framework 1
    Ano_ZFTwig
    Automatic escaping
    Layout inheritance
    Javascripts helpers
    Stylesheets helpers
    Routing helpers
    https://github.com/benjamindulau/Ano_ZFTwig

    View full-size slide

  76. Zend Framework 2
    Zfc_Twig
    Automatic escaping
    Layout inheritance
    Action rendering
    Forms helpers
    Events triggering
    https://github.com/ZF-Commons/ZfcTwig

    View full-size slide

  77. Drupal 7
    Drupal themes
    Switch/Case tag
    I18n capabilities
    Extra filters
    Extra functions

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

    View full-size slide

  78. WordPress
    o  http://wordpress.org/extend/plugins/ap-twig-bridge/
    o  http://inchoo.net/wordpress/twig-wordpress-part2/
    o  https://github.com/cordoval/Twig-for-WordPress
    o  https://github.com/maxcal/Twigpress

    View full-size slide

  79. Join us in Berlin!

    Symfony Live
    November 22nd – 23rd

    View full-size slide

  80. Community Feedbacks

    View full-size slide