4. 4 / 80
Twig is:
—— One of the best template engines for PHP
—— Fast, secure and modern
—— Easy to learn, to read and to write.
—— standalone and easy to implement and
further develop
5. 5 / 80
Twig is:
—— One of the best template engines for PHP
—— Fast, secure and modern
—— Easy to learn, to read and to write.
—— standalone and easy to implement and
further develop
—— If you don’t know Twig yet go to:
http://twig.sensiolabs.org
Twig logo
6. 6 / 80
The short history
Fabien Potencier
(CEO @ SensioLabs)
7. 7 / 80
The short history
Fabien Potencier
(CEO @ SensioLabs)
8. 8 / 80
The short history
Fast
Embeddable
Secure
Sandboxed
“Modern” features
Not tied to only HTML templates
Easily extensible
Syntax similar to Django & Jinja
Clean OO code
Fabien Potencier
(CEO @ SensioLabs)
9. 9 / 80
The short history
Fast
Embeddable
Secure
Sandboxed
“Modern” features
Not tied to only HTML templates
Easily extensible
Syntax similar to Django & Jinja
Clean OO code
Fabien Potencier
(CEO @ SensioLabs)
10. 10 / 80
The short history
Fast
Embeddable
Secure
Sandboxed
“Modern” features
Not tied to only HTML templates
Easily extensible
Syntax similar to Django & Jinja
Clean OO code
Fabien Potencier
(CEO @ SensioLabs)
Armin Ronacher
(Pocoo Team)
11. 11 / 80
the takeover
—— Existing twig was first created out of Jinja fame
(python template engine)
—— it was Not available as a standalone project
—— it was Not maintained or updated
12. 12 / 80
the takeover
—— Existing twig was created of Jinja fame
(python template engine)
—— Not available as a standalone project
—— Not maintained or updated
13. 13 / 80
the takeover
—— Existing twig was created of Jinja fame
(python template engine)
—— Not available as a standalone project
—— Not maintained or updated
14. 14 / 80
the takeover
—— Existing twig was created of Jinja fame
(python template engine)
—— Not available as a standalone project
—— Not maintained or updated
15. 15 / 80
the takeover
—— Existing twig was created of Jinja fame
(python template engine)
—— Not available as a standalone project
—— Not maintained or updated
16. 16 / 80
the takeover
—— Existing twig was created of Jinja fame
(python template engine)
—— Not available as a standalone project
—— Not maintained or updated
18. 18 / 80
Templating system
—— Simple text file which can generate any text-based format
(HTML, XML, CSV, LaTeX, ...)
tags
variables
expressions
19. 19 / 80
Templating system
—— Simple text file which can generate any text-based format
(HTML, XML, CSV, LaTeX, ...)
tags
variables
values
expressions
20. 20 / 80
Templating system
—— Simple text file which can generate any text-based format
(HTML, XML, CSV, LaTeX, ...)
tags
variables
values
logic of template
expressions
23. 23 / 80
Templating system
{# Twig template #}
<ul>
{% for item in items %}
<li>{{ item.value }}</li>
{% endfor %}
<ul>
Hello {{ name|title }}
Categories {{ categories|join(‘, ‘) }}
comment
tag
24. 24 / 80
Templating system
{# Twig template #}
<ul>
{% for item in items %}
<li>{{ item.value }}</li>
{% endfor %}
<ul>
Hello {{ name|title }}
Categories {{ categories|join(‘, ‘) }}
comment
variable
tag
25. 25 / 80
Templating system
{# Twig template #}
<ul>
{% for item in items %}
<li>{{ item.value }}</li>
{% endfor %}
<ul>
Hello {{ name|title }}
Categories {{ categories|join(‘, ‘) }}
comment
variable
filter
tag
26. 26 / 80
Templating system
{# Twig template #}
<ul>
{% for item in items %}
<li>{{ item.value }}</li>
{% endfor %}
<ul>
Hello {{ name|title }}
Categories {{ categories|join(‘, ‘) }}
comment
variable
filter
filter with arguments
tag
27. 27 / 80
TAGS
—— tags are written between delimiters
—— There are two kinds of delimiters:
—— {% ... %} - execute statements such as for-loops
{% block stylesheets %}
<link href=”/css/reset.css” rel=”stylesheet” type=”text/css” />
{% endblock %}
28. 28 / 80
TAGS
—— tags are written between delimiters
—— There are two kinds of delimiters:
—— {% ... %} - execute statements such as for-loops
{% block stylesheets %}
<link href=”/css/reset.css” rel=”stylesheet” type=”text/css” />
{% endblock %}
—— {{ ... }} - prints the result of an expression to the template
{{ i }}
30. 30 / 80
COMMENTs
—— comments are used for internal information (like other designers)
—— It is nice to comment out whole parts of code :)
{# note: disabled template because we no longer use this
{% for user in users %}
...
{% endfor %}
#}
31. 31 / 80
Variables
—— app passes variables to templates
—— Variables may have attributes
—— You can use a dot ( . ) to access attributes
{{ foo.bar }}
32. 32 / 80
Variables
—— app passes variables to templates
—— Variables may have attributes
—— You can use a dot ( . ) to access attributes
{{ foo.bar }}
—— Alternatively you can use the “subscript syntax”
{{ foo[‘bar’] }}
33. 33 / 80
Variables
—— app passes variables to templates
—— Variables may have attributes
—— You can use a dot ( . ) to access attributes
{{ foo.bar }}
—— Alternatively you can use the “subscript syntax”
{{ foo[‘bar’] }}
—— If an attribute contains special characters you use
{{ attribute(foo, ‘data-foo’) }}
34. 34 / 80
Global Variables
—— The following variables are always available in templates:
_self {# references the current template #}
_context {# references the current context #}
_charset {# references the current charset #}
{% set foo = ‘foo’ %}
{% set foo = [1, 2] %}
{% set foo = {‘foo’: ‘bar’} %}
35. 35 / 80
Filters
—— Used to modify variables
—— Separated by a pipe symbol ( | )
—— Multiple filters can be chained
{{ name|striptags|title }} {# remove HTML and title case #}
36. 36 / 80
Filters
—— Used to modify variables
—— Separated by a pipe symbol ( | )
—— Multiple filters can be chained
{{ name|striptags|title }} {# remove HTML and title case #}
—— Filters that accept arguments have parentheses around the
arguments ( () )
{{ list|join(‘, ‘) }} {# list joined by commas #}
37. 37 / 80
Filters
—— Used to modify variables
—— Separated by a pipe symbol ( | )
—— Multiple filters can be chained
{{ name|striptags|title }} {# remove HTML and title case #}
—— Filters that accept arguments have parentheses around the
arguments ( () )
{{ list|join(‘, ‘) }} {# list joined by commas #}
—— To apply filter on a section of code wrap it into filter tag:
{% filter upper %}This text becomes uppercase{% endfilter %}
39. 39 / 80
FOR
—— Loops over each item in a sequence
{% for user in users %}
{{ user.username|e }}
{% endfor %}
40. 40 / 80
FOR
—— Loops over each item in a sequence
{% for user in users %}
{{ user.username|e }}
{% endfor %}
—— If there is a need to iterate over a sequence of numbers
{% for i in 0..10 %}
{{ i }},
{% endfor %}
41. 41 / 80
FOR
—— You can work with english alphabet also :)
{% for letter in ‘a’..’z’ %}
{{ letter }},
{% endfor %}
42. 42 / 80
FOR
—— You can work with english alphabet also :)
{% for letter in ‘a’..’z’ %}
{{ letter }},
{% endfor %}
—— And also, ( .. ) operator can take any expression at both sides
{% for letter in ‘a’|upper..’z’|upper %}
{{ letter }},
{% endfor %}
44. 44 / 80
LOOP VARIABLEs
loop.index
loop.index0
loop.revindex
loop.revindex0
loop.first
loop.last
loop.length
loop.parent
—— When you are inside for loop, you have some special variables :)
{% for user in users %}
{{ loop.index }} - {{ user.username }},
{% endfor %}
45. 45 / 80
FOR / ELSE
—— for can also have else option if sequence is empty
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% else %}
<li><em>no user found</em></li>
{% endfor %}
</ul>
46. 46 / 80
IF / ELSE
—— The if statement in TWIG is comparable with the statements of PHP
{% if online == false %}
<p>Website has been suspended.</p>
{% endif %}
47. 47 / 80
IF / ELSE
—— You can also test if an array is not empty or use loop variables
<ul>
{% if users %}
{% for user in users %}
<li {% if loop.last %}class=”last”{% endif %}>
{{ user.username|e }}</li>
{% endfor %}
{% endif %}
</ul>
48. 48 / 80
Functions
—— functions can be called to generate content
—— Called by their name followed by parentheses ( () )
—— They may have arguments
{# range() function returning a list containing
arithmetic progression of integers #}
{% for i in range(0, 3) %}
{{ i }},
{% endfor %}
50. 50 / 80
Named arguments
—— arguments for filters and functions can be passed
as named arguments
—— More explicit about the meaning of the values
{{ data|convert_encoding(‘UTF-8’, ‘iso-2022-jp’) }}
51. 51 / 80
Named arguments
—— arguments for filters and functions can be passed
as named arguments
—— More explicit about the meaning of the values
{{ data|convert_encoding(‘UTF-8’, ‘iso-2022-jp’) }}
{# versus #}
{{ data|convert_encoding(from=’iso-2022-jp’, to=’UTF-8’) }}
52. 52 / 80
TESTS
—— test can be used to test a variable against a common expression
{{ name is odd }} {# is variable name odd #}
53. 53 / 80
TESTS
—— test can be used to test a variable against a common expression
{{ name is odd }} {# is variable name odd #}
—— They may have arguments
{{ loop.index is divisibleby(3) }} {# is divisible by 3 #}
54. 54 / 80
TESTS
—— test can be used to test a variable against a common expression
{{ name is odd }} {# is variable name odd #}
—— They may have arguments
{{ loop.index is divisibleby(3) }} {# is divisible by 3 #}
—— or can be negated by using is not operator
{{ loop.index is not divisibleby(3) }} {# not divisible by 3 #}
56. 56 / 80
OPERATORS
—— These work similar to PHP
—— Twig allows expressions everywhere
{% set greeting = ‘Hello’ %}
{% for i in 1..10 %}
{% do foo = bar + foobar %}
{% if 1 < foo < 4 and (not bar or foobar) %}
{{ foo + 4 }}
{% endif %}
59. 59 / 80
Benefits of twig
—— Terse syntax and sugar
—— Automatic HTML escaping
—— Sandbox mode, which enables user editable templates.
—— Ability to create custom DSL’s using application tags,
operators and functions.
67. 67 / 80
Macros
—— Comparable with functions in regular programming languages
—— Useful to reuse fragments of template
—— Macros can also have defaults
68. 68 / 80
Macros
—— Comparable with functions in regular programming languages
—— Useful to reuse fragments of template
—— Macros can also have defaults
{# forms.twig #}
{% macro input(name, value, type, size = 20) %}
<input type=”{{ type|default(‘text’) }}”
name=”{{ name }}” value=”{{ value|e }}”
size=”{{ size|default(20) }}”>
{% endmacro %}
69. 69 / 80
Macros
—— They can be defined in any template
—— Need to be imported via the import tag before being used
70. 70 / 80
Macros
—— They can be defined in any template
—— Need to be imported via the import tag before being used
{# inside a template #}
{% import “forms.twig” as forms %}
{{ forms.input(‘username’) }}
{{ forms.input(‘password’, none, ‘password’) }}
71. 71 / 80
Macros
—— You can import individual macro names from a template
—— optionally alias them if needed
72. 72 / 80
Macros
—— You can import individual macro names from a template
—— optionally alias them if needed
{% from ‘forms.html’ import input as input_field %}
<fieldset>
<label>Username</label>
{{ input_field(‘username’) }}
<label>Password</label>
{{ input_field(‘password’, ‘’, ‘password’) }}
</fieldset>
73. 73 / 80
whitespace control
—— The first newline after a template tag is removed automatically
—— Whitespace is not further modified by the template engine
—— Use the spaceless tag to remove whitespace between HTML tags
74. 74 / 80
whitespace control
—— The first newline after a template tag is removed automatically
—— Whitespace is not further modified by the template engine
—— Use the spaceless tag to remove whitespace between HTML tags
{% spaceless %}
<div>
<strong>foo bar</strong>
</div>
{% endspaceless %}
75. 75 / 80
whitespace control
—— The first newline after a template tag is removed automatically
—— Whitespace is not further modified by the template engine
—— Use the spaceless tag to remove whitespace between HTML tags
{% spaceless %}
<div>
<strong>foo bar</strong>
</div>
{% endspaceless %}
{# output will be <div><strong>foo bar</strong></div> #}
76. 76 / 80
includes
—— You can easily include content of another template inside other
{% include ‘sidebar.html.twig’ %}
77. 77 / 80
includes
—— You can easily include content of another template inside other
{% include ‘sidebar.html.twig’ %}
—— Included templates have access to the variables of the active context
{# to disable access to active context, only foo variable #}
{{ include(‘template.html’, {foo: ‘bar’}, with_context = false) }}
78. 78 / 80
includes
—— You can easily include content of another template inside other
{% include ‘sidebar.html.twig’ %}
—— Included templates have access to the variables of the active context
{# to disable access to active context, only foo variable #}
{{ include(‘template.html’, {foo: ‘bar’}, with_context = false) }}
—— Include file can also be included in for-loop
{% for box in boxes %}
{% include “render_box.html” %}
{% endfor %}
79. 79 / 80
Template inheritance
—— Most powerful part of Twig - is template inheritance
—— Build a base “skeleton” template with common elements of your site
—— blocks are like slots or holes in a template, that the child can fill
—— Blocks can be used out of order, which is pretty sweet.
—— Blocks can be imported from other templates
89. 89 / 80
TEMPLATE CHILD
—— extends tag should be the first tag in the template
—— tells the template engine that this template “extends” another one
—— Template system first locates the parent
—— By using the same block NAME - you either override its contents or
you add content to it with parent() function
—— The content of child block can go above or below existing contents
90. 90 / 80
INHERITANCE TIPS
—— Extending only works three levels deep
—— Don’t use {% block %} inside a loop. It doesn’t do what you want.
—— You can use {{ block(name) }} to dynamically call blocks.
91. I mentioned it was secure
and why am I, a designer, telling that to you anyway?
92. 92 / 80
Extending twig
—— Everything in the core can be changed
—— Uses dependency injection
—— The syntax is not hardcoded
—— Built-in extension mechanism for day-to-day enhancements
—— Adding tags
—— Adding filters
—— Adding node transformers
—— Extensions are used for the core features
93. 93 / 80
Extending twig
—— Everything is an extension in the core
—— Twig_Extension_Core:
Provides the core features (tags and filters)
—— Twig_Extension_Escaper:
Adds automatic output-escaping and the possibility
to escape/unescape blocks of code
—— Twig_Extension_Sandbox:
Adds a sandbox mode to the default Twig environment,
making it safe to evaluate untrusted code
94. 94 / 80
extending twig
—— The Core extension is automatically registered
—— Core and user extensions are managed in the same way
$twig = new Twig_Environment($loader);
$twig->addExtension(
new Twig_Extension_Escaper()
);
95. 95 / 80
xss protection
—— Cross-site scripting (XSS) enables attackers to inject
client-side script into Web pages viewed by other users
—— 84% of all security vulnerabilities as of 2007
In TWIG:
—— Everything is done during compilation
—— Nothing is decided on execution
—— No overhead between escaping by hand and auto-escaping
96. 96 / 80
Output escaping
{# Manually escaping variable #}
{{ post.author|escape }}
{# Choose escaping strategy #}
{{ foo|escape(‘js’) }}
{# Escape all variables of the block #}
{% autoescape on %}
{{ post.author }}
{{ post.title|escape }} {# won’t be double-escaped #}
{{ post.html|safe }} {# marked this as safe #}
{% endautoescape %}
{# Automatic #}
{{ post.author }}
{{ post.html|safe }}
97. 97 / 80
when do you need SANDBOX?
—— When you need to evaluate non-trusted code;
a user who can edit templates, typically a webmaster in backend
—— Security is managed by a policy instance
(Twig_Sandbox_SecurityPolicy by default)
—— The code evaluated in a sandbox must respect the policy
—— The default policy works as follows
—— Tags must be explicitly allowed
—— Filters must be explicitly allowed
—— Method calls must be explicitly allowed on a method basis
98. 98 / 80
HOW TO USE SANDBOX?
—— Enable it globally or on a template basis
$sandbox = new Twig_Extension_Sandbox($policy, true);
{% include “user.twig” sandboxed %}
100. 100 / 80
twigrammar
—— Put one (and only one) space after the start of a delimiter
( {{, {%, and {# ) and before the end of a delimiter (}}, %}, and #})
{{ foo }}
{# comment #}
{% if foo %}{% endif %}
101. 101 / 80
twigrammar
—— Put one (and only one) space before and after the following
operators: comparison operators (==, !=, <, >, >=, <=),
math operators (+, -, /, *, %, //, **) logic operators (not, and, or),
~, is, in, and the ternary operator (?:)
{{ 1 + 2 }}
{{ foo ~ bar }}
{{ true ? true : false }}
102. 102 / 80
twigrammar
—— Put one (and only one) space after the ( : ) sign in hashes
and ( , ) in arrays and hashes
{{ [1, 2, 3] }}
{{ {‘foo’: ‘bar’} }}
103. 103 / 80
twigrammar
—— Do not put any spaces after an opening parenthesis
and before a closing parenthesis in expressions
{{ 1 + (2 * 3) }}
104. 104 / 80
twigrammar
—— Do not put any spaces before and after string delimiters
{{ ‘foo’ }}
{{ “foo” }}
105. 105 / 80
twigrammar
—— Do not put any spaces before and after
the following operators: |, ., .., []
{{ foo|upper|lower }}
{{ user.name }}
{{ user[name] }}
{% for i in 1..12 %}{% endfor %}
106. 106 / 80
twigrammar
—— Do not put any spaces before and after
the parenthesis used for filter and function calls
{{ foo|default(‘foo’) }}
{{ range(1..10) }}
107. 107 / 80
twigrammar
—— Do not put any spaces before and after
the opening and the closing of arrays and hashes
{{ [1, 2, 3] }}
{{ {‘foo’: ‘bar’} }}
108. 108 / 80
twigrammar
—— Use lower cased and underscored variable names
{% set foo = ‘foo’ %}
{% set foo_bar = ‘foo’ %}
109. 109 / 80
twigrammar
—— Indent your code inside tags (use the same indentation
as the one used for the main language of the file)
{% block foo %}
{% if true %}
true
{% endif %}
{% endblock %}
123. 123 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘col1_span’: ‘span9’, ‘col2_span’: ‘span3’ } %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
124. 124 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘col1_span’: ‘span9’, ‘col2_span’: ‘span3’ } %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
embed, do it like a pro :)
125. 125 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘col1_span’: ‘span9’, ‘col2_span’: ‘span3’ } %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %} match variables by using with
126. 126 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘col1_span’: ‘span9’, ‘col2_span’: ‘span3’ } %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %} and then fill your content blocks
127. 127 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘col1_span’: ‘span9’, ‘col2_span’: ‘span3’ } %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %} lemme show you one magic trick :)
128. 128 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘col1_span’: ‘span9’, ‘col2_span’: ‘span3’ } %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
allow me to hide this ...
129. 129 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘col1_span’: ‘span9’, ‘col2_span’: ‘span3’ } %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
twig magic in progress ...
130. 130 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘layout’: ‘9_3’ } %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
131. 131 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘layout’: ‘9_3’ } %}
{% set col1_span = layout|split(‘_’)[0:]|join %}
{% set col2_span = layout|split(‘_’)[1:]|join %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
132. 132 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘layout’: ‘9_3’ } %}
{% set col1_span = layout|split(‘_’)[0:]|join %}
{% set col2_span = layout|split(‘_’)[1:]|join %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
optimized
133. 133 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘layout’: ‘9_3’ } %}
{% set col1_span = layout|split(‘_’)[0:]|join %}
{% set col2_span = layout|split(‘_’)[1:]|join %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
optimized
simple
134. 134 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘layout’: ‘9_3’ } %}
{% set col1_span = layout|split(‘_’)[0:]|join %}
{% set col2_span = layout|split(‘_’)[1:]|join %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
optimized
simple
use global variables
135. 135 / 80
2-column grid in practice
{# embedding into our template and using blocks for content #}
{% embed ‘grid_2.twig’ with { ‘layout’: ‘9_3’ } %}
{% set col1_span = layout|split(‘_’)[0:]|join %}
{% set col2_span = layout|split(‘_’)[1:]|join %}
{% block column1 %} {# ... #} {% endblock %}
{% block column2 %} {# ... #} {% endblock %}
{% endembed %}
optimized
simple
use global variables
use filters
136. when to use twig
and do I have to use Symfony?
137. 137 / 80
When to use twig?
—— When users you don’t trust can edit some templates
—— a webmaster can edit the look and feel of some templates
—— When you need to create a simple language for a domain specific
problem like templates of a blog, or of an ecommerce software
138. 138 / 80
When to use twig?
—— When users you don’t trust can edit some templates
—— a webmaster can edit the look and feel of some templates
—— When you need to create a simple language for a domain specific
problem like templates of a blog, or of an ecommerce software
—— Advantages of Twig in such cases
—— You can use as little or as much features as you want
—— Security
—— Sandbox
—— Possibility to create a DSL (a set of pre-defined tags, macros,
and filters specific to the domain)
139. 139 / 80
When to use twig?
—— One of the goals - to provide the architecture that allows the
developer to create its own language
—— Configure the syntax (delimiters)
—— Override all built-in elements (tags, filters, …)
—— Create your own tags, macros, and filters
—— All without having to write complex code
—— The core classes (lexer, parser, and compiler) provides a simple
API to let you do your job without understanding the nitty-gritty
details of the internals
140. 140 / 80
Integration with frameworks
—— Symfony
—— sfTwigPlugin
—— Zend Framework
—— http://svn.twig-project.org/branches/zend_adapter
—— Yii
—— http://www.yiiframework.com/extension/twig-view-renderer
—— Kohana
—— http://github.com/shadowhand/kohana-twig
—— … and more to come
141. thank you for your patience
and if you have any questions, catch me in the lobby :)