SlideShare uma empresa Scribd logo
1 de 185
Baixar para ler offline
TWIG
            tips & tricks
SUNSHINEPHP             JAVIER EGUILUZ
FEBRUARY 8TH 2013
Thanks to sponsors and organizers




      Adam            Pablo
      Culp            Godel
About me


           Javier Eguiluz
           I’m a programmer
           and trainer from Spain.
I
SYMFONY
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!
I
TWIG
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                11

Authors                 35                 5

Versions 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 }}" %}


{% include
template_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 WITH
CAUTION
{% 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 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'),
       // ...
    );
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'),
       // ...
    );
«sort» filter uses «asort» function
new 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»
// asort
A1, a0, a10, a2

// natcasesort
a0, A1, a2, a10
1. Define a new Twig extension
class MyCoreExtension
      extends Twig_Extension_Core {
   // ...
}
2. Define the new «sort» filter
class MyCoreExtension extends Twig_Extension_Core {
    public function getFilters() {
      // ...
    }
}
2. Define the new «sort» filter
class MyCoreExtension extends Twig_Extension_Core {
   public function getFilters() {
        return array_merge(
           parent::getFilters(),
           array( ... )
        );
    }
}
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;
    }
}
3. Register the new extension
$twig = new Twig_Environment( ... );

$twig->addExtension(
   new MyCoreExtension()
);
This is now
                   natcasesort


{% for i in array|sort %}
  {# ... #}
{% endfor %}
DYNAMIC
FUNCTIONS
WordPress template tags
the_ID()
the_title()
the_time()
the_content()
the_category()
the_shortlink()
WordPress template tags
the_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 asterisks
function 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 tags
next_image_link()
next_post_link()
next_posts_link()
previous_image_link()
previous_post_link()
previous_posts_link()
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()
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()
USE WITH
CAUTION
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() }}


Reference
http://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
{% 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
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
1. Create a new token parser
class 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» 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());
    }
}
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());
    }
}
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(');')
      ;
    }
}
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(');')
      ;
    }
}
The compiled PHP template
// line 3
echo file_get_contents("simple.twig");
// ...
                  {% source 'simple.twig' %}
// line 5
echo 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 JavaScript
TWIG
       TWIG
Twig.js = Twig inside JavaScript
TWIG
       TWIG
HTML                      HTML
Twig.js = Twig inside JavaScript
TWIG
       TWIG
HTML                      HTML
          JS

JS
Twig.js = Twig inside JavaScript
TWIG
       TWIG
HTML                      HTML
          JS

JS            TWIG


TWIG
«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 property
Offer 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 error
Offer data
----------
Title: {{ offer.title }}
Description: {{ offer.description }}
Commission: {{ offer.commission }}
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.
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 BASE
TEMPLATE
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 class
class __TwigTemplate_09f...2f2
      extends Twig_Template
{
   // ...
}
lib/Twig/Template.php
abstract class Twig_Template {
     public function render(array $context)
     {
         // ...
     }
    // ...

}
lib/Twig/Template.php
abstract 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.yml
twig:
  base_template_class: "ACMEMyTwigTemplate"
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)
        );
    }
}
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));
  }
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
  structure

extends + include = embed
reuse a fixed   reuse
  structure       contents

extends + include = embed
reuse a fixed    reuse
  structure        contents

extends + 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

Mais conteúdo relacionado

Mais procurados

Spring Data JPA from 0-100 in 60 minutes
Spring Data JPA from 0-100 in 60 minutesSpring Data JPA from 0-100 in 60 minutes
Spring Data JPA from 0-100 in 60 minutesVMware Tanzu
 
JavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UXJavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UXJWORKS powered by Ordina
 
Spring Framework - Core
Spring Framework - CoreSpring Framework - Core
Spring Framework - CoreDzmitry Naskou
 
Oops concept in c++ unit 3 -topic 4
Oops concept in c++ unit 3 -topic 4Oops concept in c++ unit 3 -topic 4
Oops concept in c++ unit 3 -topic 4MOHIT TOMAR
 
Spring boot
Spring bootSpring boot
Spring bootsdeeg
 
Java access modifiers
Java access modifiersJava access modifiers
Java access modifiersKhaled Adnan
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented ProgrammingScott Wlaschin
 
Advanced JavaScript
Advanced JavaScriptAdvanced JavaScript
Advanced JavaScriptNascenia IT
 
ES6 presentation
ES6 presentationES6 presentation
ES6 presentationritika1
 
L21 io streams
L21 io streamsL21 io streams
L21 io streamsteach4uin
 
PUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBootPUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBootJosué Neis
 
Implicit parameters, when to use them (or not)!
Implicit parameters, when to use them (or not)!Implicit parameters, when to use them (or not)!
Implicit parameters, when to use them (or not)!Julien Truffaut
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsPeter Friese
 
JUnit & Mockito, first steps
JUnit & Mockito, first stepsJUnit & Mockito, first steps
JUnit & Mockito, first stepsRenato Primavera
 
Chapter 02: Classes Objects and Methods Java by Tushar B Kute
Chapter 02: Classes Objects and Methods Java by Tushar B KuteChapter 02: Classes Objects and Methods Java by Tushar B Kute
Chapter 02: Classes Objects and Methods Java by Tushar B KuteTushar B Kute
 
Introduction to class in java
Introduction to class in javaIntroduction to class in java
Introduction to class in javakamal kotecha
 
Extending Java EE with CDI and JBoss Forge
Extending Java EE with CDI and JBoss ForgeExtending Java EE with CDI and JBoss Forge
Extending Java EE with CDI and JBoss ForgeAntoine Sabot-Durand
 

Mais procurados (20)

Spring Data JPA from 0-100 in 60 minutes
Spring Data JPA from 0-100 in 60 minutesSpring Data JPA from 0-100 in 60 minutes
Spring Data JPA from 0-100 in 60 minutes
 
JavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UXJavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UX
 
Java program structure
Java program structure Java program structure
Java program structure
 
Spring Framework - Core
Spring Framework - CoreSpring Framework - Core
Spring Framework - Core
 
Oops concept in c++ unit 3 -topic 4
Oops concept in c++ unit 3 -topic 4Oops concept in c++ unit 3 -topic 4
Oops concept in c++ unit 3 -topic 4
 
Java String
Java StringJava String
Java String
 
Spring boot
Spring bootSpring boot
Spring boot
 
Java access modifiers
Java access modifiersJava access modifiers
Java access modifiers
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented Programming
 
Advanced JavaScript
Advanced JavaScriptAdvanced JavaScript
Advanced JavaScript
 
ES6 presentation
ES6 presentationES6 presentation
ES6 presentation
 
L21 io streams
L21 io streamsL21 io streams
L21 io streams
 
PUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBootPUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBoot
 
Implicit parameters, when to use them (or not)!
Implicit parameters, when to use them (or not)!Implicit parameters, when to use them (or not)!
Implicit parameters, when to use them (or not)!
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
JUnit & Mockito, first steps
JUnit & Mockito, first stepsJUnit & Mockito, first steps
JUnit & Mockito, first steps
 
Chapter 02: Classes Objects and Methods Java by Tushar B Kute
Chapter 02: Classes Objects and Methods Java by Tushar B KuteChapter 02: Classes Objects and Methods Java by Tushar B Kute
Chapter 02: Classes Objects and Methods Java by Tushar B Kute
 
Introduction to class in java
Introduction to class in javaIntroduction to class in java
Introduction to class in java
 
Extending Java EE with CDI and JBoss Forge
Extending Java EE with CDI and JBoss ForgeExtending Java EE with CDI and JBoss Forge
Extending Java EE with CDI and JBoss Forge
 
Inner class
Inner classInner class
Inner class
 

Destaque

Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Javier Eguiluz
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)Javier Eguiluz
 
Thyristor Commutation Techniques
Thyristor Commutation TechniquesThyristor Commutation Techniques
Thyristor Commutation Techniquesprathik
 
The community Development Handbook
The community Development HandbookThe community Development Handbook
The community Development Handbookforeman
 
Anatomy Lect 3 Head & Neck
Anatomy Lect 3 Head & NeckAnatomy Lect 3 Head & Neck
Anatomy Lect 3 Head & NeckMiami Dade
 
eBay 2009 Analyst Day
eBay 2009 Analyst DayeBay 2009 Analyst Day
eBay 2009 Analyst DayPhil Wolff
 
fluid, electrolytes, acid base balance
fluid, electrolytes, acid base balancefluid, electrolytes, acid base balance
fluid, electrolytes, acid base balancetwiggypiggy
 
Legal aspects of nursing
Legal aspects of nursingLegal aspects of nursing
Legal aspects of nursingxtrm nurse
 
localization of stroke, CVS, stroke, for post graduates
localization of stroke, CVS, stroke,  for post graduates localization of stroke, CVS, stroke,  for post graduates
localization of stroke, CVS, stroke, for post graduates Kurian Joseph
 
The Ultimate Guide to Creating Visually Appealing Content
The Ultimate Guide to Creating Visually Appealing ContentThe Ultimate Guide to Creating Visually Appealing Content
The Ultimate Guide to Creating Visually Appealing ContentNeil Patel
 
Dear NSA, let me take care of your slides.
Dear NSA, let me take care of your slides.Dear NSA, let me take care of your slides.
Dear NSA, let me take care of your slides.Emiland
 
What I Carry: 10 Tools for Success
What I Carry: 10 Tools for SuccessWhat I Carry: 10 Tools for Success
What I Carry: 10 Tools for SuccessJonathon Colman
 
Breaking Free from Bootstrap: Custom Responsive Grids with Sass Susy
Breaking Free from Bootstrap: Custom Responsive Grids with Sass SusyBreaking Free from Bootstrap: Custom Responsive Grids with Sass Susy
Breaking Free from Bootstrap: Custom Responsive Grids with Sass SusyJames Steinbach
 
Thrivability: A Collaborative Sketch
Thrivability: A Collaborative SketchThrivability: A Collaborative Sketch
Thrivability: A Collaborative SketchJean Russell
 
What Makes Great Infographics
What Makes Great InfographicsWhat Makes Great Infographics
What Makes Great InfographicsSlideShare
 
Masters of SlideShare
Masters of SlideShareMasters of SlideShare
Masters of SlideShareKapost
 
STOP! VIEW THIS! 10-Step Checklist When Uploading to Slideshare
STOP! VIEW THIS! 10-Step Checklist When Uploading to SlideshareSTOP! VIEW THIS! 10-Step Checklist When Uploading to Slideshare
STOP! VIEW THIS! 10-Step Checklist When Uploading to SlideshareEmpowered Presentations
 

Destaque (20)

Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
 
Thyristor Commutation Techniques
Thyristor Commutation TechniquesThyristor Commutation Techniques
Thyristor Commutation Techniques
 
The community Development Handbook
The community Development HandbookThe community Development Handbook
The community Development Handbook
 
Anatomy Lect 3 Head & Neck
Anatomy Lect 3 Head & NeckAnatomy Lect 3 Head & Neck
Anatomy Lect 3 Head & Neck
 
eBay 2009 Analyst Day
eBay 2009 Analyst DayeBay 2009 Analyst Day
eBay 2009 Analyst Day
 
Intro to Documenting
Intro to DocumentingIntro to Documenting
Intro to Documenting
 
fluid, electrolytes, acid base balance
fluid, electrolytes, acid base balancefluid, electrolytes, acid base balance
fluid, electrolytes, acid base balance
 
Legal aspects of nursing
Legal aspects of nursingLegal aspects of nursing
Legal aspects of nursing
 
localization of stroke, CVS, stroke, for post graduates
localization of stroke, CVS, stroke,  for post graduates localization of stroke, CVS, stroke,  for post graduates
localization of stroke, CVS, stroke, for post graduates
 
BUSINESS QUIZ -Round 1
 BUSINESS QUIZ -Round 1 BUSINESS QUIZ -Round 1
BUSINESS QUIZ -Round 1
 
The Ultimate Guide to Creating Visually Appealing Content
The Ultimate Guide to Creating Visually Appealing ContentThe Ultimate Guide to Creating Visually Appealing Content
The Ultimate Guide to Creating Visually Appealing Content
 
Dear NSA, let me take care of your slides.
Dear NSA, let me take care of your slides.Dear NSA, let me take care of your slides.
Dear NSA, let me take care of your slides.
 
What I Carry: 10 Tools for Success
What I Carry: 10 Tools for SuccessWhat I Carry: 10 Tools for Success
What I Carry: 10 Tools for Success
 
Breaking Free from Bootstrap: Custom Responsive Grids with Sass Susy
Breaking Free from Bootstrap: Custom Responsive Grids with Sass SusyBreaking Free from Bootstrap: Custom Responsive Grids with Sass Susy
Breaking Free from Bootstrap: Custom Responsive Grids with Sass Susy
 
Thrivability: A Collaborative Sketch
Thrivability: A Collaborative SketchThrivability: A Collaborative Sketch
Thrivability: A Collaborative Sketch
 
What Makes Great Infographics
What Makes Great InfographicsWhat Makes Great Infographics
What Makes Great Infographics
 
Masters of SlideShare
Masters of SlideShareMasters of SlideShare
Masters of SlideShare
 
STOP! VIEW THIS! 10-Step Checklist When Uploading to Slideshare
STOP! VIEW THIS! 10-Step Checklist When Uploading to SlideshareSTOP! VIEW THIS! 10-Step Checklist When Uploading to Slideshare
STOP! VIEW THIS! 10-Step Checklist When Uploading to Slideshare
 
You Suck At PowerPoint!
You Suck At PowerPoint!You Suck At PowerPoint!
You Suck At PowerPoint!
 

Semelhante a Twig tips & tricks for advanced features, defensive templates and best practices

Twig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHPTwig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHPFabien Potencier
 
The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196Mahmoud Samir Fayed
 
Twig Brief, Tips&Tricks
Twig Brief, Tips&TricksTwig Brief, Tips&Tricks
Twig Brief, Tips&TricksAndrei Burian
 
The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189Mahmoud Samir Fayed
 
MTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new APIMTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new APISix Apart KK
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy codeShriKant Vashishtha
 
Thymeleaf and Spring Controllers.ppt
Thymeleaf and Spring Controllers.pptThymeleaf and Spring Controllers.ppt
Thymeleaf and Spring Controllers.pptPatiento Del Mar
 
Arrays and function basic c programming notes
Arrays and function basic c programming notesArrays and function basic c programming notes
Arrays and function basic c programming notesGOKULKANNANMMECLECTC
 
Mastering Kotlin Standard Library
Mastering Kotlin Standard LibraryMastering Kotlin Standard Library
Mastering Kotlin Standard LibraryNelson Glauber Leal
 
The Ring programming language version 1.5.1 book - Part 12 of 180
The Ring programming language version 1.5.1 book - Part 12 of 180The Ring programming language version 1.5.1 book - Part 12 of 180
The Ring programming language version 1.5.1 book - Part 12 of 180Mahmoud Samir Fayed
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgetsvelveeta_512
 
Elegant Solutions for Everyday Python Problems Pycon 2018 - Nina Zakharenko
Elegant Solutions for Everyday Python Problems Pycon 2018 - Nina ZakharenkoElegant Solutions for Everyday Python Problems Pycon 2018 - Nina Zakharenko
Elegant Solutions for Everyday Python Problems Pycon 2018 - Nina ZakharenkoNina Zakharenko
 
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...WordCamp Sydney
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Shinya Ohyanagi
 

Semelhante a Twig tips & tricks for advanced features, defensive templates and best practices (20)

Twig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHPTwig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHP
 
The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196
 
Twig Brief, Tips&Tricks
Twig Brief, Tips&TricksTwig Brief, Tips&Tricks
Twig Brief, Tips&Tricks
 
The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189
 
MTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new APIMTDDC 2010.2.5 Tokyo - Brand new API
MTDDC 2010.2.5 Tokyo - Brand new API
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
 
Thymeleaf and Spring Controllers.ppt
Thymeleaf and Spring Controllers.pptThymeleaf and Spring Controllers.ppt
Thymeleaf and Spring Controllers.ppt
 
Arrays and function basic c programming notes
Arrays and function basic c programming notesArrays and function basic c programming notes
Arrays and function basic c programming notes
 
Mastering Kotlin Standard Library
Mastering Kotlin Standard LibraryMastering Kotlin Standard Library
Mastering Kotlin Standard Library
 
Intro to The PHP SPL
Intro to The PHP SPLIntro to The PHP SPL
Intro to The PHP SPL
 
The Ring programming language version 1.5.1 book - Part 12 of 180
The Ring programming language version 1.5.1 book - Part 12 of 180The Ring programming language version 1.5.1 book - Part 12 of 180
The Ring programming language version 1.5.1 book - Part 12 of 180
 
.NET F# Events
.NET F# Events.NET F# Events
.NET F# Events
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
 
Elegant Solutions for Everyday Python Problems Pycon 2018 - Nina Zakharenko
Elegant Solutions for Everyday Python Problems Pycon 2018 - Nina ZakharenkoElegant Solutions for Everyday Python Problems Pycon 2018 - Nina Zakharenko
Elegant Solutions for Everyday Python Problems Pycon 2018 - Nina Zakharenko
 
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...
Stop Hacking WordPress, Start Working with it - Charly Leetham - WordCamp Syd...
 
25-functions.ppt
25-functions.ppt25-functions.ppt
25-functions.ppt
 
Ruby tricks2
Ruby tricks2Ruby tricks2
Ruby tricks2
 
Diifeerences In C#
Diifeerences In C#Diifeerences In C#
Diifeerences In C#
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 

Mais de Javier Eguiluz

deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonydeSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonyJavier Eguiluz
 
Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Javier Eguiluz
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPJavier Eguiluz
 
Twig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadasTwig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadasJavier Eguiluz
 
Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Javier Eguiluz
 
Desymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoDesymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoJavier Eguiluz
 
Desymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendDesymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendJavier Eguiluz
 
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosJavier Eguiluz
 
Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Javier Eguiluz
 
Symfony2, Jornadas Symfony
Symfony2, Jornadas SymfonySymfony2, Jornadas Symfony
Symfony2, Jornadas SymfonyJavier Eguiluz
 
Curso Symfony - Anexos
Curso Symfony - AnexosCurso Symfony - Anexos
Curso Symfony - AnexosJavier Eguiluz
 
Curso Symfony - Clase 5
Curso Symfony - Clase 5Curso Symfony - Clase 5
Curso Symfony - Clase 5Javier Eguiluz
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4Javier Eguiluz
 
Curso Symfony - Clase 3
Curso Symfony - Clase 3Curso Symfony - Clase 3
Curso Symfony - Clase 3Javier Eguiluz
 
Curso Symfony - Clase 2
Curso Symfony - Clase 2Curso Symfony - Clase 2
Curso Symfony - Clase 2Javier Eguiluz
 
Curso Symfony - Clase 1
Curso Symfony - Clase 1Curso Symfony - Clase 1
Curso Symfony - Clase 1Javier Eguiluz
 

Mais de Javier Eguiluz (20)

deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonydeSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
 
Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8
 
Silex al límite
Silex al límiteSilex al límite
Silex al límite
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHP
 
Twig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadasTwig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadas
 
Wallpaper Notifier
Wallpaper NotifierWallpaper Notifier
Wallpaper Notifier
 
Backend (sf2Vigo)
Backend (sf2Vigo)Backend (sf2Vigo)
Backend (sf2Vigo)
 
Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)
 
Desymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoDesymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseño
 
Desymfony 2011 - Twig
Desymfony 2011 - TwigDesymfony 2011 - Twig
Desymfony 2011 - Twig
 
Desymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendDesymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: Backend
 
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
 
Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2
 
Symfony2, Jornadas Symfony
Symfony2, Jornadas SymfonySymfony2, Jornadas Symfony
Symfony2, Jornadas Symfony
 
Curso Symfony - Anexos
Curso Symfony - AnexosCurso Symfony - Anexos
Curso Symfony - Anexos
 
Curso Symfony - Clase 5
Curso Symfony - Clase 5Curso Symfony - Clase 5
Curso Symfony - Clase 5
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4
 
Curso Symfony - Clase 3
Curso Symfony - Clase 3Curso Symfony - Clase 3
Curso Symfony - Clase 3
 
Curso Symfony - Clase 2
Curso Symfony - Clase 2Curso Symfony - Clase 2
Curso Symfony - Clase 2
 
Curso Symfony - Clase 1
Curso Symfony - Clase 1Curso Symfony - Clase 1
Curso Symfony - Clase 1
 

Último

Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditSkynet Technologies
 
All These Sophisticated Attacks, Can We Really Detect Them - PDF
All These Sophisticated Attacks, Can We Really Detect Them - PDFAll These Sophisticated Attacks, Can We Really Detect Them - PDF
All These Sophisticated Attacks, Can We Really Detect Them - PDFMichael Gough
 
Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Karmanjay Verma
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...Karmanjay Verma
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 
Kuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorialKuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorialJoão Esperancinha
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkPixlogix Infotech
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)Mark Simos
 
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...panagenda
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
WomenInAutomation2024: AI and Automation for eveyone
WomenInAutomation2024: AI and Automation for eveyoneWomenInAutomation2024: AI and Automation for eveyone
WomenInAutomation2024: AI and Automation for eveyoneUiPathCommunity
 
Accelerating Enterprise Software Engineering with Platformless
Accelerating Enterprise Software Engineering with PlatformlessAccelerating Enterprise Software Engineering with Platformless
Accelerating Enterprise Software Engineering with PlatformlessWSO2
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesThousandEyes
 
Français Patch Tuesday - Avril
Français Patch Tuesday - AvrilFrançais Patch Tuesday - Avril
Français Patch Tuesday - AvrilIvanti
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Mark Goldstein
 
Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Kaya Weers
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024TopCSSGallery
 

Último (20)

Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance Audit
 
All These Sophisticated Attacks, Can We Really Detect Them - PDF
All These Sophisticated Attacks, Can We Really Detect Them - PDFAll These Sophisticated Attacks, Can We Really Detect Them - PDF
All These Sophisticated Attacks, Can We Really Detect Them - PDF
 
Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 
Kuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorialKuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorial
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App Framework
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
 
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
WomenInAutomation2024: AI and Automation for eveyone
WomenInAutomation2024: AI and Automation for eveyoneWomenInAutomation2024: AI and Automation for eveyone
WomenInAutomation2024: AI and Automation for eveyone
 
Accelerating Enterprise Software Engineering with Platformless
Accelerating Enterprise Software Engineering with PlatformlessAccelerating Enterprise Software Engineering with Platformless
Accelerating Enterprise Software Engineering with Platformless
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
 
Français Patch Tuesday - Avril
Français Patch Tuesday - AvrilFrançais Patch Tuesday - Avril
Français Patch Tuesday - Avril
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
 
Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024
 

Twig tips & tricks for advanced features, defensive templates and best practices

  • 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.
  • 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!
  • 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
  • 13. Agenda • Tips and tricks about Twig. • Advanced features. • Defensive template design. • Best practices. • New and noteworthy Twig features.
  • 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
  • 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 %}
  • 56. WordPress template tags the_ID() class Post { $id = ... the_title() $title = ... the_time() $time = ... the_content() $content = ... $category = ... the_category() $shortlink = ... the_shortlink() // ... }
  • 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>') ) { ... }
  • 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()
  • 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
  • 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");
  • 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(...) ;
  • 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');
  • 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>
  • 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 );
  • 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>
  • 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
  • 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
  • 185. Contact me • javier.eguiluz@gmail.com • linkedin.com/in/javiereguiluz • twitter.com/javiereguiluz • github.com/javiereguiluz