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

Más contenido relacionado

Was ist angesagt?

Enterprise Tic-Tac-Toe
Enterprise Tic-Tac-ToeEnterprise Tic-Tac-Toe
Enterprise Tic-Tac-ToeScott Wlaschin
 
Refactoring Functional Type Classes
Refactoring Functional Type ClassesRefactoring Functional Type Classes
Refactoring Functional Type ClassesJohn De Goes
 
ACL in CodeIgniter
ACL in CodeIgniterACL in CodeIgniter
ACL in CodeIgnitermirahman
 
좌충우돌 ORM 개발기 | Devon 2012
좌충우돌 ORM 개발기 | Devon 2012좌충우돌 ORM 개발기 | Devon 2012
좌충우돌 ORM 개발기 | Devon 2012Daum DNA
 
Collections - Lists, Sets
Collections - Lists, Sets Collections - Lists, Sets
Collections - Lists, Sets Hitesh-Java
 
C# for C++ Programmers
C# for C++ ProgrammersC# for C++ Programmers
C# for C++ Programmersrussellgmorley
 
Functional Programming with Immutable Data Structures
Functional Programming with Immutable Data StructuresFunctional Programming with Immutable Data Structures
Functional Programming with Immutable Data Structureselliando dias
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKirill Rozov
 
Symfony Messenger (Symfony Live San Francisco)
Symfony Messenger (Symfony Live San Francisco)Symfony Messenger (Symfony Live San Francisco)
Symfony Messenger (Symfony Live San Francisco)Samuel ROZE
 
Alfresco勉強会#28 メタデータテンプレート
Alfresco勉強会#28 メタデータテンプレートAlfresco勉強会#28 メタデータテンプレート
Alfresco勉強会#28 メタデータテンプレートJun Terashita
 
Python decorators
Python decoratorsPython decorators
Python decoratorsAlex Su
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix itRafael Dohms
 
Java Collection framework
Java Collection frameworkJava Collection framework
Java Collection frameworkankitgarg_er
 
Advanced Javascript
Advanced JavascriptAdvanced Javascript
Advanced JavascriptAdieu
 
Xe 구조에 대한 이해
Xe 구조에 대한 이해Xe 구조에 대한 이해
Xe 구조에 대한 이해Dong Hyun Kim
 

Was ist angesagt? (20)

Enterprise Tic-Tac-Toe
Enterprise Tic-Tac-ToeEnterprise Tic-Tac-Toe
Enterprise Tic-Tac-Toe
 
Refactoring Functional Type Classes
Refactoring Functional Type ClassesRefactoring Functional Type Classes
Refactoring Functional Type Classes
 
PHP 5 Magic Methods
PHP 5 Magic MethodsPHP 5 Magic Methods
PHP 5 Magic Methods
 
Optional in Java 8
Optional in Java 8Optional in Java 8
Optional in Java 8
 
ACL in CodeIgniter
ACL in CodeIgniterACL in CodeIgniter
ACL in CodeIgniter
 
좌충우돌 ORM 개발기 | Devon 2012
좌충우돌 ORM 개발기 | Devon 2012좌충우돌 ORM 개발기 | Devon 2012
좌충우돌 ORM 개발기 | Devon 2012
 
07 2 ricorsione
07 2 ricorsione07 2 ricorsione
07 2 ricorsione
 
Collections - Lists, Sets
Collections - Lists, Sets Collections - Lists, Sets
Collections - Lists, Sets
 
C# for C++ Programmers
C# for C++ ProgrammersC# for C++ Programmers
C# for C++ Programmers
 
Functional Programming with Immutable Data Structures
Functional Programming with Immutable Data StructuresFunctional Programming with Immutable Data Structures
Functional Programming with Immutable Data Structures
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is coming
 
Symfony Messenger (Symfony Live San Francisco)
Symfony Messenger (Symfony Live San Francisco)Symfony Messenger (Symfony Live San Francisco)
Symfony Messenger (Symfony Live San Francisco)
 
Alfresco勉強会#28 メタデータテンプレート
Alfresco勉強会#28 メタデータテンプレートAlfresco勉強会#28 メタデータテンプレート
Alfresco勉強会#28 メタデータテンプレート
 
Python decorators
Python decoratorsPython decorators
Python decorators
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix it
 
Namespaces
NamespacesNamespaces
Namespaces
 
Java collections notes
Java collections notesJava collections notes
Java collections notes
 
Java Collection framework
Java Collection frameworkJava Collection framework
Java Collection framework
 
Advanced Javascript
Advanced JavascriptAdvanced Javascript
Advanced Javascript
 
Xe 구조에 대한 이해
Xe 구조에 대한 이해Xe 구조에 대한 이해
Xe 구조에 대한 이해
 

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
 
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
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)James Titcumb
 

Ä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
 
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
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
 

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
 

Último

TrustArc Webinar - How to Live in a Post Third-Party Cookie World
TrustArc Webinar - How to Live in a Post Third-Party Cookie WorldTrustArc Webinar - How to Live in a Post Third-Party Cookie World
TrustArc Webinar - How to Live in a Post Third-Party Cookie WorldTrustArc
 
Top 10 Squarespace Development Companies
Top 10 Squarespace Development CompaniesTop 10 Squarespace Development Companies
Top 10 Squarespace Development CompaniesTopCSSGallery
 
Novo Nordisk's journey in developing an open-source application on Neo4j
Novo Nordisk's journey in developing an open-source application on Neo4jNovo Nordisk's journey in developing an open-source application on Neo4j
Novo Nordisk's journey in developing an open-source application on Neo4jNeo4j
 
IT Service Management (ITSM) Best Practices for Advanced Computing
IT Service Management (ITSM) Best Practices for Advanced ComputingIT Service Management (ITSM) Best Practices for Advanced Computing
IT Service Management (ITSM) Best Practices for Advanced ComputingMAGNIntelligence
 
How to release an Open Source Dataweave Library
How to release an Open Source Dataweave LibraryHow to release an Open Source Dataweave Library
How to release an Open Source Dataweave Libraryshyamraj55
 
The Importance of Indoor Air Quality (English)
The Importance of Indoor Air Quality (English)The Importance of Indoor Air Quality (English)
The Importance of Indoor Air Quality (English)IES VE
 
LF Energy Webinar - Unveiling OpenEEMeter 4.0
LF Energy Webinar - Unveiling OpenEEMeter 4.0LF Energy Webinar - Unveiling OpenEEMeter 4.0
LF Energy Webinar - Unveiling OpenEEMeter 4.0DanBrown980551
 
Emil Eifrem at GraphSummit Copenhagen 2024 - The Art of the Possible.pptx
Emil Eifrem at GraphSummit Copenhagen 2024 - The Art of the Possible.pptxEmil Eifrem at GraphSummit Copenhagen 2024 - The Art of the Possible.pptx
Emil Eifrem at GraphSummit Copenhagen 2024 - The Art of the Possible.pptxNeo4j
 
Automation Ops Series: Session 2 - Governance for UiPath projects
Automation Ops Series: Session 2 - Governance for UiPath projectsAutomation Ops Series: Session 2 - Governance for UiPath projects
Automation Ops Series: Session 2 - Governance for UiPath projectsDianaGray10
 
UiPath Studio Web workshop series - Day 2
UiPath Studio Web workshop series - Day 2UiPath Studio Web workshop series - Day 2
UiPath Studio Web workshop series - Day 2DianaGray10
 
Oracle Database 23c Security New Features.pptx
Oracle Database 23c Security New Features.pptxOracle Database 23c Security New Features.pptx
Oracle Database 23c Security New Features.pptxSatishbabu Gunukula
 
2024.03.12 Cost drivers of cultivated meat production.pdf
2024.03.12 Cost drivers of cultivated meat production.pdf2024.03.12 Cost drivers of cultivated meat production.pdf
2024.03.12 Cost drivers of cultivated meat production.pdfThe Good Food Institute
 
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024Alkin Tezuysal
 
UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4DianaGray10
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfCheryl Hung
 
Scenario Library et REX Discover industry- and role- based scenarios
Scenario Library et REX Discover industry- and role- based scenariosScenario Library et REX Discover industry- and role- based scenarios
Scenario Library et REX Discover industry- and role- based scenariosErol GIRAUDY
 
Stobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through TokenizationStobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through TokenizationStobox
 
Extra-120324-Visite-Entreprise-icare.pdf
Extra-120324-Visite-Entreprise-icare.pdfExtra-120324-Visite-Entreprise-icare.pdf
Extra-120324-Visite-Entreprise-icare.pdfInfopole1
 
Outage Analysis: March 5th/6th 2024 Meta, Comcast, and LinkedIn
Outage Analysis: March 5th/6th 2024 Meta, Comcast, and LinkedInOutage Analysis: March 5th/6th 2024 Meta, Comcast, and LinkedIn
Outage Analysis: March 5th/6th 2024 Meta, Comcast, and LinkedInThousandEyes
 
Planetek Italia Srl - Corporate Profile Brochure
Planetek Italia Srl - Corporate Profile BrochurePlanetek Italia Srl - Corporate Profile Brochure
Planetek Italia Srl - Corporate Profile BrochurePlanetek Italia Srl
 

Último (20)

TrustArc Webinar - How to Live in a Post Third-Party Cookie World
TrustArc Webinar - How to Live in a Post Third-Party Cookie WorldTrustArc Webinar - How to Live in a Post Third-Party Cookie World
TrustArc Webinar - How to Live in a Post Third-Party Cookie World
 
Top 10 Squarespace Development Companies
Top 10 Squarespace Development CompaniesTop 10 Squarespace Development Companies
Top 10 Squarespace Development Companies
 
Novo Nordisk's journey in developing an open-source application on Neo4j
Novo Nordisk's journey in developing an open-source application on Neo4jNovo Nordisk's journey in developing an open-source application on Neo4j
Novo Nordisk's journey in developing an open-source application on Neo4j
 
IT Service Management (ITSM) Best Practices for Advanced Computing
IT Service Management (ITSM) Best Practices for Advanced ComputingIT Service Management (ITSM) Best Practices for Advanced Computing
IT Service Management (ITSM) Best Practices for Advanced Computing
 
How to release an Open Source Dataweave Library
How to release an Open Source Dataweave LibraryHow to release an Open Source Dataweave Library
How to release an Open Source Dataweave Library
 
The Importance of Indoor Air Quality (English)
The Importance of Indoor Air Quality (English)The Importance of Indoor Air Quality (English)
The Importance of Indoor Air Quality (English)
 
LF Energy Webinar - Unveiling OpenEEMeter 4.0
LF Energy Webinar - Unveiling OpenEEMeter 4.0LF Energy Webinar - Unveiling OpenEEMeter 4.0
LF Energy Webinar - Unveiling OpenEEMeter 4.0
 
Emil Eifrem at GraphSummit Copenhagen 2024 - The Art of the Possible.pptx
Emil Eifrem at GraphSummit Copenhagen 2024 - The Art of the Possible.pptxEmil Eifrem at GraphSummit Copenhagen 2024 - The Art of the Possible.pptx
Emil Eifrem at GraphSummit Copenhagen 2024 - The Art of the Possible.pptx
 
Automation Ops Series: Session 2 - Governance for UiPath projects
Automation Ops Series: Session 2 - Governance for UiPath projectsAutomation Ops Series: Session 2 - Governance for UiPath projects
Automation Ops Series: Session 2 - Governance for UiPath projects
 
UiPath Studio Web workshop series - Day 2
UiPath Studio Web workshop series - Day 2UiPath Studio Web workshop series - Day 2
UiPath Studio Web workshop series - Day 2
 
Oracle Database 23c Security New Features.pptx
Oracle Database 23c Security New Features.pptxOracle Database 23c Security New Features.pptx
Oracle Database 23c Security New Features.pptx
 
2024.03.12 Cost drivers of cultivated meat production.pdf
2024.03.12 Cost drivers of cultivated meat production.pdf2024.03.12 Cost drivers of cultivated meat production.pdf
2024.03.12 Cost drivers of cultivated meat production.pdf
 
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024
 
UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
 
Scenario Library et REX Discover industry- and role- based scenarios
Scenario Library et REX Discover industry- and role- based scenariosScenario Library et REX Discover industry- and role- based scenarios
Scenario Library et REX Discover industry- and role- based scenarios
 
Stobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through TokenizationStobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
 
Extra-120324-Visite-Entreprise-icare.pdf
Extra-120324-Visite-Entreprise-icare.pdfExtra-120324-Visite-Entreprise-icare.pdf
Extra-120324-Visite-Entreprise-icare.pdf
 
Outage Analysis: March 5th/6th 2024 Meta, Comcast, and LinkedIn
Outage Analysis: March 5th/6th 2024 Meta, Comcast, and LinkedInOutage Analysis: March 5th/6th 2024 Meta, Comcast, and LinkedIn
Outage Analysis: March 5th/6th 2024 Meta, Comcast, and LinkedIn
 
Planetek Italia Srl - Corporate Profile Brochure
Planetek Italia Srl - Corporate Profile BrochurePlanetek Italia Srl - Corporate Profile Brochure
Planetek Italia Srl - Corporate Profile Brochure
 

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