SlideShare a Scribd company logo
1 of 185
Download to read offline
TWIG
            tips & tricks
SUNSHINEPHP             JAVIER EGUILUZ
FEBRUARY 8TH 2013
Thanks to sponsors and organizers




      Adam            Pablo
      Culp            Godel
About me


           Javier Eguiluz
           Iā€™m a programmer
           and trainer from Spain.
I
SYMFONY
Iā€™m a long-term Symfony enthusiast
My Symfony2 book


              Agile web development
              with Symfony2
My Symfony website




                          WINNER 2011
                     Best Symfony Blog
Iā€™m the Ā« A week of SymfonyĀ» guy
Iā€™m the Ā« A week of SymfonyĀ» guy




              this is me!
I
TWIG
Twig is...
ā€¢ The best template engine for PHP.
ā€¢ Fast, secure and modern.
ā€¢ Easy to learn, to read and to write.
ā€¢ If you donā€™t know Twig yet, read the
  official docs at:
  http://twig.sensiolabs.org/documentation
AGENDA
Agenda
ā€¢ Tips and tricks about Twig.
ā€¢ Advanced features.
ā€¢ Defensive template design.
ā€¢ Best practices.
ā€¢ New and noteworthy Twig features.
NEW &
NOTEWORTHY
Twig development activity is crazy!
(last 12 months)      Twig             Jinja2
                    (PHP, Symfony)   (Python, Django)


Commits                525                11

Authors                 35                 5

Versions released       23                 0
New and noteworthy



 1     2     3       4   5
Ā«template_from_stringĀ» function
{% set user_template = "{{ description[0:80] }}
<br/> Price: {{ product.price }}" %}


{% include
template_from_string(user_template) %}
New and noteworthy



 1     2     3       4   5
Ā«includeĀ» function
{% include 'template.twig' %}
{{ include('template.twig') }}
                             equivalent
Ā«includeĀ» function              WRONG
{% set content = include('index.twig') %}

                                OK
{% set content %}
{{ include('index.twig') }}
{% endset %}
Ā«includeĀ» function                      WRONG
{{ include('index.twig')|striptags('<a>')[0:80] }}

{% set content %}                       OK
{{ include('index.twig') }}
{% endset %}

{{ content|striptags('<a>')[0:80] }}
New and noteworthy



 1     2     3       4   5
Ā«firstĀ» and Ā«lastĀ» filters
{% set firstElement = array|first %}
{% set lastElement = array|last %}
Ā«firstĀ» and Ā«lastĀ» filters
{% set first = array[0] %}
{% set last = array[array|length - 1] %}
Ā«firstĀ» and Ā«lastĀ» filters
{% set first = array[0] %}
{% set last = array[array|length - 1] %}

       only works for zero-
    indexed numeric arrays
Ā«firstĀ» and Ā«lastĀ» filters
{{ [1, 2, 3, 4]|first }}                      1

{{ { a: 1, b: 2, c: 3, d: 4 }|first }}        1

{{ '1234'|first }}                            1

                                     result
New and noteworthy



 1     2     3       4   5
Named arguments
{{ text|convert_encoding('UTF-8', 'ISO-8859-1') }}

{{ text|convert_encoding(
        to='UTF-8', from='ISO-8859-1') }}


{{ text|convert_encoding(
        from='ISO-8859-1', to='UTF-8') }}
Named arguments
{{ text|convert_encoding('UTF-8', 'ISO-8859-1') }}

{{ text|convert_encoding(
        to='UTF-8', from='ISO-8859-1') }}
                                         equivalent

{{ text|convert_encoding(
        from='ISO-8859-1', to='UTF-8') }}
Named arguments
{{ text|convert_encoding('UTF-8', 'ISO-8859-1') }}

{{ text|convert_encoding(
        to='UTF-8', from='ISO-8859-1') }}
                                         equivalent

{{ text|convert_encoding(
        from='ISO-8859-1', to='UTF-8') }}

              argument order changed!
Named arguments
{{ "now"|date("Y-m-d", "America/New_York") }}
                      mandatory to set the
                      second argument

{{ "now"|date(timezone="America/New_York") }}
                   just set the argument
                   you need
New and noteworthy



 1     2     3       4   5
Functions and filters before 1.12
$twig->addFunction('functionName',
   new Twig_Function_Function('someFunction')
);

$twig->addFunction('otherFunction',
   new Twig_Function_Method($this, 'someMethod')
);
Functions and filters in Twig 1.12
$twig->addFunction(new Twig_SimpleFunction(
      'twig_function', 'some_php_function'
));
$twig->addFunction(new Twig_SimpleFunction(
      'twig_function', array($object, 'method_name')
));
$twig->addFunction(new Twig_SimpleFunction(
      'twig_function', function() { ... }
));
Functions and filters in Twig 1.12
$twig->addFunction(new Twig_SimpleFunction(
      'twig_function', 'some_php_function'
));
$twig->addFunction(new Twig_SimpleFunction(
      'twig_function', array($object, 'method_name')
));
$twig->addFunction(new Twig_SimpleFunction(
      'twig_function', function() { ... }
));
Functions and filters in Twig 1.12
$twig->addFunction(new Twig_SimpleFunction(
      'twig_function', 'some_php_function'
));
$twig->addFunction(new Twig_SimpleFunction(
      'twig_function', array($object, 'method_name')
));
$twig->addFunction(new Twig_SimpleFunction(
      'twig_function', function() { ... }
));                                          super cool
OVERRIDING
   FILTERS
USE WITH
CAUTION
{% for i in array|sort %}
  {# ... #}
{% endfor %}
How is the
                   array sorted?


{% for i in array|sort %}
  {# ... #}
{% endfor %}
PHP defines 15 sorting functions
ā€¢   asort()      ā€¢   array_multisort()
ā€¢   arsort()     ā€¢   natcasesort()
ā€¢   krsort()     ā€¢   natsort()
ā€¢   ksort()      ā€¢   rsort()
ā€¢   rsort()      ā€¢   shuffle()
ā€¢   shuffle()    ā€¢   uasort()
ā€¢   sort()       ā€¢   uksort()
ā€¢   usort()
Overriding filters
ā€¢ Where can I find the PHP function
 used by Twig?
ā€¢ How can I override it with my own
 implementation?
Where Twig defines everything
lib/twig/Extension/Core.php
class Twig_Extension_Core extends Twig_Extension {
   public function getTokenParsers() {
     return array(

                                                          +1,300 lines
        new Twig_TokenParser_For(),
        new Twig_TokenParser_If(),

                                                          class!
        new Twig_TokenParser_Extends(),
        new Twig_TokenParser_Include(),
        new Twig_TokenParser_Block(),
        // ...
     );
   }

  public function getFilters() {
    $filters = array(
       'format' => new Twig_Filter_Function('sprintf'),
       'replace' => new Twig_Filter_Function('strtr'),
       'abs'      => new Twig_Filter_Function('abs'),
       // ...
    );
Where Twig defines everything
lib/twig/Extension/Core.php
class Twig_Extension_Core extends Twig_Extension {
   public function getTokenParsers() {
     return array(                                        ā€¢ Filters
        new Twig_TokenParser_For(),
        new Twig_TokenParser_If(),
        new Twig_TokenParser_Extends(),                   ā€¢ Functions
        new Twig_TokenParser_Include(),
        new Twig_TokenParser_Block(),
        // ...
                                                          ā€¢ Tags
                                                          ā€¢ Operators
     );
   }

  public function getFilters() {
    $filters = array(                                     ā€¢ Tests
       'format' => new Twig_Filter_Function('sprintf'),
       'replace' => new Twig_Filter_Function('strtr'),
       'abs'      => new Twig_Filter_Function('abs'),
       // ...
    );
Ā«sortĀ» filter uses Ā«asortĀ» function
new Twig_SimpleFilter('sort', 'twig_sort_filter'),

// ...

function twig_sort_filter($array)
{
  asort($array);

    return $array;
}
Overriding filters
āœ” Where can I find the PHP function
ā€¢
  used by Twig?
ā€¢ How can I override it with my own
  implementation?
Replace Ā«asortĀ» with Ā«natcasesortĀ»
// asort
A1, a0, a10, a2

// natcasesort
a0, A1, a2, a10
1. Define a new Twig extension
class MyCoreExtension
      extends Twig_Extension_Core {
   // ...
}
2. Define the new Ā«sortĀ» filter
class MyCoreExtension extends Twig_Extension_Core {
    public function getFilters() {
      // ...
    }
}
2. Define the new Ā«sortĀ» filter
class MyCoreExtension extends Twig_Extension_Core {
   public function getFilters() {
        return array_merge(
           parent::getFilters(),
           array( ... )
        );
    }
}
2. Define the new Ā«sortĀ» filter
class MyCoreExtension extends Twig_Extension_Core
{
   public function getFilters()
   {
     return array_merge(parent::getFilters(), array(
         'sort' => new Twig_Filter_Method($this, 'sortFilter')
     ));
   }

    public function sortFilter($array)
    {
      natcasesort($array);
      return $array;
    }
}
3. Register the new extension
$twig = new Twig_Environment( ... );

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


{% for i in array|sort %}
  {# ... #}
{% endfor %}
DYNAMIC
FUNCTIONS
WordPress template tags
the_ID()
the_title()
the_time()
the_content()
the_category()
the_shortlink()
WordPress template tags
the_ID()          class Post {
                     $id = ...
the_title()          $title = ...
the_time()           $time = ...
the_content()        $content = ...
                     $category = ...
the_category()       $shortlink = ...
the_shortlink()      // ...
                  }
the_*()
Variable functions
$twig->addFunction(
     'the_*',
     new Twig_Function_Function('wordpress')
);

function wordpress($property, $options)
{
  // ...
}
Variable functions
$twig->addFunction(
     'the_*',
     new Twig_Function_Function('wordpress')
);

function wordpress($property, $options)
{
  // ...
}
Variable functions
$twig->addFunction(
  'the_*',
   new Twig_Function_Function('wordpress')
);          donā€™t use regexps,
            just asterisks
function wordpress($property, $options)
{
   // ...
}
Variable functions in practice
{{ the_ID() }}
function wordpress('ID') { ... }

{{ the_content() }}
function wordpress('content') { ... }
Variable functions in practice
{{ the_title('<h3>', '</h3>') }}

function wordpress(
   'title',
   array('<h3>', '</h3>')
) { ... }
WordPress template tags
next_image_link()
next_post_link()
next_posts_link()
previous_image_link()
previous_post_link()
previous_posts_link()
WordPress template tags
next_image_link()        next_*_link()
next_post_link()          next_*_link()
next_posts_link()        next_*_link()
previous_image_link() previous_*_link()
previous_post_link()  previous_*_link()
previous_posts_link() previous_*_link()
WordPress template tags
next_image_link()       *_*_link()
next_post_link()        *_*_link()
next_posts_link()       *_*_link()
previous_image_link()   *_*_link()
previous_post_link()    *_*_link()
previous_posts_link()   *_*_link()
USE WITH
CAUTION
php_*()
php_* dynamic function
      $twig->addFunction(new Twig_SimpleFunction('php_*',
      function() {
        $arg_list = func_get_args();
        $function = array_shift($arg_list);

        return call_user_func_array($function, $arg_list);
      },
      array('pre_escape' => 'html', 'is_safe' => array('html')
));
Exposing PHP functions
{{ php_print_r(['value1', 'value2']) }}
{{ php_crypt('mypassword') }}
{{ php_memory_get_usage() }}
{{ php_uniqid() }}


Reference
http://github.com/lidaa/LidaaTwigBundle
CUSTOM TAGS
{% source ā€˜...ā€™ %}
{% source ā€˜...ā€™ %}
       file_get_contents()
The new Ā«sourceĀ» tag
{% source 'home.twig' %}
{% source '../../../composer.json' %}
How does Twig work internally
{% source
'simple.twig' %}
                      Twig                PHP     class
                                                  __TwigTemplate_06dff1ec7c2c
                                                  ceb3f45ac76fc059b730



                      template             file
                                                  extends Twig_Template
                                                  {
                                                     public function
{# ... #}                                         __construct(Twig_Environment
                                                  $env)
                                                     {
                                                       parent::__construct($env);

                                                      $this->parent = $this-
                                                  >env-
                                                  >loadTemplate("layout.twig");

                                                      $this->blocks = array(




                   Lexer         Parser    Compiler
How does Twig work internally
{% source
'simple.twig' %}
                      Twig                 PHP     class
                                                   __TwigTemplate_06dff1ec7c2c
                                                   ceb3f45ac76fc059b730



                      template              file
                                                   extends Twig_Template
                                                   {
                                                      public function
{# ... #}                                          __construct(Twig_Environment



                               you must
                                                   $env)
                                                      {
                                                        parent::__construct($env);




                           provide these
                                                       $this->parent = $this-
                                                   >env-
                                                   >loadTemplate("layout.twig");

                                                       $this->blocks = array(




                   Lexer         Parser     Compiler
1. Create a new token parser
class SourceTokenParser extends Twig_TokenParser
{
   public function getTag()
   {
     return 'source';
   }
}
2. Register the new token parser
$loader = new Twig_Loader_Filesystem(...);
$twig = new Twig_Environment($loader, array(...));

$twig->addTokenParser(
   new SourceTokenParser()
);
3. Fill in the Ā«parseĀ» method
class SourceTokenParser extends Twig_TokenParser
{
   public function parse(Twig_Token $token)
   {
     $lineno = $token->getLine();
     $value = $this->parser->getExpressionParser()
                              ->parseExpression();

        $this->parser->getStream()
                     ->expect(Twig_Token::BLOCK_END_TYPE);

        return new SourceNode($value, $lineno, $this->getTag());
    }
}
3. Fill in the Ā«parseĀ» method
class SourceTokenParser extends Twig_TokenParser
{
   public function parse(Twig_Token $token)
   {                                               this    is hard
     $lineno = $token->getLine();
     $value = $this->parser->getExpressionParser()
                              ->parseExpression();

        $this->parser->getStream()
                     ->expect(Twig_Token::BLOCK_END_TYPE);

        return new SourceNode($value, $lineno, $this->getTag());
    }
}
4. Define the node class that compiles tags
class SourceNode extends Twig_Node {
    public function __construct(Twig_Node_Expression $value, $lineno, $tag = null) {
        parent::__construct(array('file' => $value), array(), $lineno, $tag);
    }

    public function compile(Twig_Compiler $compiler) {
      $compiler
         -> // ...
         ->write('echo file_get_contents(')
         ->subcompile($this->getNode('file'))
         ->raw(');')
      ;
    }
}
4. Define the node class that compiles tags
class SourceNode extends Twig_Node {
    public function __construct(Twig_Node_Expression $value, $lineno, $tag = null) {
        parent::__construct(array('file' => $value), array(), $lineno, $tag);
    }

    public function compile(Twig_Compiler $compiler) {
      $compiler
         -> // ...                                                                     this is
         ->write('echo file_get_contents(')                                            very hard
         ->subcompile($this->getNode('file'))
         ->raw(');')
      ;
    }
}
The compiled PHP template
// line 3
echo file_get_contents("simple.twig");
// ...
                  {% source 'simple.twig' %}
// line 5
echo file_get_contents("../../../composer.json");
TEMPLATE
 LOADERS
Most apps use a single loader
$loader = new Twig_Loader_Filesystem(
   __DIR__.'/templates'
);
$twig = new Twig_Environment($loader, array());

$html = $twig->render('home.html.twig');
Chaining several loaders
$loader1 = new Twig_Loader_Filesystem(...);
$loader2 = new Twig_Loader_Filesystem(...);
$loader = new Twig_Loader_Chain(array(
    $loader1, $loader2
));

$twig = new Twig_Environment($loader, array());

// ...
Chaining different loaders
$loader1 = new Twig_Loader_Filesystem(...);
$loader2 = new Twig_Loader_Array(...);
$loader3 = new Twig_Loader_String(...);

$loader = new Twig_Loader_Chain(array(
    $loader1, $loader2, $loader3
));

// ...
Chaining different loaders
$loader1 = new Twig_Loader_Filesystem(...);
$loader2 = new Twig_Loader_Array(...);
$loader3 = new Twig_Loader_String(...);

$loader = new Twig_Loader_Chain(array(
    $loader1, $loader2, $loader3
));
                              order matters!
// ...
Adding loaders at runtime
$loader = new Twig_Loader_Filesystem('/templates');
$twig = new Twig_Environment($loader, array());

if ( ... ) {
    $twig->addLoader(
         new Twig_Loader_Filesystem('/special_templates')
    );
}

// ...
Storing templates in several folders
$loader = new Twig_Loader_Filesystem(array(
    __DIR__.'/default',
    __DIR__.'/templates',
    __DIR__.'/themes'
));
$twig = new Twig_Environment($loader, array());

// ...
Storing templates in several folders
$loader = new Twig_Loader_Filesystem(array(
    __DIR__.'/default',
    __DIR__.'/templates',
    __DIR__.'/themes'
));
$twig = new Twig_Environment($loader,went wrong.
        Whoops, looks like something array());

// ... Twig_Error_Loader: The "_DIR_/templates"
       directory does not exist.
Adding folders at runtime
$loader = new Twig_Loader_Filesystem('/templates');

$path = $user_slug.'/templates';
if (file_exists($path)) {
    $loader->addPath($path);
}

$twig = new Twig_Environment($loader, array());

// ...
Prioritizing template folders
$loader = new Twig_Loader_Filesystem('/templates');

if(...) {
   $loader->addPath($user_slug.'/templates');
   $loader->prependPath($user_slug.'/themes');
}

$twig = new Twig_Environment($loader, array());

// ...
Prioritizing template folders
$loader = new Twig_Loader_Filesystem('/templates');

if(...) {
   $loader->addPath($user_slug.'/templates');
   $loader->prependPath($user_slug.'/themes');
}                     path is added before
                      any other path
$twig = new Twig_Environment($loader, array());

// ...
Itā€™s difficult to prioritize folders
$loader
  ->addPath(...)
  ->addPath(...)
  ->prependPath(...)
  ->addPath(...)
  ->prependPath(...)
;
NAMESPACES
Namespaces are better than folders

       templates/
         themes/index.twig
         admin/index.twig
         frontend/index.twig
Namespaces are better than folders
$loader = new Twig_Loader_Filesystem(__DIR__.'/templates');
$twig = new Twig_Environment($loader, array());


$html = $twig->render('admin/index.twig');
$html = $twig->render('themes/index.twig');
$html = $twig->render('frontend/index.twig');
App doesnā€™t work if folders change

       templates/
         themes/index.twig
         frontend/index.twig

       admin/
App doesnā€™t work if folders change

        templates/
           themes/index.twig
    Whoops, looks like something went wrong.
           frontend/index.twig
    Twig_Error_Loader: The "admin/index.twig"

        admin/
    template does not exist.
Registering and using namespaces
$loader = new Twig_Loader_Filesystem(__DIR__.'/templates');
$loader->addPath(__DIR__.'/admin', 'admin');
$twig = new Twig_Environment($loader, array());

$html = $twig->render('@admin/index.twig');
$html = $twig->render('admin/index.twig');
Registering and using namespaces
$loader = new Twig_Loader_Filesystem(__DIR__.'/templates');
$loader->addPath(__DIR__.'/admin', 'admin');
$twig = new Twig_Environment($loader, array());

$html = $twig->render('@admin/index.twig');
$html = $twig->render('admin/index.twig');
Registering and using namespaces
$loader = new Twig_Loader_Filesystem(__DIR__.'/templates');
$loader->addPath(__DIR__.'/admin', 'admin');
$twig = new Twig_Environment($loader, array());

$html = $twig->render('@admin/index.twig');
$html = $twig->render('admin/index.twig');
Registering and using namespaces
$loader = new Twig_Loader_Filesystem(__DIR__.'/templates');
$loader->addPath(__DIR__.'/admin', 'admin');
$twig = new Twig_Environment($loader, array());

$html = $twig->render('@admin/index.twig');
$html = $twig->render('admin/index.twig');
                                     logical path
                     physical path
A practical use case
$loader->addPath(
   __DIR__.'/themes/default/admin', 'backend'
);

// with namespaces
$html = $twig->render('@backend/edit.twig');

// with physical paths
$html = $twig->render('themes/default/admin/
edit.twig');
TWIG.JS
Twig.js = Twig inside JavaScript
Twig.js = Twig inside JavaScript
TWIG
       TWIG
Twig.js = Twig inside JavaScript
TWIG
       TWIG
HTML                      HTML
Twig.js = Twig inside JavaScript
TWIG
       TWIG
HTML                      HTML
          JS

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

JS            TWIG


TWIG
Ā«A tale of two twig.jsĀ»
         twig.js by
         Johannes Schmitt
         http://github.com/schmittjoh/twig.js


         twig.js by
         John Roepke
         https://github.com/justjohn/twig.js
Ā«A tale of two twig.jsĀ»
         twig.js by
         Johannes Schmitt
         http://github.com/schmittjoh/twig.js


         twig.js by
         John Roepke
         https://github.com/justjohn/twig.js
twig.js by Johannes Schmitt
ā€¢ A templating engine for Javascript
  using Jinja/Twig style syntax.
ā€¢ It compiles your Twig templates to
  raw Javascript (to use the same
  templates for both server and client).
Defining the template
{# tweet.twig #}
{% twig_js name="tweet" %}
<p>
  {{ message }} <span>by {{ author }}</span>
  <time datetime="{{ published_at|date }}">
     {{ published_at|date }}
  </time>
</p>
Defining the template
                              important!
{# tweet.twig #}
{% twig_js name="tweet" %}
<p>
  {{ message }} <span>by {{ author }}</span>
  <time datetime="{{ published_at|date }}">
     {{ published_at|date }}
  </time>
</p>
Rendering the template in the browser
<script type="text/javascript" src="twig.js"></script>
<script type="text/javascript" src="tweet.js"></script>
<script language="javascript" type="text/javascript">
  var html = Twig.render(tweet, {
      message: "...", author: "...", published_at: "..."
   }));
</script>
Rendering the template in the browser
<script type="text/javascript" src="twig.js"></script>
<script type="text/javascript" src="tweet.js"></script>
<script language="javascript" type="text/javascript">
  var html = Twig.render(tweet, {
      message: "...", author: "...", published_at: "..."
   }));
</script>
                {% twig_js name="tweet" %}
Ā«A tale of two twig.jsĀ»
         twig.js by
         Johannes Schmitt
         http://github.com/schmittjoh/twig.js


         twig.js by
         John Roepke
         http://github.com/justjohn/twig.js
twig.js by John Roepke
ā€¢ A pure JavaScript implementation of
  the Twig PHP templating language.
ā€¢ The goal is to provide a library that is
  compatible with both browsers and
  server side node.js.
Defining the template
{# tweet.twig #}         not necessary
{% twig_js name="tweet" %}
<p>
  {{ message }} <span>by {{ author }}</span>
  <time datetime="{{ published_at|date }}">
     {{ published_at|date }}
  </time>
</p>
Rendering the template in the browser
<script type="text/javascript" src="twig.js"></script>
<script language="javascript" type="text/javascript">
  var template = twig({data:
     '<p> {{ message }} ... </p>'           templateā€™s
  });                                       source code
  var html = template.render({
     message: "...", author: "...", published_at: "..."
  });
</script>
Rendering the template in node.js
<script type="text/javascript">
var Twig = require("twig"), express = require('express'), app = express();

app.set("twig options", { strict_variables: false });

app.get('/tweet', function(req, res){
   res.render('tweet.twig', {
      message: "...", author: "...", published_at: "..."
   });
});

app.listen(80);
</script>
SANDBOX
A simple object
$offer = new Offer();
$offer->title = "Lorem Ipsum Dolor Sit Amet";
$offer->description = "Ut enim ad minim veniam ...";
$offer->commission = 20;
A simple object
$offer = new Offer();
$offer->title = "Lorem Ipsum Dolor Sit Amet";
$offer->description = "Ut enim ad minim veniam ...";
$offer->commission = 20;

                  TOP-SECRET
                  information
Templates can show any property
Offer data
----------
Title: {{ offer.title }}
Description: {{ offer.description }}
Commission: {{ offer.commission }}
Twitter Sandbox
ā€¢ Itā€™s a regular Twig extension.
ā€¢ Disabled by default.
ā€¢ It allows to restrict the functions,
  filters, tags and object properties
  used in the templates.
ā€¢ Itā€™s based on security policies.
Define a new security policy
$loader = new Twig_Loader_Filesystem('...');
$twig = new Twig_Environment($loader, array());

$properties = array(
   'Offer' => array('title', 'description')
);

$policy = new Twig_Sandbox_SecurityPolicy(
   array(), array(), array(), $properties, array()
);
Define a new security policy
$loader = new Twig_Loader_Filesystem('...');

                                                  commission is
$twig = new Twig_Environment($loader, array());

$properties = array(                              not included
   'Offer' => array('title', 'description')
);

$policy = new Twig_Sandbox_SecurityPolicy(
   array(), array(), array(), $properties, array()
);
Define a new security policy
$loader = new Twig_Loader_Filesystem('...');
$twig = new Twig_Environment($loader, array());
$properties = array('Offer' => array('title', 'description'));
$policy = new Twig_Sandbox_SecurityPolicy(
   array(), array(), array(), $properties, array()
);

$sandbox = new Twig_Extension_Sandbox(
   $policy, true
);
$twig->addExtension($sandbox);
Define a new security policy
$loader = new Twig_Loader_Filesystem('...');
$twig = new Twig_Environment($loader, array());
$properties = array('Offer' => array('title', 'description'));
$policy = new Twig_Sandbox_SecurityPolicy(
   array(), array(), array(), $properties, array()
);

$sandbox = new Twig_Extension_Sandbox(
   $policy, true
);                ALL templates are sandboxed
$twig->addExtension($sandbox);
The template now displays an error
Offer data
----------
Title: {{ offer.title }}
Description: {{ offer.description }}
Commission: {{ offer.commission }}
The template now displays an error
Offer data
----------
Title: {{ offer.title }}
Description: {{ offer.description }}
      Whoops, looks like something went wrong.
Commission: {{ offer.commission }}
     Calling "comission" property on a "Offer"
     object is not allowed in ... at line 6.
Security policy arguments
$policy = new Twig_Sandbox_SecurityPolicy(
     $tags,
     $filters,
     $methods,
     $properties,
     $functions
);
Allow to use just 3 filters
$policy = new Twig_Sandbox_SecurityPolicy(
     $tags,
     array('escape', 'upper', 'lower'),
     $methods,
     $properties,
     $functions
);
Allow to use just 2 tags
$policy = new Twig_Sandbox_SecurityPolicy(
     array('include', 'extends'),
     $filters,
     $methods,
     $properties,
     $functions
);
Use any tag except include and extends
$policy = new Twig_Sandbox_SecurityPolicy(
   array_diff(
      array_keys($twig->getTags()),
      array('include', 'extends')
   ),
   $filters,
   $methods,
   $properties,
   $functions
);
THE BASE
TEMPLATE
Adding a trace to all web pages
<html>
<head> ... </head>

<body>
 ...

 <span data-host="Darwin 10.8.0 ..."
       data-elapsed="0.97804594039 sec."
       data-timestamp="1339609672.9781">
 </span>
</body>
</html>
Twig cache
cache/09/fc/2d8a188dda8245d295e6324582f2.php

/* homepage.html.twig */
class __TwigTemplate_09f8a...582f2
      extends Twig_Template
{
    public function __construct(Twig_Environment $env) { ... }
    protected function doGetParent(array $context) { ... }
    protected function doDisplay(array $context, array $blocks) { ... }

    // ...
}
cache/09/fc/2d8a188dda8245d295e6324582f2.php

/* homepage.html.twig */
class __TwigTemplate_09f8a...582f2
      extends Twig_Template
{
    public function __construct(Twig_Environment $env) { ... }
    protected function doGetParent(array $context) { ... }
    protected function doDisplay(array $context, array $blocks) { ... }

    // ...
}
The base template class
class __TwigTemplate_09f...2f2
      extends Twig_Template
{
   // ...
}
lib/Twig/Template.php
abstract class Twig_Template {
     public function render(array $context)
     {
         // ...
     }
    // ...

}
lib/Twig/Template.php
abstract class Twig_Template {
     public function render(array $context)
     {
                         tweak this method
         // ...          to tweak templates
     }
    // ...

}
Use a different base template
$loader = new Twig_Loader_Filesystem('...');
$twig = new Twig_Environment($loader, array(
    'base_template_class' => 'ACMEMyTwigTemplate',
));

# if you use Symfony2
# app/config/config.yml
twig:
  base_template_class: "ACMEMyTwigTemplate"
The new base template class
class MyTwigTemplate extends Twig_Template
{
    public function render(array $context)
    {
        $trace = ...

        return str_replace(
          "</body>",
          $trace."n</body>",
             parent::render($context)
        );
    }
}
The new base template class
abstract class MyTwigTemplate extends Twig_Template
{
  public function render(array $context)
  {
      $trace = sprintf('<span data-host="%s" data-elapsed="%s sec."
                              data-timestamp="%s"></span>',
         php_uname(),
         microtime(true) - $_SERVER['REQUEST_TIME'],
         microtime(true)
      );
      return str_replace("</body>", $trace."n</body>", parent::render($context));
  }
Adding a trace to all web pages
<html>
<head> ... </head>

<body>
 ...

 <span data-host="Darwin 10.8.0 ..."
       data-elapsed="0.97804594039 sec."
       data-timestamp="1339609672.9781">
 </span>
</body>
</html>
DEFENSIVE
   DESIGN
Errors will happen

                 ERROR 500


  ERROR
Errors will happen

                     ERROR 500


  ERROR




          users prefer this
Default values
{{ variable|default("value") }}

(discount {{ discount|default(0) }}%)
Hi {{ user_name|default("") }}!
The use_strict_variables option
$loader = new Twig_Loader_Filesystem('...');
$twig = new Twig_Environment($loader, array(
      'use_strict_variablesā€™ => false
));
The use_strict_variables option
$loader = new Twig_Loader_Filesystem('...');
$twig = new Twig_Environment($loader, array(
      'use_strict_variablesā€™ => false
));
       inexistent variables wonā€™t
       produce a Twig error page
Ignore missing templates
{% include 'section_' ~ slug ~ '.twig'
   ignore missing %}
Define fallback templates
{% extends [
  'layout_' ~ locale ~ '.html.twig',
  'layout.html.twig'
] %}
Use fallbacks and ignore errors
{% include [
  'layout_' ~ locale ~ '.html.twig',
  'layout.html.twig'
] ignore missing %}
Twig linter
ā€¢ A linter detects syntax errors
  automatically.
ā€¢ Use it as a preventive measure to
  detect errors before serving pages to
  users.
Twig linter in practice
$twig = new Twig_Environment($loader, array(..));

try {
   $twig->parse($twig->tokenize($plantilla));
   echo "[OK]";
} catch (Twig_Error_Syntax $e) {
   echo "[ERROR] There are some syntax errors";
}
INTEGRATING
    TWITTER
 BOOTSTRAP
Twitter Bootstrap
Twitter Bootstrap grid model


     3
Each row adds up to 12 columns


     3




         12 = 3 + 9
Each row adds up to 12 columns


     3




         12 = 3 + 4 + 5
Each row adds up to 12 columns


     3




   12 = 3 + 2 + 3 + 4
Most grids are based on 2 or 3 columns



      3




           2 columns
Most grids are based on 2 or 3 columns



      3




           2 columns
Most grids are based on 2 or 3 columns



      3




           3 columns
extends + include = embed
reuse a fixed
  structure

extends + include = embed
reuse a fixed   reuse
  structure       contents

extends + include = embed
reuse a fixed    reuse
  structure        contents

extends + include = embed
              reuse contents and a
                 flexible structure
Reusable 2-column grid
{# grid_2.twig #}
<div class="row">
  <div class="{{ col1_span }} {{ col1_offset }}">
     {% block column1 %}{% endblock %}
  </div>

  <div class="{{ col2_span }} {{ col2_offset }}">
    {% block column2 %}{% endblock %}
  </div>
</div>
2-column grid in practice
{% embed 'grid_2.twig' with { 'col1_span': 'span9',
                             'col2_span': 'span3' } %}

  {% block column1 %} {# ... #} {% endblock %}

  {% block column2 %} {# ... #} {% endblock %}

{% endembed %}
2-column grid in practice
{% embed 'grid_2.twig' with { 'col1_span':Magic
                                    Twig   'span9',
                                    in progress...
                             'col2_span': 'span3' } %}

  {% block column1 %} {# ... #} {% endblock %}

  {% block column2 %} {# ... #} {% endblock %}

{% endembed %}
2-column grid in practice
{% embed 'grid_2.twig' with { 'layout': '9_3' } %}

  {% block column1 %} {# ... #} {% endblock %}

  {% block column2 %} {# ... #} {% endblock %}

{% endembed %}
2-column grid in practice
{% embed 'grid_2.twig' with { 'layout': '9_3' } %}

{% set col1_span = layout|split('_')[0:]|join %}
{% set col2_span = layout|split('_')[1:]|join %}
3-column grid
{% embed 'grid_3.twig' with { 'layout': '3_6_3' } %}

  {% block column1 %} {# ... #} {% endblock %}

  {% block column2 %} {# ... #} {% endblock %}

  {% block column3 %} {# ... #} {% endblock %}

{% endembed %}
3-column grid
{% embed 'grid_3.twig' with { 'layout': '3_6_3' } %}

{% set col1_span = layout|split('_')[0:]|join %}
{% set col2_span = layout|split('_')[1:]|join %}
{% set col3_span = layout|split('_')[2:]|join %}
Recap
ā€¢ New and noteworthy   ā€¢ Sandbox
ā€¢ Overriding filters   ā€¢ Base template
ā€¢ Dynamic functions    ā€¢ Defensive design
ā€¢ Custom tags          ā€¢ Embed tag
ā€¢ Template loaders
ā€¢ Namespaces
ā€¢ Twig.js
http://twig.sensiolabs.org
THANK YOU.
CONTACT ME
Contact me
ā€¢ javier.eguiluz@gmail.com
ā€¢ linkedin.com/in/javiereguiluz
ā€¢ twitter.com/javiereguiluz
ā€¢ github.com/javiereguiluz

More Related Content

What's hot

Advanced Javascript
Advanced JavascriptAdvanced Javascript
Advanced Javascript
Adieu
Ā 

What's hot (20)

Demystifying Angular Animations
Demystifying Angular AnimationsDemystifying Angular Animations
Demystifying Angular Animations
Ā 
30 Symfony Best Practices
30 Symfony Best Practices30 Symfony Best Practices
30 Symfony Best Practices
Ā 
Class 5 - PHP Strings
Class 5 - PHP StringsClass 5 - PHP Strings
Class 5 - PHP Strings
Ā 
Express JS
Express JSExpress JS
Express JS
Ā 
Advanced Javascript
Advanced JavascriptAdvanced Javascript
Advanced Javascript
Ā 
C# Delegates
C# DelegatesC# Delegates
C# Delegates
Ā 
Jdbc architecture and driver types ppt
Jdbc architecture and driver types pptJdbc architecture and driver types ppt
Jdbc architecture and driver types ppt
Ā 
Database Connectivity in PHP
Database Connectivity in PHPDatabase Connectivity in PHP
Database Connectivity in PHP
Ā 
Php Tutorials for Beginners
Php Tutorials for BeginnersPhp Tutorials for Beginners
Php Tutorials for Beginners
Ā 
Delegetes in c#
Delegetes in c#Delegetes in c#
Delegetes in c#
Ā 
introduction to Vue.js 3
introduction to Vue.js 3 introduction to Vue.js 3
introduction to Vue.js 3
Ā 
Automated Web Testing Using Selenium
Automated Web Testing Using SeleniumAutomated Web Testing Using Selenium
Automated Web Testing Using Selenium
Ā 
Api testing
Api testingApi testing
Api testing
Ā 
Introduction to Node js
Introduction to Node jsIntroduction to Node js
Introduction to Node js
Ā 
PHP - DataType,Variable,Constant,Operators,Array,Include and require
PHP - DataType,Variable,Constant,Operators,Array,Include and requirePHP - DataType,Variable,Constant,Operators,Array,Include and require
PHP - DataType,Variable,Constant,Operators,Array,Include and require
Ā 
testng
testngtestng
testng
Ā 
Servlet.ppt
Servlet.pptServlet.ppt
Servlet.ppt
Ā 
Php sessions & cookies
Php sessions & cookiesPhp sessions & cookies
Php sessions & cookies
Ā 
Oops in PHP
Oops in PHPOops in PHP
Oops in PHP
Ā 
Collections - Maps
Collections - Maps Collections - Maps
Collections - Maps
Ā 

Viewers also liked

Anatomy Lect 3 Head & Neck
Anatomy Lect 3 Head & NeckAnatomy Lect 3 Head & Neck
Anatomy Lect 3 Head & Neck
Miami Dade
Ā 
eBay 2009 Analyst Day
eBay 2009 Analyst DayeBay 2009 Analyst Day
eBay 2009 Analyst Day
Phil Wolff
Ā 
fluid, electrolytes, acid base balance
fluid, electrolytes, acid base balancefluid, electrolytes, acid base balance
fluid, electrolytes, acid base balance
twiggypiggy
Ā 
BUSINESS QUIZ -Round 1
 BUSINESS QUIZ -Round 1 BUSINESS QUIZ -Round 1
BUSINESS QUIZ -Round 1
pradeep acharya
Ā 

Viewers also liked (20)

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

Similar to 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 PHP
Fabien Potencier
Ā 
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
Six Apart KK
Ā 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
ShriKant Vashishtha
Ā 

Similar to Twig tips and tricks (20)

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

More from Javier 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 8
Javier Eguiluz
Ā 
Silex al lĆ­mite
Silex al lĆ­miteSilex al lĆ­mite
Silex al lĆ­mite
Javier 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 avanzadas
Javier Eguiluz
Ā 

More from 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
Ā 

Recently uploaded

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
Ā 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(ā˜Žļø+971_581248768%)**%*]'#abortion pills for sale in dubai@
Ā 

Recently uploaded (20)

Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
Ā 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
Ā 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
Ā 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
Ā 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Ā 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
Ā 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Ā 
šŸ¬ The future of MySQL is Postgres šŸ˜
šŸ¬  The future of MySQL is Postgres   šŸ˜šŸ¬  The future of MySQL is Postgres   šŸ˜
šŸ¬ The future of MySQL is Postgres šŸ˜
Ā 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
Ā 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
Ā 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
Ā 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
Ā 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
Ā 
Scaling API-first ā€“ The story of a global engineering organization
Scaling API-first ā€“ The story of a global engineering organizationScaling API-first ā€“ The story of a global engineering organization
Scaling API-first ā€“ The story of a global engineering organization
Ā 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Ā 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Ā 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
Ā 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
Ā 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Ā 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Ā 

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