SlideShare ist ein Scribd-Unternehmen logo
1 von 185
Downloaden Sie, um offline zu lesen
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

Weitere ähnliche Inhalte

Was ist angesagt?

jQuery Fundamentals
jQuery FundamentalsjQuery Fundamentals
jQuery FundamentalsGil Fink
 
Introduction to Bootstrap
Introduction to BootstrapIntroduction to Bootstrap
Introduction to BootstrapRon Reiter
 
Jquery Complete Presentation along with Javascript Basics
Jquery Complete Presentation along with Javascript BasicsJquery Complete Presentation along with Javascript Basics
Jquery Complete Presentation along with Javascript BasicsEPAM Systems
 
Web Technology Lab files with practical
Web Technology Lab  files with practicalWeb Technology Lab  files with practical
Web Technology Lab files with practicalNitesh Dubey
 
JavaScript: Variables and Functions
JavaScript: Variables and FunctionsJavaScript: Variables and Functions
JavaScript: Variables and FunctionsJussi Pohjolainen
 
Introduction to advanced python
Introduction to advanced pythonIntroduction to advanced python
Introduction to advanced pythonCharles-Axel Dein
 
Angular - Chapter 4 - Data and Event Handling
 Angular - Chapter 4 - Data and Event Handling Angular - Chapter 4 - Data and Event Handling
Angular - Chapter 4 - Data and Event HandlingWebStackAcademy
 
A Hacker's perspective on AEM applications security
A Hacker's perspective on AEM applications securityA Hacker's perspective on AEM applications security
A Hacker's perspective on AEM applications securityMikhail Egorov
 
Collections - Maps
Collections - Maps Collections - Maps
Collections - Maps Hitesh-Java
 
A Basic Django Introduction
A Basic Django IntroductionA Basic Django Introduction
A Basic Django IntroductionGanga Ram
 
AngularJS Directives
AngularJS DirectivesAngularJS Directives
AngularJS DirectivesEyal Vardi
 
Lets make a better react form
Lets make a better react formLets make a better react form
Lets make a better react formYao Nien Chung
 
What-is-Laravel-23-August-2017.pptx
What-is-Laravel-23-August-2017.pptxWhat-is-Laravel-23-August-2017.pptx
What-is-Laravel-23-August-2017.pptxAbhijeetKumar456867
 

Was ist angesagt? (20)

jQuery Fundamentals
jQuery FundamentalsjQuery Fundamentals
jQuery Fundamentals
 
Introduction to Bootstrap
Introduction to BootstrapIntroduction to Bootstrap
Introduction to Bootstrap
 
jQuery for beginners
jQuery for beginnersjQuery for beginners
jQuery for beginners
 
Jquery
JqueryJquery
Jquery
 
Jquery Complete Presentation along with Javascript Basics
Jquery Complete Presentation along with Javascript BasicsJquery Complete Presentation along with Javascript Basics
Jquery Complete Presentation along with Javascript Basics
 
Javascript essentials
Javascript essentialsJavascript essentials
Javascript essentials
 
Web Technology Lab files with practical
Web Technology Lab  files with practicalWeb Technology Lab  files with practical
Web Technology Lab files with practical
 
JavaScript: Variables and Functions
JavaScript: Variables and FunctionsJavaScript: Variables and Functions
JavaScript: Variables and Functions
 
Introduction to advanced python
Introduction to advanced pythonIntroduction to advanced python
Introduction to advanced python
 
Angular - Chapter 4 - Data and Event Handling
 Angular - Chapter 4 - Data and Event Handling Angular - Chapter 4 - Data and Event Handling
Angular - Chapter 4 - Data and Event Handling
 
A Hacker's perspective on AEM applications security
A Hacker's perspective on AEM applications securityA Hacker's perspective on AEM applications security
A Hacker's perspective on AEM applications security
 
PHP FUNCTIONS
PHP FUNCTIONSPHP FUNCTIONS
PHP FUNCTIONS
 
Collections - Maps
Collections - Maps Collections - Maps
Collections - Maps
 
A Basic Django Introduction
A Basic Django IntroductionA Basic Django Introduction
A Basic Django Introduction
 
AngularJS Directives
AngularJS DirectivesAngularJS Directives
AngularJS Directives
 
Lets make a better react form
Lets make a better react formLets make a better react form
Lets make a better react form
 
String in java
String in javaString in java
String in java
 
4.2 PHP Function
4.2 PHP Function4.2 PHP Function
4.2 PHP Function
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
 
What-is-Laravel-23-August-2017.pptx
What-is-Laravel-23-August-2017.pptxWhat-is-Laravel-23-August-2017.pptx
What-is-Laravel-23-August-2017.pptx
 

Andere mochten auch

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
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricksJavier 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
 

Andere mochten auch (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)
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
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
 

Ähnlich wie Twig tips and tricks

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
 

Ähnlich wie Twig tips and tricks (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
 

Mehr von 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
 

Mehr von 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
 

Kürzlich hochgeladen

Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintMahmoud Rabie
 
20230202 - Introduction to tis-py
20230202 - Introduction to tis-py20230202 - Introduction to tis-py
20230202 - Introduction to tis-pyJamie (Taka) Wang
 
Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)Commit University
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfDianaGray10
 
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationUsing IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationIES VE
 
Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1DianaGray10
 
OpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability AdventureOpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability AdventureEric D. Schabell
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxGDSC PJATK
 
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1DianaGray10
 
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online CollaborationCOMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online Collaborationbruanjhuli
 
Building AI-Driven Apps Using Semantic Kernel.pptx
Building AI-Driven Apps Using Semantic Kernel.pptxBuilding AI-Driven Apps Using Semantic Kernel.pptx
Building AI-Driven Apps Using Semantic Kernel.pptxUdaiappa Ramachandran
 
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...Aggregage
 
AI You Can Trust - Ensuring Success with Data Integrity Webinar
AI You Can Trust - Ensuring Success with Data Integrity WebinarAI You Can Trust - Ensuring Success with Data Integrity Webinar
AI You Can Trust - Ensuring Success with Data Integrity WebinarPrecisely
 
COMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a WebsiteCOMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a Websitedgelyza
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPathCommunity
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXTarek Kalaji
 
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...Will Schroeder
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxMatsuo Lab
 
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDEADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDELiveplex
 

Kürzlich hochgeladen (20)

Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership Blueprint
 
20230202 - Introduction to tis-py
20230202 - Introduction to tis-py20230202 - Introduction to tis-py
20230202 - Introduction to tis-py
 
Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)
 
201610817 - edge part1
201610817 - edge part1201610817 - edge part1
201610817 - edge part1
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
 
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationUsing IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
 
Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1
 
OpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability AdventureOpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability Adventure
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptx
 
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
 
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online CollaborationCOMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
 
Building AI-Driven Apps Using Semantic Kernel.pptx
Building AI-Driven Apps Using Semantic Kernel.pptxBuilding AI-Driven Apps Using Semantic Kernel.pptx
Building AI-Driven Apps Using Semantic Kernel.pptx
 
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
 
AI You Can Trust - Ensuring Success with Data Integrity Webinar
AI You Can Trust - Ensuring Success with Data Integrity WebinarAI You Can Trust - Ensuring Success with Data Integrity Webinar
AI You Can Trust - Ensuring Success with Data Integrity Webinar
 
COMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a WebsiteCOMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a Website
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation Developers
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBX
 
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptx
 
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDEADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
 

Twig tips and tricks

  • 1. TWIG tips & tricks SUNSHINEPHP JAVIER EGUILUZ FEBRUARY 8TH 2013
  • 2. Thanks to sponsors and organizers Adam Pablo Culp Godel
  • 3. About me Javier Eguiluz I’m a programmer and trainer from Spain.
  • 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