Sie sind auf Seite 1von 185

JAVIER EGUILUZ

SUNSHINEPHP
FEBRUARY 8TH 2013
TWIG
tips & tricks
Thanks to sponsors and organizers
Adam
Culp
Pablo
Godel
About me
Javier Eguiluz
Im a programmer
and trainer from Spain.
I
SYMFONY
Im a long!term Symfony enthusiast
My Symfony2 book
Agile web development
with Symfony2
My Symfony website
WINNER 2011
Best Symfony Blog
Im the A week of Symfony guy
Im 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 dont 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
(PHP, Symfony)
Jinja2
(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
{% set content = include('index.twig') %}
{% set content %}
{{ include('index.twig') }}
{% endset %}
WRONG
OK
include function
{{ include('index.twig')|striptags('<a>')[0:80] }}
{% set content %}
{{ include('index.twig') }}
{% endset %}
{{ content|striptags('<a>')[0:80] }}
WRONG
OK
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') }}
{{ text|convert_encoding(
from='ISO-8859-1', to='UTF-8') }}
equivalent
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') }}
equivalent
argument order changed!
Named arguments
{{ "now"|date("Y-m-d", "America/New_York") }}
{{ "now"|date(timezone="America/New_York") }}
mandatory to set the
second argument
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 %}
{% for i in array|sort %}
{# ... #}
{% endfor %}
How is the
array sorted?
PHP defines 15 sorting functions

asort()

arsort()

krsort()

ksort()

rsort()

shuffle()

sort()

usort()

array_multisort()

natcasesort()

natsort()

rsort()

shuffle()

uasort()

uksort()
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(
new Twig_TokenParser_For(),
new Twig_TokenParser_If(),
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'),
// ...
);
}
+1,300 lines
class!
Where Twig defines everything
lib/twig/Extension/Core.php
class Twig_Extension_Core extends Twig_Extension {
public function getTokenParsers() {
return array(
new Twig_TokenParser_For(),
new Twig_TokenParser_If(),
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'),
// ...
);
}

Filters

Functions

Tags

Operators

Tests
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()
);
{% for i in array|sort %}
{# ... #}
{% endfor %}
This is now
natcasesort
DYNAMIC
FUNCTIONS
WordPress template tags
the_ID()
the_title()
the_time()
the_content()
the_category()
the_shortlink()
WordPress template tags
the_ID()
the_title()
the_time()
the_content()
the_category()
the_shortlink()
class Post {
$id = ...
$title = ...
$time = ...
$content = ...
$category = ...
$shortlink = ...
// ...
}
the_*()
$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')
);
function wordpress($property, $options)
{
// ...
}
Variable functions
dont use regexps,
just asterisks
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
class
__TwigTemplate_06dff1ec7c2c
ceb3f45ac76fc059b730
extends Twig_Template
{
public function
__construct(Twig_Environment
$env)
{
parent::__construct($env);
$this->parent = $this-
>env-
>loadTemplate("layout.twig");
$this->blocks = array(
PHP
file
Lexer Parser Compiler
Twig
template
{% source
'simple.twig' %}
{# ... #}
How does Twig work internally
class
__TwigTemplate_06dff1ec7c2c
ceb3f45ac76fc059b730
extends Twig_Template
{
public function
__construct(Twig_Environment
$env)
{
parent::__construct($env);
$this->parent = $this-
>env-
>loadTemplate("layout.twig");
$this->blocks = array(
PHP
file
Lexer Parser Compiler
Twig
template
{% source
'simple.twig' %}
{# ... #}
you must
provide these
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)
{
$lineno = $token->getLine();
$value = $this->parser->getExpressionParser()
->parseExpression();
$this->parser->getStream()
->expect(Twig_Token::BLOCK_END_TYPE);
return new SourceNode($value, $lineno, $this->getTag());
}
}
this is hard
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
-> // ...
->write('echo file_get_contents(')
->subcompile($this->getNode('file'))
->raw(');')
;
}
}
this is
very hard
The compiled PHP template
// line 3
echo file_get_contents("simple.twig");
// ...
// line 5
echo file_get_contents("../../../composer.json");
{% source 'simple.twig' %}
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, array());
// ...
Whoops, looks like something went wrong.
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');
}
$twig = new Twig_Environment($loader, array());
// ...
path is added before
any other path
Its 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 doesnt work if folders change
templates/
themes/index.twig
frontend/index.twig
admin/
App doesnt work if folders change
templates/
themes/index.twig
frontend/index.twig
admin/
Whoops, looks like something went wrong.
Twig_Error_Loader: The "admin/index.twig"
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');
physical path
logical 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
Twig.js = Twig inside JavaScript
TWIG
TWIG
Twig.js = Twig inside JavaScript
TWIG
HTML
HTML
TWIG
Twig.js = Twig inside JavaScript
TWIG
HTML
JS
HTML
JS
TWIG
Twig.js = Twig inside JavaScript
TWIG
HTML
JS
TWIG
HTML
JS
TWIG
A tale of two twig.js
twig.js by
Johannes Schmitt
twig.js by
John Roepke
http://github.com/schmittjoh/twig.js
https://github.com/justjohn/twig.js
A tale of two twig.js
twig.js by
Johannes Schmitt
twig.js by
John Roepke
http://github.com/schmittjoh/twig.js
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
{# tweet.twig #}
{% twig_js name="tweet" %}
<p>
{{ message }} <span>by {{ author }}</span>
<time datetime="{{ published_at|date }}">
{{ published_at|date }}
</time>
</p>
important!
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
twig.js by
John Roepke
http://github.com/schmittjoh/twig.js
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 #}
{% twig_js name="tweet" %}
<p>
{{ message }} <span>by {{ author }}</span>
<time datetime="{{ published_at|date }}">
{{ published_at|date }}
</time>
</p>
not necessary
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>'
});
var html = template.render({
message: "...", author: "...", published_at: "..."
});
</script>
templates
source code
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

Its a regular Twig extension.

Disabled by default.

It allows to restrict the functions,


filters, tags and object properties
used in the templates.

Its 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('...');
$twig = new Twig_Environment($loader, array());
$properties = array(
'Offer' => array('title', 'description')
);
$policy = new Twig_Sandbox_SecurityPolicy(
array(), array(), array(), $properties, array()
);
commission is
not included
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
);
$twig->addExtension($sandbox);
ALL templates are sandboxed
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 }}
Commission: {{ offer.commission }}
Whoops, looks like something went wrong.
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' => '\ACME\MyTwigTemplate',
));
# if you use Symfony2
# app/config/config.yml
twig:
base_template_class: "\ACME\MyTwigTemplate"
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 wont
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
extends + include = embed
reuse a fixed
structure
extends + include = embed
reuse a fixed
structure
reuse
contents
extends + include = embed
reuse a fixed
structure
reuse contents and a
flexible structure
reuse
contents
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': 'span9',
'col2_span': 'span3' } %}

{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
Twig Magic
in progress...
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

Overriding filters

Dynamic functions

Custom tags

Template loaders

Namespaces

Twig.js

Sandbox

Base template

Defensive design

Embed tag
http://twig.sensiolabs.org
THANK YOU.
CONTACT ME
Contact me

javier.eguiluz@gmail.com

linkedin.com/in/javiereguiluz

twitter.com/javiereguiluz

github.com/javiereguiluz

Das könnte Ihnen auch gefallen