commit
92b792e8f2
@ -0,0 +1,9 @@ |
|||||||
|
<IfModule mod_rewrite.c> |
||||||
|
RewriteEngine On |
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f |
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d |
||||||
|
RewriteRule . index.php [L] |
||||||
|
</IfModule> |
||||||
|
|
||||||
|
# Prevent file browsing |
||||||
|
Options -Indexes |
@ -0,0 +1 @@ |
|||||||
|
Pico is a stupidly simple, blazing fast, flat file CMS. See http://pico.dev7studios.com for more info. |
@ -0,0 +1,17 @@ |
|||||||
|
<?php
|
||||||
|
|
||||||
|
/* |
||||||
|
Override any of the default settings below: |
||||||
|
|
||||||
|
$config['site_title'] = 'Pico'; // Site title |
||||||
|
$config['base_url'] = ''; // Override base URL (e.g. http://example.com) |
||||||
|
$config['theme'] = 'default'; // Set the theme (defaults to "default") |
||||||
|
$config['enable_cache'] = false; // Enable caching |
||||||
|
|
||||||
|
To add a custom config setting: |
||||||
|
|
||||||
|
$config['custom_setting'] = 'Hello'; // Can be accessed by {{ config.custom_setting }} in a theme |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
?> |
@ -0,0 +1,9 @@ |
|||||||
|
/* |
||||||
|
Title: Error 404 |
||||||
|
Robots: noindex,nofollow |
||||||
|
*/ |
||||||
|
|
||||||
|
Error 404 |
||||||
|
========= |
||||||
|
|
||||||
|
Woops. Looks like this page doesn't exist. |
@ -0,0 +1,77 @@ |
|||||||
|
/* |
||||||
|
Title: Welcome |
||||||
|
Description: This description will go in the meta description tag |
||||||
|
*/ |
||||||
|
|
||||||
|
Welcome to Pico |
||||||
|
=============== |
||||||
|
|
||||||
|
Congratulations you have successfully installed [Pico](http://pico.dev7studios.com). Pico is a stupidly simple, blazing fast, flat file CMS. |
||||||
|
|
||||||
|
Creating Content |
||||||
|
---------------- |
||||||
|
|
||||||
|
Pico is a flat file CMS, this means there is no administration backend and database to deal with. You simply create `.txt` files in the "content" |
||||||
|
folder and that becomes a page. For example this file is called `index.txt` and is shown as the main landing page. |
||||||
|
|
||||||
|
If you created folder within the content folder (e.g. `content/sub`) and put an `index.txt` inside it, you can access that folder at the URL |
||||||
|
`http://yousite.com/sub`. If you want another page within the sub folder, simply create a text file with the corresponding name (e.g. `content/sub/page.txt`) |
||||||
|
and will be able to access it from the URL `http://yousite.com/sub/page`. Below we've shown some examples of content locations and their corresponing URL's: |
||||||
|
|
||||||
|
<table> |
||||||
|
<thead> |
||||||
|
<tr><th>Physical Location</th><th>URL</th></tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
<tr><td>content/index.txt</td><td>/</td></tr> |
||||||
|
<tr><td>content/sub.txt</td><td>/sub</td></tr> |
||||||
|
<tr><td>content/sub/index.txt</td><td>/sub (same as above)</td></tr> |
||||||
|
<tr><td>content/sub/page.txt</td><td>/sub/page</td></tr> |
||||||
|
<tr><td>content/a/very/long/url.txt</td><td>/a/very/long/url</td></tr> |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
|
||||||
|
If a file cannot be found, the file `content/404.txt` will be shown. |
||||||
|
|
||||||
|
Text File Markup |
||||||
|
---------------- |
||||||
|
|
||||||
|
Text files are marked up using [Markdown](http://daringfireball.net/projects/markdown/syntax). They can also contain regular HTML. |
||||||
|
|
||||||
|
At the top of text files you can place a block comment and specify certain attributes of the page. For example: |
||||||
|
|
||||||
|
/ * |
||||||
|
Title: Welcome |
||||||
|
Description: This description will go in the meta description tag |
||||||
|
Robots: noindex,nofollow |
||||||
|
*/ |
||||||
|
|
||||||
|
These values will be contained in the `{{ meta }}` variable in themes (see below). |
||||||
|
|
||||||
|
There are also certain variables that you can use in your text files: |
||||||
|
|
||||||
|
* %base_url% - The URL to your Pico site |
||||||
|
|
||||||
|
Themes |
||||||
|
------ |
||||||
|
|
||||||
|
You can create themes for your Pico installation and in the "themes" folder. Check out the default theme for an example of a theme. Pico uses |
||||||
|
[Twig](http://twig.sensiolabs.org/documentation) for it's templating engine. You can select your theme by setting the `$config['theme']` variable |
||||||
|
in config.php to your theme folder. |
||||||
|
|
||||||
|
All themes must include an `index.html` file to define the HTML structure of the theme. Below are the Twig variables that are available to use in your theme: |
||||||
|
|
||||||
|
* `{{ config }}` - Conatins the values you set in config.php (e.g. `{{ config.theme }}` = "default") |
||||||
|
* `{{ base_dir }}` - The path to your Pico root directory |
||||||
|
* `{{ base_url }}` - The URL to your Pico site |
||||||
|
* `{{ theme_dir }}` - The path to the Pico active theme direcotry |
||||||
|
* `{{ theme_url }}` - The URL to the Pico active theme direcotry |
||||||
|
* `{{ site_title }}` - Shortcut to the site title (defined in config.php) |
||||||
|
* `{{ meta }}` - Contains the meta values from the current page (e.g. `{{ meta.title }}`, `{{ meta.description }}`, `{{ meta.robots }}`) |
||||||
|
* `{{ content }}` - The content of the current page (after it has been processed through Markdown) |
||||||
|
|
||||||
|
Config |
||||||
|
------ |
||||||
|
|
||||||
|
You can override the default Pico settings (and add your own custom settings) by editing config.php in the root Pico directory. The config.php file |
||||||
|
list all of the settings and their defaults. To override a setting simply uncomment it in config.php and set your custom value. |
@ -0,0 +1,6 @@ |
|||||||
|
/* |
||||||
|
Title: Sub Page Index |
||||||
|
*/ |
||||||
|
|
||||||
|
This is a Sub Page Index |
||||||
|
======================== |
@ -0,0 +1,6 @@ |
|||||||
|
/* |
||||||
|
Title: Sub Page |
||||||
|
*/ |
||||||
|
|
||||||
|
This is a Sub Page |
||||||
|
================== |
@ -0,0 +1,19 @@ |
|||||||
|
<?php |
||||||
|
/* |
||||||
|
* Pico v0.1 |
||||||
|
*/ |
||||||
|
|
||||||
|
// Defines |
||||||
|
define('ROOT_DIR', realpath(dirname(__FILE__)) .'/'); |
||||||
|
define('CONTENT_DIR', ROOT_DIR .'content/'); |
||||||
|
define('LIB_DIR', ROOT_DIR .'lib/'); |
||||||
|
define('THEMES_DIR', ROOT_DIR .'themes/'); |
||||||
|
define('CACHE_DIR', LIB_DIR .'cache/'); |
||||||
|
|
||||||
|
require('config.php'); |
||||||
|
require(LIB_DIR .'markdown.php'); |
||||||
|
require(LIB_DIR .'twig/lib/Twig/Autoloader.php'); |
||||||
|
require(LIB_DIR .'pico.php'); |
||||||
|
$pico = new Pico(); |
||||||
|
|
||||||
|
?> |
@ -0,0 +1,87 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* index.html */ |
||||||
|
class __TwigTemplate_b0360ab98142b2e0bbb248aad053058f extends Twig_Template |
||||||
|
{ |
||||||
|
protected function doDisplay(array $context, array $blocks = array()) |
||||||
|
{ |
||||||
|
// line 1 |
||||||
|
echo "<!DOCTYPE html> |
||||||
|
<html lang=\"en\" class=\"no-js\"> |
||||||
|
<head> |
||||||
|
<meta charset=\"utf-8\" /> |
||||||
|
|
||||||
|
<title>"; |
||||||
|
// line 6 |
||||||
|
if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; } |
||||||
|
if ($this->getAttribute($_meta_, "title")) { |
||||||
|
if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; } |
||||||
|
echo $this->getAttribute($_meta_, "title"); |
||||||
|
echo " | "; |
||||||
|
} |
||||||
|
if (isset($context["site_title"])) { $_site_title_ = $context["site_title"]; } else { $_site_title_ = null; } |
||||||
|
echo $_site_title_; |
||||||
|
echo "</title> |
||||||
|
"; |
||||||
|
// line 7 |
||||||
|
if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; } |
||||||
|
if ($this->getAttribute($_meta_, "description")) { |
||||||
|
echo "<meta name=\"description\" content=\""; |
||||||
|
if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; } |
||||||
|
echo $this->getAttribute($_meta_, "description"); |
||||||
|
echo "\">"; |
||||||
|
} |
||||||
|
// line 8 |
||||||
|
echo " "; |
||||||
|
if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; } |
||||||
|
if ($this->getAttribute($_meta_, "robots")) { |
||||||
|
echo "<meta name=\"robots\" content=\""; |
||||||
|
if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; } |
||||||
|
echo $this->getAttribute($_meta_, "robots"); |
||||||
|
echo "\">"; |
||||||
|
} |
||||||
|
// line 9 |
||||||
|
echo " |
||||||
|
<link rel=\"stylesheet\" href=\""; |
||||||
|
// line 10 |
||||||
|
if (isset($context["theme_url"])) { $_theme_url_ = $context["theme_url"]; } else { $_theme_url_ = null; } |
||||||
|
echo $_theme_url_; |
||||||
|
echo "/style.css\" type=\"text/css\" media=\"screen\" /> |
||||||
|
|
||||||
|
<!--[if IE]> |
||||||
|
\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\"> |
||||||
|
\t<script type=\"text/javascript\" src=\"https://html5shiv.googlecode.com/svn/trunk/html5.js\"></script> |
||||||
|
<![endif]--> |
||||||
|
<script src=\""; |
||||||
|
// line 16 |
||||||
|
if (isset($context["theme_url"])) { $_theme_url_ = $context["theme_url"]; } else { $_theme_url_ = null; } |
||||||
|
echo $_theme_url_; |
||||||
|
echo "/scripts/modernizr-1.7.min.js\"></script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
|
||||||
|
"; |
||||||
|
// line 20 |
||||||
|
if (isset($context["content"])) { $_content_ = $context["content"]; } else { $_content_ = null; } |
||||||
|
echo $_content_; |
||||||
|
echo " |
||||||
|
|
||||||
|
</body> |
||||||
|
</html>"; |
||||||
|
} |
||||||
|
|
||||||
|
public function getTemplateName() |
||||||
|
{ |
||||||
|
return "index.html"; |
||||||
|
} |
||||||
|
|
||||||
|
public function isTraitable() |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public function getDebugInfo() |
||||||
|
{ |
||||||
|
return array ( 63 => 20, 55 => 16, 45 => 10, 42 => 9, 33 => 8, 25 => 7, 14 => 6, 7 => 1,); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,114 @@ |
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Pico { |
||||||
|
|
||||||
|
function __construct() |
||||||
|
{ |
||||||
|
|
||||||
|
|
||||||
|
// Get request url and script url |
||||||
|
$url = ''; |
||||||
|
$request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : ''; |
||||||
|
$script_url = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : ''; |
||||||
|
|
||||||
|
// Get our url path and trim the / of the left and the right |
||||||
|
if($request_url != $script_url) $url = trim(preg_replace('/'. str_replace('/', '\/', str_replace('index.php', '', $script_url)) .'/', '', $request_url, 1), '/'); |
||||||
|
|
||||||
|
// Get the file path |
||||||
|
if($url) $file = strtolower(CONTENT_DIR . $url); |
||||||
|
else $file = CONTENT_DIR .'index'; |
||||||
|
|
||||||
|
// Load the file |
||||||
|
if(is_dir($file)) $file = CONTENT_DIR . $url .'/index.txt'; |
||||||
|
else $file .= '.txt'; |
||||||
|
|
||||||
|
if(file_exists($file)) $content = file_get_contents($file); |
||||||
|
else $content = file_get_contents(CONTENT_DIR .'404.txt'); |
||||||
|
|
||||||
|
$meta = $this->read_file_meta($content); |
||||||
|
$content = preg_replace('#/\*.+?\*/#s', '', $content); // Remove comments and meta |
||||||
|
$content = $this->parse_content($content); |
||||||
|
|
||||||
|
// Load the settings |
||||||
|
$settings = $this->get_config(); |
||||||
|
$env = array('autoescape' => false); |
||||||
|
if($settings['enable_cache']) $env['cache'] = CACHE_DIR; |
||||||
|
|
||||||
|
// Load the theme |
||||||
|
Twig_Autoloader::register(); |
||||||
|
$loader = new Twig_Loader_Filesystem(THEMES_DIR . $settings['theme']); |
||||||
|
$twig = new Twig_Environment($loader, $env); |
||||||
|
echo $twig->render('index.html', array( |
||||||
|
'config' => $settings, |
||||||
|
'base_dir' => rtrim(ROOT_DIR, '/'), |
||||||
|
'base_url' => $settings['base_url'], |
||||||
|
'theme_dir' => THEMES_DIR . $settings['theme'], |
||||||
|
'theme_url' => $settings['base_url'] .'/'. basename(THEMES_DIR) .'/'. $settings['theme'], |
||||||
|
'site_title' => $settings['site_title'], |
||||||
|
'meta' => $meta, |
||||||
|
'content' => $content |
||||||
|
)); |
||||||
|
} |
||||||
|
|
||||||
|
function parse_content($content) |
||||||
|
{ |
||||||
|
$content = str_replace('%base_url%', $this->base_url(), $content); |
||||||
|
$content = Markdown($content); |
||||||
|
|
||||||
|
return $content; |
||||||
|
} |
||||||
|
|
||||||
|
function read_file_meta($content) |
||||||
|
{ |
||||||
|
$headers = array( |
||||||
|
'title' => 'Title', |
||||||
|
'description' => 'Description', |
||||||
|
'robots' => 'Robots' |
||||||
|
); |
||||||
|
|
||||||
|
foreach ($headers as $field => $regex){ |
||||||
|
if (preg_match('/^[ \t\/*#@]*' . preg_quote($regex, '/') . ':(.*)$/mi', $content, $match) && $match[1]){ |
||||||
|
$headers[ $field ] = trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $match[1])); |
||||||
|
} else { |
||||||
|
$headers[ $field ] = ''; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return $headers; |
||||||
|
} |
||||||
|
|
||||||
|
function get_config() |
||||||
|
{ |
||||||
|
global $config; |
||||||
|
|
||||||
|
$defaults = array( |
||||||
|
'site_title' => 'Pico', |
||||||
|
'base_url' => $this->base_url(), |
||||||
|
'theme' => 'default', |
||||||
|
'enable_cache' => false |
||||||
|
); |
||||||
|
|
||||||
|
foreach($defaults as $key=>$val){ |
||||||
|
if(isset($config[$key]) && $config[$key]) $defaults[$key] = $config[$key]; |
||||||
|
} |
||||||
|
|
||||||
|
return $defaults; |
||||||
|
} |
||||||
|
|
||||||
|
function base_url() |
||||||
|
{ |
||||||
|
global $config; |
||||||
|
if(isset($config['base_url']) && $config['base_url']) return $config['base_url']; |
||||||
|
|
||||||
|
$url = ''; |
||||||
|
$request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : ''; |
||||||
|
$script_url = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : ''; |
||||||
|
if($request_url != $script_url) $url = trim(preg_replace('/'. str_replace('/', '\/', str_replace('index.php', '', $script_url)) .'/', '', $request_url, 1), '/'); |
||||||
|
|
||||||
|
$protocol = $_SERVER['HTTPS'] ? "https" : "http"; |
||||||
|
return rtrim(str_replace($url, '', $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']), '/'); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
?> |
@ -0,0 +1,9 @@ |
|||||||
|
Twig is written and maintained by the Twig Team: |
||||||
|
|
||||||
|
Lead Developer: |
||||||
|
|
||||||
|
- Fabien Potencier <fabien.potencier@symfony-project.org> |
||||||
|
|
||||||
|
Project Founder: |
||||||
|
|
||||||
|
- Armin Ronacher <armin.ronacher@active-4.com> |
@ -0,0 +1,474 @@ |
|||||||
|
* 1.6.4 (2012-04-02) |
||||||
|
|
||||||
|
* fixed PHP notice in Twig_Error::guessTemplateLine() introduced in 1.6.3 |
||||||
|
* fixed performance when compiling large files |
||||||
|
* optimized parent template creation when the template does not use dynamic inheritance |
||||||
|
|
||||||
|
* 1.6.3 (2012-03-22) |
||||||
|
|
||||||
|
* fixed usage of Z_ADDREF_P for PHP 5.2 in the C extension |
||||||
|
* fixed compilation of numeric values used in templates when using a locale where the decimal separator is not a dot |
||||||
|
* made the strategy used to guess the real template file name and line number in exception messages much faster and more accurate |
||||||
|
|
||||||
|
* 1.6.2 (2012-03-18) |
||||||
|
|
||||||
|
* fixed sandbox mode when used with inheritance |
||||||
|
* added preserveKeys support for the slice filter |
||||||
|
* fixed the date filter when a DateTime instance is passed with a specific timezone |
||||||
|
* added a trim filter |
||||||
|
|
||||||
|
* 1.6.1 (2012-02-29) |
||||||
|
|
||||||
|
* fixed Twig C extension |
||||||
|
* removed the creation of Twig_Markup instances when not needed |
||||||
|
* added a way to set the default global timezone for dates |
||||||
|
* fixed the slice filter on strings when the length is not specified |
||||||
|
* fixed the creation of the cache directory in case of a race condition |
||||||
|
|
||||||
|
* 1.6.0 (2012-02-04) |
||||||
|
|
||||||
|
* fixed raw blocks when used with the whitespace trim option |
||||||
|
* made a speed optimization to macro calls when imported via the "from" tag |
||||||
|
* fixed globals, parsers, visitors, filters, tests, and functions management in Twig_Environment when a new one or new extension is added |
||||||
|
* fixed the attribute function when passing arguments |
||||||
|
* added slice notation support for the [] operator (syntactic sugar for the slice operator) |
||||||
|
* added a slice filter |
||||||
|
* added string support for the reverse filter |
||||||
|
* fixed the empty test and the length filter for Twig_Markup instances |
||||||
|
* added a date function to ease date comparison |
||||||
|
* fixed unary operators precedence |
||||||
|
* added recursive parsing support in the parser |
||||||
|
* added string and integer handling for the random function |
||||||
|
|
||||||
|
* 1.5.1 (2012-01-05) |
||||||
|
|
||||||
|
* fixed a regression when parsing strings |
||||||
|
|
||||||
|
* 1.5.0 (2012-01-04) |
||||||
|
|
||||||
|
* added Traversable objects support for the join filter |
||||||
|
|
||||||
|
* 1.5.0-RC2 (2011-12-30) |
||||||
|
|
||||||
|
* added a way to set the default global date interval format |
||||||
|
* fixed the date filter for DateInterval instances (setTimezone() does not exist for them) |
||||||
|
* refactored Twig_Template::display() to ease its extension |
||||||
|
* added a number_format filter |
||||||
|
|
||||||
|
* 1.5.0-RC1 (2011-12-26) |
||||||
|
|
||||||
|
* removed the need to quote hash keys |
||||||
|
* allowed hash keys to be any expression |
||||||
|
* added a do tag |
||||||
|
* added a flush tag |
||||||
|
* added support for dynamically named filters and functions |
||||||
|
* added a dump function to help debugging templates |
||||||
|
* added a nl2br filter |
||||||
|
* added a random function |
||||||
|
* added a way to change the default format for the date filter |
||||||
|
* fixed the lexer when an operator ending with a letter ends a line |
||||||
|
* added string interpolation support |
||||||
|
* enhanced exceptions for unknown filters, functions, tests, and tags |
||||||
|
|
||||||
|
* 1.4.0 (2011-12-07) |
||||||
|
|
||||||
|
* fixed lexer when using big numbers (> PHP_INT_MAX) |
||||||
|
* added missing preserveKeys argument to the reverse filter |
||||||
|
* fixed macros containing filter tag calls |
||||||
|
|
||||||
|
* 1.4.0-RC2 (2011-11-27) |
||||||
|
|
||||||
|
* removed usage of Reflection in Twig_Template::getAttribute() |
||||||
|
* added a C extension that can optionally replace Twig_Template::getAttribute() |
||||||
|
* added negative timestamp support to the date filter |
||||||
|
|
||||||
|
* 1.4.0-RC1 (2011-11-20) |
||||||
|
|
||||||
|
* optimized variable access when using PHP 5.4 |
||||||
|
* changed the precedence of the .. operator to be more consistent with languages that implements such a feature like Ruby |
||||||
|
* added an Exception to Twig_Loader_Array::isFresh() method when the template does not exist to be consistent with other loaders |
||||||
|
* added Twig_Function_Node to allow more complex functions to have their own Node class |
||||||
|
* added Twig_Filter_Node to allow more complex filters to have their own Node class |
||||||
|
* added Twig_Test_Node to allow more complex tests to have their own Node class |
||||||
|
* added a better error message when a template is empty but contain a BOM |
||||||
|
* fixed "in" operator for empty strings |
||||||
|
* fixed the "defined" test and the "default" filter (now works with more than one call (foo.bar.foo) and for both values of the strict_variables option) |
||||||
|
* changed the way extensions are loaded (addFilter/addFunction/addGlobal/addTest/addNodeVisitor/addTokenParser/addExtension can now be called in any order) |
||||||
|
* added Twig_Environment::display() |
||||||
|
* made the escape filter smarter when the encoding is not supported by PHP |
||||||
|
* added a convert_encoding filter |
||||||
|
* moved all node manipulations outside the compile() Node method |
||||||
|
* made several speed optimizations |
||||||
|
|
||||||
|
* 1.3.0 (2011-10-08) |
||||||
|
|
||||||
|
no changes |
||||||
|
|
||||||
|
* 1.3.0-RC1 (2011-10-04) |
||||||
|
|
||||||
|
* added an optimization for the parent() function |
||||||
|
* added cache reloading when auto_reload is true and an extension has been modified |
||||||
|
* added the possibility to force the escaping of a string already marked as safe (instance of Twig_Markup) |
||||||
|
* allowed empty templates to be used as traits |
||||||
|
* added traits support for the "parent" function |
||||||
|
|
||||||
|
* 1.2.0 (2011-09-13) |
||||||
|
|
||||||
|
no changes |
||||||
|
|
||||||
|
* 1.2.0-RC1 (2011-09-10) |
||||||
|
|
||||||
|
* enhanced the exception when a tag remains unclosed |
||||||
|
* added support for empty Countable objects for the "empty" test |
||||||
|
* fixed algorithm that determines if a template using inheritance is valid (no output between block definitions) |
||||||
|
* added better support for encoding problems when escaping a string (available as of PHP 5.4) |
||||||
|
* added a way to ignore a missing template when using the "include" tag ({% include "foo" ignore missing %}) |
||||||
|
* added support for an array of templates to the "include" and "extends" tags ({% include ['foo', 'bar'] %}) |
||||||
|
* added support for bitwise operators in expressions |
||||||
|
* added the "attribute" function to allow getting dynamic attributes on variables |
||||||
|
* added Twig_Loader_Chain |
||||||
|
* added Twig_Loader_Array::setTemplate() |
||||||
|
* added an optimization for the set tag when used to capture a large chunk of static text |
||||||
|
* changed name regex to match PHP one "[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*" (works for blocks, tags, functions, filters, and macros) |
||||||
|
* removed the possibility to use the "extends" tag from a block |
||||||
|
* added "if" modifier support to "for" loops |
||||||
|
|
||||||
|
* 1.1.2 (2011-07-30) |
||||||
|
|
||||||
|
* fixed json_encode filter on PHP 5.2 |
||||||
|
* fixed regression introduced in 1.1.1 ({{ block(foo|lower) }}) |
||||||
|
* fixed inheritance when using conditional parents |
||||||
|
* fixed compilation of templates when the body of a child template is not empty |
||||||
|
* fixed output when a macro throws an exception |
||||||
|
* fixed a parsing problem when a large chunk of text is enclosed in a comment tag |
||||||
|
* added PHPDoc for all Token parsers and Core extension functions |
||||||
|
|
||||||
|
* 1.1.1 (2011-07-17) |
||||||
|
|
||||||
|
* added a performance optimization in the Optimizer (also helps to lower the number of nested level calls) |
||||||
|
* made some performance improvement for some edge cases |
||||||
|
|
||||||
|
* 1.1.0 (2011-06-28) |
||||||
|
|
||||||
|
* fixed json_encode filter |
||||||
|
|
||||||
|
* 1.1.0-RC3 (2011-06-24) |
||||||
|
|
||||||
|
* fixed method case-sensitivity when using the sandbox mode |
||||||
|
* added timezone support for the date filter |
||||||
|
* fixed possible security problems with NUL bytes |
||||||
|
|
||||||
|
* 1.1.0-RC2 (2011-06-16) |
||||||
|
|
||||||
|
* added an exception when the template passed to "use" is not a string |
||||||
|
* made 'a.b is defined' not throw an exception if a is not defined (in strict mode) |
||||||
|
* added {% line \d+ %} directive |
||||||
|
|
||||||
|
* 1.1.0-RC1 (2011-05-28) |
||||||
|
|
||||||
|
Flush your cache after upgrading. |
||||||
|
|
||||||
|
* fixed date filter when using a timestamp |
||||||
|
* fixed the defined test for some cases |
||||||
|
* fixed a parsing problem when a large chunk of text is enclosed in a raw tag |
||||||
|
* added support for horizontal reuse of template blocks (see docs for more information) |
||||||
|
* added whitespace control modifier to all tags (see docs for more information) |
||||||
|
* added null as an alias for none (the null test is also an alias for the none test now) |
||||||
|
* made TRUE, FALSE, NONE equivalent to their lowercase counterparts |
||||||
|
* wrapped all compilation and runtime exceptions with Twig_Error_Runtime and added logic to guess the template name and line |
||||||
|
* moved display() method to Twig_Template (generated templates should now use doDisplay() instead) |
||||||
|
|
||||||
|
* 1.0.0 (2011-03-27) |
||||||
|
|
||||||
|
* fixed output when using mbstring |
||||||
|
* fixed duplicate call of methods when using the sandbox |
||||||
|
* made the charset configurable for the escape filter |
||||||
|
|
||||||
|
* 1.0.0-RC2 (2011-02-21) |
||||||
|
|
||||||
|
* changed the way {% set %} works when capturing (the content is now marked as safe) |
||||||
|
* added support for macro name in the endmacro tag |
||||||
|
* make Twig_Error compatible with PHP 5.3.0 > |
||||||
|
* fixed an infinite loop on some Windows configurations |
||||||
|
* fixed the "length" filter for numbers |
||||||
|
* fixed Template::getAttribute() as properties in PHP are case sensitive |
||||||
|
* removed coupling between Twig_Node and Twig_Template |
||||||
|
* fixed the ternary operator precedence rule |
||||||
|
|
||||||
|
* 1.0.0-RC1 (2011-01-09) |
||||||
|
|
||||||
|
Backward incompatibilities: |
||||||
|
|
||||||
|
* the "items" filter, which has been deprecated for quite a long time now, has been removed |
||||||
|
* the "range" filter has been converted to a function: 0|range(10) -> range(0, 10) |
||||||
|
* the "constant" filter has been converted to a function: {{ some_date|date('DATE_W3C'|constant) }} -> {{ some_date|date(constant('DATE_W3C')) }} |
||||||
|
* the "cycle" filter has been converted to a function: {{ ['odd', 'even']|cycle(i) }} -> {{ cycle(['odd', 'even'], i) }} |
||||||
|
* the "for" tag does not support "joined by" anymore |
||||||
|
* the "autoescape" first argument is now "true"/"false" (instead of "on"/"off") |
||||||
|
* the "parent" tag has been replaced by a "parent" function ({{ parent() }} instead of {% parent %}) |
||||||
|
* the "display" tag has been replaced by a "block" function ({{ block('title') }} instead of {% display title %}) |
||||||
|
* removed the grammar and simple token parser (moved to the Twig Extensions repository) |
||||||
|
|
||||||
|
Changes: |
||||||
|
|
||||||
|
* added "needs_context" option for filters and functions (the context is then passed as a first argument) |
||||||
|
* added global variables support |
||||||
|
* made macros return their value instead of echoing directly (fixes calling a macro in sandbox mode) |
||||||
|
* added the "from" tag to import macros as functions |
||||||
|
* added support for functions (a function is just syntactic sugar for a getAttribute() call) |
||||||
|
* made macros callable when sandbox mode is enabled |
||||||
|
* added an exception when a macro uses a reserved name |
||||||
|
* the "default" filter now uses the "empty" test instead of just checking for null |
||||||
|
* added the "empty" test |
||||||
|
|
||||||
|
* 0.9.10 (2010-12-16) |
||||||
|
|
||||||
|
Backward incompatibilities: |
||||||
|
|
||||||
|
* The Escaper extension is enabled by default, which means that all displayed |
||||||
|
variables are now automatically escaped. You can revert to the previous |
||||||
|
behavior by removing the extension via $env->removeExtension('escaper') |
||||||
|
or just set the 'autoescape' option to 'false'. |
||||||
|
* removed the "without loop" attribute for the "for" tag (not needed anymore |
||||||
|
as the Optimizer take care of that for most cases) |
||||||
|
* arrays and hashes have now a different syntax |
||||||
|
* arrays keep the same syntax with square brackets: [1, 2] |
||||||
|
* hashes now use curly braces (["a": "b"] should now be written as {"a": "b"}) |
||||||
|
* support for "arrays with keys" and "hashes without keys" is not supported anymore ([1, "foo": "bar"] or {"foo": "bar", 1}) |
||||||
|
* the i18n extension is now part of the Twig Extensions repository |
||||||
|
|
||||||
|
Changes: |
||||||
|
|
||||||
|
* added the merge filter |
||||||
|
* removed 'is_escaper' option for filters (a left over from the previous version) -- you must use 'is_safe' now instead |
||||||
|
* fixed usage of operators as method names (like is, in, and not) |
||||||
|
* changed the order of execution for node visitors |
||||||
|
* fixed default() filter behavior when used with strict_variables set to on |
||||||
|
* fixed filesystem loader compatibility with PHAR files |
||||||
|
* enhanced error messages when an unexpected token is parsed in an expression |
||||||
|
* fixed filename not being added to syntax error messages |
||||||
|
* added the autoescape option to enable/disable autoescaping |
||||||
|
* removed the newline after a comment (mimicks PHP behavior) |
||||||
|
* added a syntax error exception when parent block is used on a template that does not extend another one |
||||||
|
* made the Escaper extension enabled by default |
||||||
|
* fixed sandbox extension when used with auto output escaping |
||||||
|
* fixed escaper when wrapping a Twig_Node_Print (the original class must be preserved) |
||||||
|
* added an Optimizer extension (enabled by default; optimizes "for" loops and "raw" filters) |
||||||
|
* added priority to node visitors |
||||||
|
|
||||||
|
* 0.9.9 (2010-11-28) |
||||||
|
|
||||||
|
Backward incompatibilities: |
||||||
|
* the self special variable has been renamed to _self |
||||||
|
* the odd and even filters are now tests: |
||||||
|
{{ foo|odd }} must now be written {{ foo is odd }} |
||||||
|
* the "safe" filter has been renamed to "raw" |
||||||
|
* in Node classes, |
||||||
|
sub-nodes are now accessed via getNode() (instead of property access) |
||||||
|
attributes via getAttribute() (instead of array access) |
||||||
|
* the urlencode filter had been renamed to url_encode |
||||||
|
* the include tag now merges the passed variables with the current context by default |
||||||
|
(the old behavior is still possible by adding the "only" keyword) |
||||||
|
* moved Exceptions to Twig_Error_* (Twig_SyntaxError/Twig_RuntimeError are now Twig_Error_Syntax/Twig_Error_Runtime) |
||||||
|
* removed support for {{ 1 < i < 3 }} (use {{ i > 1 and i < 3 }} instead) |
||||||
|
* the "in" filter has been removed ({{ a|in(b) }} should now be written {{ a in b }}) |
||||||
|
|
||||||
|
Changes: |
||||||
|
* added file and line to Twig_Error_Runtime exceptions thrown from Twig_Template |
||||||
|
* changed trans tag to accept any variable for the plural count |
||||||
|
* fixed sandbox mode (__toString() method check was not enforced if called implicitly from complex statements) |
||||||
|
* added the ** (power) operator |
||||||
|
* changed the algorithm used for parsing expressions |
||||||
|
* added the spaceless tag |
||||||
|
* removed trim_blocks option |
||||||
|
* added support for is*() methods for attributes (foo.bar now looks for foo->getBar() or foo->isBar()) |
||||||
|
* changed all exceptions to extend Twig_Error |
||||||
|
* fixed unary expressions ({{ not(1 or 0) }}) |
||||||
|
* fixed child templates (with an extend tag) that uses one or more imports |
||||||
|
* added support for {{ 1 not in [2, 3] }} (more readable than the current {{ not (1 in [2, 3]) }}) |
||||||
|
* escaping has been rewritten |
||||||
|
* the implementation of template inheritance has been rewritten |
||||||
|
(blocks can now be called individually and still work with inheritance) |
||||||
|
* fixed error handling for if tag when a syntax error occurs within a subparse process |
||||||
|
* added a way to implement custom logic for resolving token parsers given a tag name |
||||||
|
* fixed js escaper to be stricter (now uses a whilelist-based js escaper) |
||||||
|
* added the following filers: "constant", "trans", "replace", "json_encode" |
||||||
|
* added a "constant" test |
||||||
|
* fixed objects with __toString() not being autoescaped |
||||||
|
* fixed subscript expressions when calling __call() (methods now keep the case) |
||||||
|
* added "test" feature (accessible via the "is" operator) |
||||||
|
* removed the debug tag (should be done in an extension) |
||||||
|
* fixed trans tag when no vars are used in plural form |
||||||
|
* fixed race condition when writing template cache |
||||||
|
* added the special _charset variable to reference the current charset |
||||||
|
* added the special _context variable to reference the current context |
||||||
|
* renamed self to _self (to avoid conflict) |
||||||
|
* fixed Twig_Template::getAttribute() for protected properties |
||||||
|
|
||||||
|
* 0.9.8 (2010-06-28) |
||||||
|
|
||||||
|
Backward incompatibilities: |
||||||
|
* the trans tag plural count is now attached to the plural tag: |
||||||
|
old: `{% trans count %}...{% plural %}...{% endtrans %}` |
||||||
|
new: `{% trans %}...{% plural count %}...{% endtrans %}` |
||||||
|
|
||||||
|
* added a way to translate strings coming from a variable ({% trans var %}) |
||||||
|
* fixed trans tag when used with the Escaper extension |
||||||
|
* fixed default cache umask |
||||||
|
* removed Twig_Template instances from the debug tag output |
||||||
|
* fixed objects with __isset() defined |
||||||
|
* fixed set tag when used with a capture |
||||||
|
* fixed type hinting for Twig_Environment::addFilter() method |
||||||
|
|
||||||
|
* 0.9.7 (2010-06-12) |
||||||
|
|
||||||
|
Backward incompatibilities: |
||||||
|
* changed 'as' to '=' for the set tag ({% set title as "Title" %} must now be {% set title = "Title" %}) |
||||||
|
* removed the sandboxed attribute of the include tag (use the new sandbox tag instead) |
||||||
|
* refactored the Node system (if you have custom nodes, you will have to update them to use the new API) |
||||||
|
|
||||||
|
* added self as a special variable that refers to the current template (useful for importing macros from the current template) |
||||||
|
* added Twig_Template instance support to the include tag |
||||||
|
* added support for dynamic and conditional inheritance ({% extends some_var %} and {% extends standalone ? "minimum" : "base" %}) |
||||||
|
* added a grammar sub-framework to ease the creation of custom tags |
||||||
|
* fixed the for tag for large arrays (some loop variables are now only available for arrays and objects that implement the Countable interface) |
||||||
|
* removed the Twig_Resource::resolveMissingFilter() method |
||||||
|
* fixed the filter tag which did not apply filtering to included files |
||||||
|
* added a bunch of unit tests |
||||||
|
* added a bunch of phpdoc |
||||||
|
* added a sandbox tag in the sandbox extension |
||||||
|
* changed the date filter to support any date format supported by DateTime |
||||||
|
* added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default) |
||||||
|
* added the lexer, parser, and compiler as arguments to the Twig_Environment constructor |
||||||
|
* changed the cache option to only accepts an explicit path to a cache directory or false |
||||||
|
* added a way to add token parsers, filters, and visitors without creating an extension |
||||||
|
* added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface |
||||||
|
* changed the generated code to match the new coding standards |
||||||
|
* fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }}) |
||||||
|
* added an exception when a child template has a non-empty body (as it is always ignored when rendering) |
||||||
|
|
||||||
|
* 0.9.6 (2010-05-12) |
||||||
|
|
||||||
|
* fixed variables defined outside a loop and for which the value changes in a for loop |
||||||
|
* fixed the test suite for PHP 5.2 and older versions of PHPUnit |
||||||
|
* added support for __call() in expression resolution |
||||||
|
* fixed node visiting for macros (macros are now visited by visitors as any other node) |
||||||
|
* fixed nested block definitions with a parent call (rarely useful but nonetheless supported now) |
||||||
|
* added the cycle filter |
||||||
|
* fixed the Lexer when mbstring.func_overload is used with an mbstring.internal_encoding different from ASCII |
||||||
|
* added a long-syntax for the set tag ({% set foo %}...{% endset %}) |
||||||
|
* unit tests are now powered by PHPUnit |
||||||
|
* added support for gettext via the `i18n` extension |
||||||
|
* fixed twig_capitalize_string_filter() and fixed twig_length_filter() when used with UTF-8 values |
||||||
|
* added a more useful exception if an if tag is not closed properly |
||||||
|
* added support for escaping strategy in the autoescape tag |
||||||
|
* fixed lexer when a template has a big chunk of text between/in a block |
||||||
|
|
||||||
|
* 0.9.5 (2010-01-20) |
||||||
|
|
||||||
|
As for any new release, don't forget to remove all cached templates after |
||||||
|
upgrading. |
||||||
|
|
||||||
|
If you have defined custom filters, you MUST upgrade them for this release. To |
||||||
|
upgrade, replace "array" with "new Twig_Filter_Function", and replace the |
||||||
|
environment constant by the "needs_environment" option: |
||||||
|
|
||||||
|
// before |
||||||
|
'even' => array('twig_is_even_filter', false), |
||||||
|
'escape' => array('twig_escape_filter', true), |
||||||
|
|
||||||
|
// after |
||||||
|
'even' => new Twig_Filter_Function('twig_is_even_filter'), |
||||||
|
'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)), |
||||||
|
|
||||||
|
If you have created NodeTransformer classes, you will need to upgrade them to |
||||||
|
the new interface (please note that the interface is not yet considered |
||||||
|
stable). |
||||||
|
|
||||||
|
* fixed list nodes that did not extend the Twig_NodeListInterface |
||||||
|
* added the "without loop" option to the for tag (it disables the generation of the loop variable) |
||||||
|
* refactored node transformers to node visitors |
||||||
|
* fixed automatic-escaping for blocks |
||||||
|
* added a way to specify variables to pass to an included template |
||||||
|
* changed the automatic-escaping rules to be more sensible and more configurable in custom filters (the documentation lists all the rules) |
||||||
|
* improved the filter system to allow object methods to be used as filters |
||||||
|
* changed the Array and String loaders to actually make use of the cache mechanism |
||||||
|
* included the default filter function definitions in the extension class files directly (Core, Escaper) |
||||||
|
* added the // operator (like the floor() PHP function) |
||||||
|
* added the .. operator (as a syntactic sugar for the range filter when the step is 1) |
||||||
|
* added the in operator (as a syntactic sugar for the in filter) |
||||||
|
* added the following filters in the Core extension: in, range |
||||||
|
* added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes) |
||||||
|
* enhanced some error messages to provide better feedback in case of parsing errors |
||||||
|
|
||||||
|
* 0.9.4 (2009-12-02) |
||||||
|
|
||||||
|
If you have custom loaders, you MUST upgrade them for this release: The |
||||||
|
Twig_Loader base class has been removed, and the Twig_LoaderInterface has also |
||||||
|
been changed (see the source code for more information or the documentation). |
||||||
|
|
||||||
|
* added support for DateTime instances for the date filter |
||||||
|
* fixed loop.last when the array only has one item |
||||||
|
* made it possible to insert newlines in tag and variable blocks |
||||||
|
* fixed a bug when a literal '\n' were present in a template text |
||||||
|
* fixed bug when the filename of a template contains */ |
||||||
|
* refactored loaders |
||||||
|
|
||||||
|
* 0.9.3 (2009-11-11) |
||||||
|
|
||||||
|
This release is NOT backward compatible with the previous releases. |
||||||
|
|
||||||
|
The loaders do not take the cache and autoReload arguments anymore. Instead, |
||||||
|
the Twig_Environment class has two new options: cache and auto_reload. |
||||||
|
Upgrading your code means changing this kind of code: |
||||||
|
|
||||||
|
$loader = new Twig_Loader_Filesystem('/path/to/templates', '/path/to/compilation_cache', true); |
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
|
||||||
|
to something like this: |
||||||
|
|
||||||
|
$loader = new Twig_Loader_Filesystem('/path/to/templates'); |
||||||
|
$twig = new Twig_Environment($loader, array( |
||||||
|
'cache' => '/path/to/compilation_cache', |
||||||
|
'auto_reload' => true, |
||||||
|
)); |
||||||
|
|
||||||
|
* deprecated the "items" filter as it is not needed anymore |
||||||
|
* made cache and auto_reload options of Twig_Environment instead of arguments of Twig_Loader |
||||||
|
* optimized template loading speed |
||||||
|
* removed output when an error occurs in a template and render() is used |
||||||
|
* made major speed improvements for loops (up to 300% on even the smallest loops) |
||||||
|
* added properties as part of the sandbox mode |
||||||
|
* added public properties support (obj.item can now be the item property on the obj object) |
||||||
|
* extended set tag to support expression as value ({% set foo as 'foo' ~ 'bar' %} ) |
||||||
|
* fixed bug when \ was used in HTML |
||||||
|
|
||||||
|
* 0.9.2 (2009-10-29) |
||||||
|
|
||||||
|
* made some speed optimizations |
||||||
|
* changed the cache extension to .php |
||||||
|
* added a js escaping strategy |
||||||
|
* added support for short block tag |
||||||
|
* changed the filter tag to allow chained filters |
||||||
|
* made lexer more flexible as you can now change the default delimiters |
||||||
|
* added set tag |
||||||
|
* changed default directory permission when cache dir does not exist (more secure) |
||||||
|
* added macro support |
||||||
|
* changed filters first optional argument to be a Twig_Environment instance instead of a Twig_Template instance |
||||||
|
* made Twig_Autoloader::autoload() a static method |
||||||
|
* avoid writing template file if an error occurs |
||||||
|
* added $ escaping when outputting raw strings |
||||||
|
* enhanced some error messages to ease debugging |
||||||
|
* fixed empty cache files when the template contains an error |
||||||
|
|
||||||
|
* 0.9.1 (2009-10-14) |
||||||
|
|
||||||
|
* fixed a bug in PHP 5.2.6 |
||||||
|
* fixed numbers with one than one decimal |
||||||
|
* added support for method calls with arguments ({{ foo.bar('a', 43) }}) |
||||||
|
* made small speed optimizations |
||||||
|
* made minor tweaks to allow better extensibility and flexibility |
||||||
|
|
||||||
|
* 0.9.0 (2009-10-12) |
||||||
|
|
||||||
|
* Initial release |
@ -0,0 +1,31 @@ |
|||||||
|
Copyright (c) 2009 by the Twig Team, see AUTHORS for more details. |
||||||
|
|
||||||
|
Some rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions are |
||||||
|
met: |
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
|
||||||
|
* The names of the contributors may not be used to endorse or |
||||||
|
promote products derived from this software without specific |
||||||
|
prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,15 @@ |
|||||||
|
Twig, the flexible, fast, and secure template language for PHP |
||||||
|
============================================================== |
||||||
|
|
||||||
|
Twig is a template language for PHP, released under the new BSD license (code |
||||||
|
and documentation). |
||||||
|
|
||||||
|
Twig uses a syntax similar to the Django and Jinja template languages which |
||||||
|
inspired the Twig runtime environment. |
||||||
|
|
||||||
|
More Information |
||||||
|
---------------- |
||||||
|
|
||||||
|
Read the [documentation][1] for more information. |
||||||
|
|
||||||
|
[1]: http://twig.sensiolabs.org/documentation |
@ -0,0 +1,42 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
if (!isset($argv[1])) |
||||||
|
{ |
||||||
|
die('You must provide the version (1.0.0)'); |
||||||
|
} |
||||||
|
|
||||||
|
if (!isset($argv[2])) |
||||||
|
{ |
||||||
|
die('You must provide the stability (alpha, beta, or stable)'); |
||||||
|
} |
||||||
|
|
||||||
|
$context = array( |
||||||
|
'date' => date('Y-m-d'), |
||||||
|
'time' => date('H:m:00'), |
||||||
|
'version' => $argv[1], |
||||||
|
'api_version' => $argv[1], |
||||||
|
'stability' => $argv[2], |
||||||
|
'api_stability' => $argv[2], |
||||||
|
); |
||||||
|
|
||||||
|
$context['files'] = ''; |
||||||
|
$path = realpath(dirname(__FILE__).'/../lib/Twig'); |
||||||
|
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::LEAVES_ONLY) as $file) |
||||||
|
{ |
||||||
|
if (preg_match('/\.php$/', $file)) |
||||||
|
{ |
||||||
|
$name = str_replace($path.'/', '', $file); |
||||||
|
$context['files'] .= ' <file install-as="Twig/'.$name.'" name="'.$name.'" role="php" />'."\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$template = file_get_contents(dirname(__FILE__).'/../package.xml.tpl'); |
||||||
|
$content = preg_replace_callback('/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/', 'replace_parameters', $template); |
||||||
|
file_put_contents(dirname(__FILE__).'/../package.xml', $content); |
||||||
|
|
||||||
|
function replace_parameters($matches) |
||||||
|
{ |
||||||
|
global $context; |
||||||
|
|
||||||
|
return isset($context[$matches[1]]) ? $context[$matches[1]] : null; |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
{ |
||||||
|
"name": "twig/twig", |
||||||
|
"type": "library", |
||||||
|
"description": "Twig, the flexible, fast, and secure template language for PHP", |
||||||
|
"keywords": ["templating"], |
||||||
|
"homepage": "http://twig.sensiolabs.org", |
||||||
|
"version": "1.6.4", |
||||||
|
"license": "BSD", |
||||||
|
"authors": [ |
||||||
|
{ |
||||||
|
"name": "Fabien Potencier", |
||||||
|
"email": "fabien@symfony.com" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Armin Ronacher", |
||||||
|
"email": "armin.ronacher@active-4.com" |
||||||
|
} |
||||||
|
], |
||||||
|
"require": { |
||||||
|
"php": ">=5.2.4" |
||||||
|
}, |
||||||
|
"autoload": { |
||||||
|
"psr-0" : { |
||||||
|
"Twig_" : "lib/" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,489 @@ |
|||||||
|
Extending Twig |
||||||
|
============== |
||||||
|
|
||||||
|
Twig can be extended in many ways; you can add extra tags, filters, tests, |
||||||
|
operators, global variables, and functions. You can even extend the parser |
||||||
|
itself with node visitors. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
This chapter describes how to extend Twig easily. If you want to reuse |
||||||
|
your changes in different projects or if you want to share them with |
||||||
|
others, you should then create an extension as described in the next |
||||||
|
chapter. |
||||||
|
|
||||||
|
Before extending Twig, you must understand the differences between all the |
||||||
|
different possible extension points and when to use them. |
||||||
|
|
||||||
|
First, remember that Twig has two main language constructs: |
||||||
|
|
||||||
|
* ``{{ }}``: used to print the result of an expression evaluation; |
||||||
|
|
||||||
|
* ``{% %}``: used to execute statements. |
||||||
|
|
||||||
|
To understand why Twig exposes so many extension points, let's see how to |
||||||
|
implement a *Lorem ipsum* generator (it needs to know the number of words to |
||||||
|
generate). |
||||||
|
|
||||||
|
You can use a ``lipsum`` *tag*: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% lipsum 40 %} |
||||||
|
|
||||||
|
That works, but using a tag for ``lipsum`` is not a good idea for at least |
||||||
|
three main reasons: |
||||||
|
|
||||||
|
* ``lipsum`` is not a language construct; |
||||||
|
* The tag outputs something; |
||||||
|
* The tag is not flexible as you cannot use it in an expression: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }} |
||||||
|
|
||||||
|
In fact, you rarely need to create tags; and that's good news because tags are |
||||||
|
the most complex extension point of Twig. |
||||||
|
|
||||||
|
Now, let's use a ``lipsum`` *filter*: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 40|lipsum }} |
||||||
|
|
||||||
|
Again, it works, but it looks weird. A filter transforms the passed value to |
||||||
|
something else but here we use the value to indicate the number of words to |
||||||
|
generate. |
||||||
|
|
||||||
|
Next, let's use a ``lipsum`` *function*: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ lipsum(40) }} |
||||||
|
|
||||||
|
Here we go. For this specific example, the creation of a function is the |
||||||
|
extension point to use. And you can use it anywhere an expression is accepted: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 'some text' ~ ipsum(40) ~ 'some more text' }} |
||||||
|
|
||||||
|
{% set ipsum = ipsum(40) %} |
||||||
|
|
||||||
|
Last but not the least, you can also use a *global* object with a method able |
||||||
|
to generate lorem ipsum text: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ text.lipsum(40) }} |
||||||
|
|
||||||
|
As a rule of thumb, use functions for frequently used features and global |
||||||
|
objects for everything else. |
||||||
|
|
||||||
|
Keep in mind the following when you want to extend Twig: |
||||||
|
|
||||||
|
========== ========================== ========== ========================= |
||||||
|
What? Implementation difficulty? How often? When? |
||||||
|
========== ========================== ========== ========================= |
||||||
|
*macro* trivial frequent Content generation |
||||||
|
*global* trivial frequent Helper object |
||||||
|
*function* trivial frequent Content generation |
||||||
|
*filter* trivial frequent Value transformation |
||||||
|
*tag* complex rare DSL language construct |
||||||
|
*test* trivial rare Boolean decision |
||||||
|
*operator* trivial rare Values transformation |
||||||
|
========== ========================== ========== ========================= |
||||||
|
|
||||||
|
Globals |
||||||
|
------- |
||||||
|
|
||||||
|
A global variable is like any other template variable, except that it's |
||||||
|
available in all templates and macros:: |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->addGlobal('text', new Text()); |
||||||
|
|
||||||
|
You can then use the ``text`` variable anywhere in a template: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ text.lipsum(40) }} |
||||||
|
|
||||||
|
Filters |
||||||
|
------- |
||||||
|
|
||||||
|
A filter is a regular PHP function or an object method that takes the left |
||||||
|
side of the filter (before the pipe ``|``) as first argument and the extra |
||||||
|
arguments passed to the filter (within parentheses ``()``) as extra arguments. |
||||||
|
|
||||||
|
Defining a filter is as easy as associating the filter name with a PHP |
||||||
|
callable. For instance, let's say you have the following code in a template: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 'TWIG'|lower }} |
||||||
|
|
||||||
|
When compiling this template to PHP, Twig looks for the PHP callable |
||||||
|
associated with the ``lower`` filter. The ``lower`` filter is a built-in Twig |
||||||
|
filter, and it is simply mapped to the PHP ``strtolower()`` function. After |
||||||
|
compilation, the generated PHP code is roughly equivalent to: |
||||||
|
|
||||||
|
.. code-block:: html+php |
||||||
|
|
||||||
|
<?php echo strtolower('TWIG') ?> |
||||||
|
|
||||||
|
As you can see, the ``'TWIG'`` string is passed as a first argument to the PHP |
||||||
|
function. |
||||||
|
|
||||||
|
A filter can also take extra arguments like in the following example: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ now|date('d/m/Y') }} |
||||||
|
|
||||||
|
In this case, the extra arguments are passed to the function after the main |
||||||
|
argument, and the compiled code is equivalent to: |
||||||
|
|
||||||
|
.. code-block:: html+php |
||||||
|
|
||||||
|
<?php echo twig_date_format_filter($now, 'd/m/Y') ?> |
||||||
|
|
||||||
|
Let's see how to create a new filter. |
||||||
|
|
||||||
|
In this section, we will create a ``rot13`` filter, which should return the |
||||||
|
`rot13`_ transformation of a string. Here is an example of its usage and the |
||||||
|
expected output: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ "Twig"|rot13 }} |
||||||
|
|
||||||
|
{# should displays Gjvt #} |
||||||
|
|
||||||
|
Adding a filter is as simple as calling the ``addFilter()`` method on the |
||||||
|
``Twig_Environment`` instance:: |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->addFilter('rot13', new Twig_Filter_Function('str_rot13')); |
||||||
|
|
||||||
|
The second argument of ``addFilter()`` is an instance of ``Twig_Filter``. |
||||||
|
Here, we use ``Twig_Filter_Function`` as the filter is a PHP function. The |
||||||
|
first argument passed to the ``Twig_Filter_Function`` constructor is the name |
||||||
|
of the PHP function to call, here ``str_rot13``, a native PHP function. |
||||||
|
|
||||||
|
Let's say I now want to be able to add a prefix before the converted string: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ "Twig"|rot13('prefix_') }} |
||||||
|
|
||||||
|
{# should displays prefix_Gjvt #} |
||||||
|
|
||||||
|
As the PHP ``str_rot13()`` function does not support this requirement, let's |
||||||
|
create a new PHP function:: |
||||||
|
|
||||||
|
function project_compute_rot13($string, $prefix = '') |
||||||
|
{ |
||||||
|
return $prefix.str_rot13($string); |
||||||
|
} |
||||||
|
|
||||||
|
As you can see, the ``prefix`` argument of the filter is passed as an extra |
||||||
|
argument to the ``project_compute_rot13()`` function. |
||||||
|
|
||||||
|
Adding this filter is as easy as before:: |
||||||
|
|
||||||
|
$twig->addFilter('rot13', new Twig_Filter_Function('project_compute_rot13')); |
||||||
|
|
||||||
|
For better encapsulation, a filter can also be defined as a static method of a |
||||||
|
class. The ``Twig_Filter_Function`` class can also be used to register such |
||||||
|
static methods as filters:: |
||||||
|
|
||||||
|
$twig->addFilter('rot13', new Twig_Filter_Function('SomeClass::rot13Filter')); |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
In an extension, you can also define a filter as a static method of the |
||||||
|
extension class. |
||||||
|
|
||||||
|
Environment aware Filters |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
The ``Twig_Filter`` classes take options as their last argument. For instance, |
||||||
|
if you want access to the current environment instance in your filter, set the |
||||||
|
``needs_environment`` option to ``true``:: |
||||||
|
|
||||||
|
$filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true)); |
||||||
|
|
||||||
|
Twig will then pass the current environment as the first argument to the |
||||||
|
filter call:: |
||||||
|
|
||||||
|
function twig_compute_rot13(Twig_Environment $env, $string) |
||||||
|
{ |
||||||
|
// get the current charset for instance |
||||||
|
$charset = $env->getCharset(); |
||||||
|
|
||||||
|
return str_rot13($string); |
||||||
|
} |
||||||
|
|
||||||
|
Automatic Escaping |
||||||
|
~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
If automatic escaping is enabled, the output of the filter may be escaped |
||||||
|
before printing. If your filter acts as an escaper (or explicitly outputs html |
||||||
|
or javascript code), you will want the raw output to be printed. In such a |
||||||
|
case, set the ``is_safe`` option:: |
||||||
|
|
||||||
|
$filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html'))); |
||||||
|
|
||||||
|
Some filters may have to work on already escaped or safe values. In such a |
||||||
|
case, set the ``pre_escape`` option:: |
||||||
|
|
||||||
|
$filter = new Twig_Filter_Function('somefilter', array('pre_escape' => 'html', 'is_safe' => array('html'))); |
||||||
|
|
||||||
|
Dynamic Filters |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
Dynamic filters support was added in Twig 1.5. |
||||||
|
|
||||||
|
A filter name containing the special ``*`` character is a dynamic filter as |
||||||
|
the ``*`` can be any string:: |
||||||
|
|
||||||
|
$twig->addFilter('*_path', new Twig_Filter_Function('twig_path')); |
||||||
|
|
||||||
|
function twig_path($name, $arguments) |
||||||
|
{ |
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
The following filters will be matched by the above defined dynamic filter: |
||||||
|
|
||||||
|
* ``product_path`` |
||||||
|
* ``category_path`` |
||||||
|
|
||||||
|
A dynamic filter can define more than one dynamic parts:: |
||||||
|
|
||||||
|
$twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path')); |
||||||
|
|
||||||
|
function twig_path($name, $suffix, $arguments) |
||||||
|
{ |
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
The filter will receive all dynamic part values before the normal filters |
||||||
|
arguments. For instance, a call to ``'foo'|a_path_b()`` will result in the |
||||||
|
following PHP call: ``twig_path('a', 'b', 'foo')``. |
||||||
|
|
||||||
|
Functions |
||||||
|
--------- |
||||||
|
|
||||||
|
A function is a regular PHP function or an object method that can be called from |
||||||
|
templates. |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ constant("DATE_W3C") }} |
||||||
|
|
||||||
|
When compiling this template to PHP, Twig looks for the PHP callable |
||||||
|
associated with the ``constant`` function. The ``constant`` function is a built-in Twig |
||||||
|
function, and it is simply mapped to the PHP ``constant()`` function. After |
||||||
|
compilation, the generated PHP code is roughly equivalent to: |
||||||
|
|
||||||
|
.. code-block:: html+php |
||||||
|
|
||||||
|
<?php echo constant('DATE_W3C') ?> |
||||||
|
|
||||||
|
Adding a function is similar to adding a filter. This can be done by calling the |
||||||
|
``addFunction()`` method on the ``Twig_Environment`` instance:: |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->addFunction('functionName', new Twig_Function_Function('someFunction')); |
||||||
|
|
||||||
|
You can also expose extension methods as functions in your templates:: |
||||||
|
|
||||||
|
// $this is an object that implements Twig_ExtensionInterface. |
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->addFunction('otherFunction', new Twig_Function_Method($this, 'someMethod')); |
||||||
|
|
||||||
|
Functions also support ``needs_environment`` and ``is_safe`` parameters. |
||||||
|
|
||||||
|
Dynamic Functions |
||||||
|
~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
Dynamic functions support was added in Twig 1.5. |
||||||
|
|
||||||
|
A function name containing the special ``*`` character is a dynamic function |
||||||
|
as the ``*`` can be any string:: |
||||||
|
|
||||||
|
$twig->addFunction('*_path', new Twig_Function_Function('twig_path')); |
||||||
|
|
||||||
|
function twig_path($name, $arguments) |
||||||
|
{ |
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
The following functions will be matched by the above defined dynamic function: |
||||||
|
|
||||||
|
* ``product_path`` |
||||||
|
* ``category_path`` |
||||||
|
|
||||||
|
A dynamic function can define more than one dynamic parts:: |
||||||
|
|
||||||
|
$twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path')); |
||||||
|
|
||||||
|
function twig_path($name, $suffix, $arguments) |
||||||
|
{ |
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
The function will receive all dynamic part values before the normal functions |
||||||
|
arguments. For instance, a call to ``a_path_b('foo')`` will result in the |
||||||
|
following PHP call: ``twig_path('a', 'b', 'foo')``. |
||||||
|
|
||||||
|
Tags |
||||||
|
---- |
||||||
|
|
||||||
|
One of the most exciting feature of a template engine like Twig is the |
||||||
|
possibility to define new language constructs. This is also the most complex |
||||||
|
feature as you need to understand how Twig's internals work. |
||||||
|
|
||||||
|
Let's create a simple ``set`` tag that allows the definition of simple |
||||||
|
variables from within a template. The tag can be used like follows: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set name = "value" %} |
||||||
|
|
||||||
|
{{ name }} |
||||||
|
|
||||||
|
{# should output value #} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
The ``set`` tag is part of the Core extension and as such is always |
||||||
|
available. The built-in version is slightly more powerful and supports |
||||||
|
multiple assignments by default (cf. the template designers chapter for |
||||||
|
more information). |
||||||
|
|
||||||
|
Three steps are needed to define a new tag: |
||||||
|
|
||||||
|
* Defining a Token Parser class (responsible for parsing the template code); |
||||||
|
|
||||||
|
* Defining a Node class (responsible for converting the parsed code to PHP); |
||||||
|
|
||||||
|
* Registering the tag. |
||||||
|
|
||||||
|
Registering a new tag |
||||||
|
~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Adding a tag is as simple as calling the ``addTokenParser`` method on the |
||||||
|
``Twig_Environment`` instance:: |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->addTokenParser(new Project_Set_TokenParser()); |
||||||
|
|
||||||
|
Defining a Token Parser |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Now, let's see the actual code of this class:: |
||||||
|
|
||||||
|
class Project_Set_TokenParser extends Twig_TokenParser |
||||||
|
{ |
||||||
|
public function parse(Twig_Token $token) |
||||||
|
{ |
||||||
|
$lineno = $token->getLine(); |
||||||
|
$name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue(); |
||||||
|
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '='); |
||||||
|
$value = $this->parser->getExpressionParser()->parseExpression(); |
||||||
|
|
||||||
|
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); |
||||||
|
|
||||||
|
return new Project_Set_Node($name, $value, $lineno, $this->getTag()); |
||||||
|
} |
||||||
|
|
||||||
|
public function getTag() |
||||||
|
{ |
||||||
|
return 'set'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
The ``getTag()`` method must return the tag we want to parse, here ``set``. |
||||||
|
|
||||||
|
The ``parse()`` method is invoked whenever the parser encounters a ``set`` |
||||||
|
tag. It should return a ``Twig_Node`` instance that represents the node (the |
||||||
|
``Project_Set_Node`` calls creating is explained in the next section). |
||||||
|
|
||||||
|
The parsing process is simplified thanks to a bunch of methods you can call |
||||||
|
from the token stream (``$this->parser->getStream()``): |
||||||
|
|
||||||
|
* ``getCurrent()``: Gets the current token in the stream. |
||||||
|
|
||||||
|
* ``next()``: Moves to the next token in the stream, *but returns the old one*. |
||||||
|
|
||||||
|
* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether |
||||||
|
the current token is of a particular type or value (or both). The value may be an |
||||||
|
array of several possible values. |
||||||
|
|
||||||
|
* ``expect($type[, $value[, $message]])``: If the current token isn't of the given |
||||||
|
type/value a syntax error is thrown. Otherwise, if the type and value are correct, |
||||||
|
the token is returned and the stream moves to the next token. |
||||||
|
|
||||||
|
* ``look()``: Looks a the next token without consuming it. |
||||||
|
|
||||||
|
Parsing expressions is done by calling the ``parseExpression()`` like we did for |
||||||
|
the ``set`` tag. |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
Reading the existing ``TokenParser`` classes is the best way to learn all |
||||||
|
the nitty-gritty details of the parsing process. |
||||||
|
|
||||||
|
Defining a Node |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
The ``Project_Set_Node`` class itself is rather simple:: |
||||||
|
|
||||||
|
class Project_Set_Node extends Twig_Node |
||||||
|
{ |
||||||
|
public function __construct($name, Twig_Node_Expression $value, $lineno, $tag = null) |
||||||
|
{ |
||||||
|
parent::__construct(array('value' => $value), array('name' => $name), $lineno, $tag); |
||||||
|
} |
||||||
|
|
||||||
|
public function compile(Twig_Compiler $compiler) |
||||||
|
{ |
||||||
|
$compiler |
||||||
|
->addDebugInfo($this) |
||||||
|
->write('$context[\''.$this->getAttribute('name').'\'] = ') |
||||||
|
->subcompile($this->getNode('value')) |
||||||
|
->raw(";\n") |
||||||
|
; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
The compiler implements a fluid interface and provides methods that helps the |
||||||
|
developer generate beautiful and readable PHP code: |
||||||
|
|
||||||
|
* ``subcompile()``: Compiles a node. |
||||||
|
|
||||||
|
* ``raw()``: Writes the given string as is. |
||||||
|
|
||||||
|
* ``write()``: Writes the given string by adding indentation at the beginning |
||||||
|
of each line. |
||||||
|
|
||||||
|
* ``string()``: Writes a quoted string. |
||||||
|
|
||||||
|
* ``repr()``: Writes a PHP representation of a given value (see |
||||||
|
``Twig_Node_For`` for a usage example). |
||||||
|
|
||||||
|
* ``addDebugInfo()``: Adds the line of the original template file related to |
||||||
|
the current node as a comment. |
||||||
|
|
||||||
|
* ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a |
||||||
|
usage example). |
||||||
|
|
||||||
|
* ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a |
||||||
|
usage example). |
||||||
|
|
||||||
|
.. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php |
@ -0,0 +1,479 @@ |
|||||||
|
Twig for Developers |
||||||
|
=================== |
||||||
|
|
||||||
|
This chapter describes the API to Twig and not the template language. It will |
||||||
|
be most useful as reference to those implementing the template interface to |
||||||
|
the application and not those who are creating Twig templates. |
||||||
|
|
||||||
|
Basics |
||||||
|
------ |
||||||
|
|
||||||
|
Twig uses a central object called the **environment** (of class |
||||||
|
``Twig_Environment``). Instances of this class are used to store the |
||||||
|
configuration and extensions, and are used to load templates from the file |
||||||
|
system or other locations. |
||||||
|
|
||||||
|
Most applications will create one ``Twig_Environment`` object on application |
||||||
|
initialization and use that to load templates. In some cases it's however |
||||||
|
useful to have multiple environments side by side, if different configurations |
||||||
|
are in use. |
||||||
|
|
||||||
|
The simplest way to configure Twig to load templates for your application |
||||||
|
looks roughly like this:: |
||||||
|
|
||||||
|
require_once '/path/to/lib/Twig/Autoloader.php'; |
||||||
|
Twig_Autoloader::register(); |
||||||
|
|
||||||
|
$loader = new Twig_Loader_Filesystem('/path/to/templates'); |
||||||
|
$twig = new Twig_Environment($loader, array( |
||||||
|
'cache' => '/path/to/compilation_cache', |
||||||
|
)); |
||||||
|
|
||||||
|
This will create a template environment with the default settings and a loader |
||||||
|
that looks up the templates in the ``/path/to/templates/`` folder. Different |
||||||
|
loaders are available and you can also write your own if you want to load |
||||||
|
templates from a database or other resources. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Notice that the second argument of the environment is an array of options. |
||||||
|
The ``cache`` option is a compilation cache directory, where Twig caches |
||||||
|
the compiled templates to avoid the parsing phase for sub-sequent |
||||||
|
requests. It is very different from the cache you might want to add for |
||||||
|
the evaluated templates. For such a need, you can use any available PHP |
||||||
|
cache library. |
||||||
|
|
||||||
|
To load a template from this environment you just have to call the |
||||||
|
``loadTemplate()`` method which then returns a ``Twig_Template`` instance:: |
||||||
|
|
||||||
|
$template = $twig->loadTemplate('index.html'); |
||||||
|
|
||||||
|
To render the template with some variables, call the ``render()`` method:: |
||||||
|
|
||||||
|
echo $template->render(array('the' => 'variables', 'go' => 'here')); |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
The ``display()`` method is a shortcut to output the template directly. |
||||||
|
|
||||||
|
You can also load and render the template in one fell swoop:: |
||||||
|
|
||||||
|
echo $twig->render('index.html', array('the' => 'variables', 'go' => 'here')); |
||||||
|
|
||||||
|
Environment Options |
||||||
|
------------------- |
||||||
|
|
||||||
|
When creating a new ``Twig_Environment`` instance, you can pass an array of |
||||||
|
options as the constructor second argument:: |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader, array('debug' => true)); |
||||||
|
|
||||||
|
The following options are available: |
||||||
|
|
||||||
|
* ``debug``: When set to ``true``, the generated templates have a |
||||||
|
``__toString()`` method that you can use to display the generated nodes |
||||||
|
(default to ``false``). |
||||||
|
|
||||||
|
* ``charset``: The charset used by the templates (default to ``utf-8``). |
||||||
|
|
||||||
|
* ``base_template_class``: The base template class to use for generated |
||||||
|
templates (default to ``Twig_Template``). |
||||||
|
|
||||||
|
* ``cache``: An absolute path where to store the compiled templates, or |
||||||
|
``false`` to disable caching (which is the default). |
||||||
|
|
||||||
|
* ``auto_reload``: When developing with Twig, it's useful to recompile the |
||||||
|
template whenever the source code changes. If you don't provide a value for |
||||||
|
the ``auto_reload`` option, it will be determined automatically based on the |
||||||
|
``debug`` value. |
||||||
|
|
||||||
|
* ``strict_variables``: If set to ``false``, Twig will silently ignore invalid |
||||||
|
variables (variables and or attributes/methods that do not exist) and |
||||||
|
replace them with a ``null`` value. When set to ``true``, Twig throws an |
||||||
|
exception instead (default to ``false``). |
||||||
|
|
||||||
|
* ``autoescape``: If set to ``true``, auto-escaping will be enabled by default |
||||||
|
for all templates (default to ``true``). |
||||||
|
|
||||||
|
* ``optimizations``: A flag that indicates which optimizations to apply |
||||||
|
(default to ``-1`` -- all optimizations are enabled; set it to ``0`` to |
||||||
|
disable). |
||||||
|
|
||||||
|
Loaders |
||||||
|
------- |
||||||
|
|
||||||
|
Loaders are responsible for loading templates from a resource such as the file |
||||||
|
system. |
||||||
|
|
||||||
|
Compilation Cache |
||||||
|
~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
All template loaders can cache the compiled templates on the filesystem for |
||||||
|
future reuse. It speeds up Twig a lot as templates are only compiled once; and |
||||||
|
the performance boost is even larger if you use a PHP accelerator such as APC. |
||||||
|
See the ``cache`` and ``auto_reload`` options of ``Twig_Environment`` above |
||||||
|
for more information. |
||||||
|
|
||||||
|
Built-in Loaders |
||||||
|
~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Here is a list of the built-in loaders Twig provides: |
||||||
|
|
||||||
|
* ``Twig_Loader_Filesystem``: Loads templates from the file system. This |
||||||
|
loader can find templates in folders on the file system and is the preferred |
||||||
|
way to load them:: |
||||||
|
|
||||||
|
$loader = new Twig_Loader_Filesystem($templateDir); |
||||||
|
|
||||||
|
It can also look for templates in an array of directories:: |
||||||
|
|
||||||
|
$loader = new Twig_Loader_Filesystem(array($templateDir1, $templateDir2)); |
||||||
|
|
||||||
|
With such a configuration, Twig will first look for templates in |
||||||
|
``$templateDir1`` and if they do not exist, it will fallback to look for |
||||||
|
them in the ``$templateDir2``. |
||||||
|
|
||||||
|
* ``Twig_Loader_String``: Loads templates from a string. It's a dummy loader |
||||||
|
as you pass it the source code directly:: |
||||||
|
|
||||||
|
$loader = new Twig_Loader_String(); |
||||||
|
|
||||||
|
* ``Twig_Loader_Array``: Loads a template from a PHP array. It's passed an |
||||||
|
array of strings bound to template names. This loader is useful for unit |
||||||
|
testing:: |
||||||
|
|
||||||
|
$loader = new Twig_Loader_Array($templates); |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
When using the ``Array`` or ``String`` loaders with a cache mechanism, you |
||||||
|
should know that a new cache key is generated each time a template content |
||||||
|
"changes" (the cache key being the source code of the template). If you |
||||||
|
don't want to see your cache grows out of control, you need to take care |
||||||
|
of clearing the old cache file by yourself. |
||||||
|
|
||||||
|
Create your own Loader |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
All loaders implement the ``Twig_LoaderInterface``:: |
||||||
|
|
||||||
|
interface Twig_LoaderInterface |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Gets the source code of a template, given its name. |
||||||
|
* |
||||||
|
* @param string $name string The name of the template to load |
||||||
|
* |
||||||
|
* @return string The template source code |
||||||
|
*/ |
||||||
|
function getSource($name); |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the cache key to use for the cache for a given template name. |
||||||
|
* |
||||||
|
* @param string $name string The name of the template to load |
||||||
|
* |
||||||
|
* @return string The cache key |
||||||
|
*/ |
||||||
|
function getCacheKey($name); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if the template is still fresh. |
||||||
|
* |
||||||
|
* @param string $name The template name |
||||||
|
* @param timestamp $time The last modification time of the cached template |
||||||
|
*/ |
||||||
|
function isFresh($name, $time); |
||||||
|
} |
||||||
|
|
||||||
|
As an example, here is how the built-in ``Twig_Loader_String`` reads:: |
||||||
|
|
||||||
|
class Twig_Loader_String implements Twig_LoaderInterface |
||||||
|
{ |
||||||
|
public function getSource($name) |
||||||
|
{ |
||||||
|
return $name; |
||||||
|
} |
||||||
|
|
||||||
|
public function getCacheKey($name) |
||||||
|
{ |
||||||
|
return $name; |
||||||
|
} |
||||||
|
|
||||||
|
public function isFresh($name, $time) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
The ``isFresh()`` method must return ``true`` if the current cached template |
||||||
|
is still fresh, given the last modification time, or ``false`` otherwise. |
||||||
|
|
||||||
|
Using Extensions |
||||||
|
---------------- |
||||||
|
|
||||||
|
Twig extensions are packages that add new features to Twig. Using an |
||||||
|
extension is as simple as using the ``addExtension()`` method:: |
||||||
|
|
||||||
|
$twig->addExtension(new Twig_Extension_Sandbox()); |
||||||
|
|
||||||
|
Twig comes bundled with the following extensions: |
||||||
|
|
||||||
|
* *Twig_Extension_Core*: Defines all the core features of Twig. |
||||||
|
|
||||||
|
* *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 evaluated untrusted code. |
||||||
|
|
||||||
|
* *Twig_Extension_Optimizer*: Optimizers the node tree before compilation. |
||||||
|
|
||||||
|
The core, escaper, and optimizer extensions do not need to be added to the |
||||||
|
Twig environment, as they are registered by default. You can disable an |
||||||
|
already registered extension:: |
||||||
|
|
||||||
|
$twig->removeExtension('escaper'); |
||||||
|
|
||||||
|
Built-in Extensions |
||||||
|
------------------- |
||||||
|
|
||||||
|
This section describes the features added by the built-in extensions. |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
Read the chapter about extending Twig to learn how to create your own |
||||||
|
extensions. |
||||||
|
|
||||||
|
Core Extension |
||||||
|
~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
The ``core`` extension defines all the core features of Twig: |
||||||
|
|
||||||
|
* Tags: |
||||||
|
|
||||||
|
* ``for`` |
||||||
|
* ``if`` |
||||||
|
* ``extends`` |
||||||
|
* ``include`` |
||||||
|
* ``block`` |
||||||
|
* ``filter`` |
||||||
|
* ``macro`` |
||||||
|
* ``import`` |
||||||
|
* ``from`` |
||||||
|
* ``set`` |
||||||
|
* ``spaceless`` |
||||||
|
|
||||||
|
* Filters: |
||||||
|
|
||||||
|
* ``date`` |
||||||
|
* ``format`` |
||||||
|
* ``replace`` |
||||||
|
* ``url_encode`` |
||||||
|
* ``json_encode`` |
||||||
|
* ``title`` |
||||||
|
* ``capitalize`` |
||||||
|
* ``upper`` |
||||||
|
* ``lower`` |
||||||
|
* ``striptags`` |
||||||
|
* ``join`` |
||||||
|
* ``reverse`` |
||||||
|
* ``length`` |
||||||
|
* ``sort`` |
||||||
|
* ``merge`` |
||||||
|
* ``default`` |
||||||
|
* ``keys`` |
||||||
|
* ``escape`` |
||||||
|
* ``e`` |
||||||
|
|
||||||
|
* Functions: |
||||||
|
|
||||||
|
* ``range`` |
||||||
|
* ``constant`` |
||||||
|
* ``cycle`` |
||||||
|
* ``parent`` |
||||||
|
* ``block`` |
||||||
|
|
||||||
|
* Tests: |
||||||
|
|
||||||
|
* ``even`` |
||||||
|
* ``odd`` |
||||||
|
* ``defined`` |
||||||
|
* ``sameas`` |
||||||
|
* ``null`` |
||||||
|
* ``divisibleby`` |
||||||
|
* ``constant`` |
||||||
|
* ``empty`` |
||||||
|
|
||||||
|
Escaper Extension |
||||||
|
~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
The ``escaper`` extension adds automatic output escaping to Twig. It defines a |
||||||
|
new tag, ``autoescape``, and a new filter, ``raw``. |
||||||
|
|
||||||
|
When creating the escaper extension, you can switch on or off the global |
||||||
|
output escaping strategy:: |
||||||
|
|
||||||
|
$escaper = new Twig_Extension_Escaper(true); |
||||||
|
$twig->addExtension($escaper); |
||||||
|
|
||||||
|
If set to ``true``, all variables in templates are escaped, except those using |
||||||
|
the ``raw`` filter: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ article.to_html|raw }} |
||||||
|
|
||||||
|
You can also change the escaping mode locally by using the ``autoescape`` tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% autoescape true %} |
||||||
|
{{ var }} |
||||||
|
{{ var|raw }} {# var won't be escaped #} |
||||||
|
{{ var|escape }} {# var won't be double-escaped #} |
||||||
|
{% endautoescape %} |
||||||
|
|
||||||
|
.. warning:: |
||||||
|
|
||||||
|
The ``autoescape`` tag has no effect on included files. |
||||||
|
|
||||||
|
The escaping rules are implemented as follows: |
||||||
|
|
||||||
|
* Literals (integers, booleans, arrays, ...) used in the template directly as |
||||||
|
variables or filter arguments are never automatically escaped: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ "Twig<br />" }} {# won't be escaped #} |
||||||
|
|
||||||
|
{% set text = "Twig<br />" %} |
||||||
|
{{ text }} {# will be escaped #} |
||||||
|
|
||||||
|
* Expressions which the result is always a literal or a variable marked safe |
||||||
|
are never automatically escaped: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ foo ? "Twig<br />" : "<br />Twig" }} {# won't be escaped #} |
||||||
|
|
||||||
|
{% set text = "Twig<br />" %} |
||||||
|
{{ foo ? text : "<br />Twig" }} {# will be escaped #} |
||||||
|
|
||||||
|
{% set text = "Twig<br />" %} |
||||||
|
{{ foo ? text|raw : "<br />Twig" }} {# won't be escaped #} |
||||||
|
|
||||||
|
{% set text = "Twig<br />" %} |
||||||
|
{{ foo ? text|escape : "<br />Twig" }} {# the result of the expression won't be escaped #} |
||||||
|
|
||||||
|
* Escaping is applied before printing, after any other filter is applied: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ var|upper }} {# is equivalent to {{ var|upper|escape }} #} |
||||||
|
|
||||||
|
* The `raw` filter should only be used at the end of the filter chain: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ var|raw|upper }} {# will be escaped #} |
||||||
|
|
||||||
|
{{ var|upper|raw }} {# won't be escaped #} |
||||||
|
|
||||||
|
* Automatic escaping is not applied if the last filter in the chain is marked |
||||||
|
safe for the current context (e.g. ``html`` or ``js``). ``escaper`` and |
||||||
|
``escaper('html')`` are marked safe for html, ``escaper('js')`` is marked |
||||||
|
safe for javascript, ``raw`` is marked safe for everything. |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% autoescape true js %} |
||||||
|
{{ var|escape('html') }} {# will be escaped for html and javascript #} |
||||||
|
{{ var }} {# will be escaped for javascript #} |
||||||
|
{{ var|escape('js') }} {# won't be double-escaped #} |
||||||
|
{% endautoescape %} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Note that autoescaping has some limitations as escaping is applied on |
||||||
|
expressions after evaluation. For instance, when working with |
||||||
|
concatenation, ``{{ foo|raw ~ bar }}`` won't give the expected result as |
||||||
|
escaping is applied on the result of the concatenation, not on the |
||||||
|
individual variables (so, the ``raw`` filter won't have any effect here). |
||||||
|
|
||||||
|
Sandbox Extension |
||||||
|
~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
The ``sandbox`` extension can be used to evaluate untrusted code. Access to |
||||||
|
unsafe attributes and methods is prohibited. The sandbox security is managed |
||||||
|
by a policy instance. By default, Twig comes with one policy class: |
||||||
|
``Twig_Sandbox_SecurityPolicy``. This class allows you to white-list some |
||||||
|
tags, filters, properties, and methods:: |
||||||
|
|
||||||
|
$tags = array('if'); |
||||||
|
$filters = array('upper'); |
||||||
|
$methods = array( |
||||||
|
'Article' => array('getTitle', 'getBody'), |
||||||
|
); |
||||||
|
$properties = array( |
||||||
|
'Article' => array('title', 'body'), |
||||||
|
); |
||||||
|
$functions = array('range'); |
||||||
|
$policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions); |
||||||
|
|
||||||
|
With the previous configuration, the security policy will only allow usage of |
||||||
|
the ``if`` tag, and the ``upper`` filter. Moreover, the templates will only be |
||||||
|
able to call the ``getTitle()`` and ``getBody()`` methods on ``Article`` |
||||||
|
objects, and the ``title`` and ``body`` public properties. Everything else |
||||||
|
won't be allowed and will generate a ``Twig_Sandbox_SecurityError`` exception. |
||||||
|
|
||||||
|
The policy object is the first argument of the sandbox constructor:: |
||||||
|
|
||||||
|
$sandbox = new Twig_Extension_Sandbox($policy); |
||||||
|
$twig->addExtension($sandbox); |
||||||
|
|
||||||
|
By default, the sandbox mode is disabled and should be enabled when including |
||||||
|
untrusted template code by using the ``sandbox`` tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% sandbox %} |
||||||
|
{% include 'user.html' %} |
||||||
|
{% endsandbox %} |
||||||
|
|
||||||
|
You can sandbox all templates by passing ``true`` as the second argument of |
||||||
|
the extension constructor:: |
||||||
|
|
||||||
|
$sandbox = new Twig_Extension_Sandbox($policy, true); |
||||||
|
|
||||||
|
Optimizer Extension |
||||||
|
~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
The ``optimizer`` extension optimizes the node tree before compilation:: |
||||||
|
|
||||||
|
$twig->addExtension(new Twig_Extension_Optimizer()); |
||||||
|
|
||||||
|
By default, all optimizations are turned on. You can select the ones you want |
||||||
|
to enable by passing them to the constructor:: |
||||||
|
|
||||||
|
$optimizer = new Twig_Extension_Optimizer(Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR); |
||||||
|
|
||||||
|
$twig->addExtension($optimizer); |
||||||
|
|
||||||
|
Exceptions |
||||||
|
---------- |
||||||
|
|
||||||
|
Twig can throw exceptions: |
||||||
|
|
||||||
|
* ``Twig_Error``: The base exception for all errors. |
||||||
|
|
||||||
|
* ``Twig_Error_Syntax``: Thrown to tell the user that there is a problem with |
||||||
|
the template syntax. |
||||||
|
|
||||||
|
* ``Twig_Error_Runtime``: Thrown when an error occurs at runtime (when a filter |
||||||
|
does not exist for instance). |
||||||
|
|
||||||
|
* ``Twig_Error_Loader``: Thrown when an error occurs during template loading. |
||||||
|
|
||||||
|
* ``Twig_Sandbox_SecurityError``: Thrown when an unallowed tag, filter, or |
||||||
|
method is called in a sandboxed template. |
@ -0,0 +1,101 @@ |
|||||||
|
Coding Standards |
||||||
|
================ |
||||||
|
|
||||||
|
When writing Twig templates, we recommend you to follow these official coding |
||||||
|
standards: |
||||||
|
|
||||||
|
* Put one (and only one) space after the start of a delimiter (``{{``, ``{%``, |
||||||
|
and ``{#``) and before the end of a delimiter (``}}``, ``%}``, and ``#}``): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ foo }} |
||||||
|
{# comment #} |
||||||
|
{% if foo %}{% endif %} |
||||||
|
|
||||||
|
When using the whitespace control character, do not put any spaces between |
||||||
|
it and the delimiter: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{- foo -}} |
||||||
|
{#- comment -#} |
||||||
|
{%- if foo -%}{%- endif -%} |
||||||
|
|
||||||
|
* 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 (``?:``): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 1 + 2 }} |
||||||
|
{{ foo ~ bar }} |
||||||
|
{{ true ? true : false }} |
||||||
|
|
||||||
|
* Put one (and only one) space after the ``:`` sign in hashes and ``,`` in |
||||||
|
arrays and hashes: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ [1, 2, 3] }} |
||||||
|
{{ {'foo': 'bar'} }} |
||||||
|
|
||||||
|
* Do not put any spaces after an opening parenthesis and before a closing |
||||||
|
parenthesis in expressions: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 1 + (2 * 3) }} |
||||||
|
|
||||||
|
* Do not put any spaces before and after string delimiters: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 'foo' }} |
||||||
|
{{ "foo" }} |
||||||
|
|
||||||
|
* Do not put any spaces before and after the following operators: ``|``, |
||||||
|
``.``, ``..``, ``[]``: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ foo|upper|lower }} |
||||||
|
{{ user.name }} |
||||||
|
{{ user[name] }} |
||||||
|
{% for i in 1..12 %}{% endfor %} |
||||||
|
|
||||||
|
* Do not put any spaces before and after the parenthesis used for filter and |
||||||
|
function calls: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ foo|default('foo') }} |
||||||
|
{{ range(1..10) }} |
||||||
|
|
||||||
|
* Do not put any spaces before and after the opening and the closing of arrays |
||||||
|
and hashes: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ [1, 2, 3] }} |
||||||
|
{{ {'foo': 'bar'} }} |
||||||
|
|
||||||
|
* Use lower cased and underscored variable names: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set foo = 'foo' %} |
||||||
|
{% set foo_bar = 'foo' %} |
||||||
|
|
||||||
|
* Indent your code inside tags (use the same indentation as the one used for |
||||||
|
the main language of the file): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% block foo %} |
||||||
|
{% if true %} |
||||||
|
true |
||||||
|
{% endif %} |
||||||
|
{% endblock %} |
@ -0,0 +1,328 @@ |
|||||||
|
Creating a Twig Extension |
||||||
|
========================= |
||||||
|
|
||||||
|
The main motivation for writing an extension is to move often used code into a |
||||||
|
reusable class like adding support for internationalization. An extension can |
||||||
|
define tags, filters, tests, operators, global variables, functions, and node |
||||||
|
visitors. |
||||||
|
|
||||||
|
Creating an extension also makes for a better separation of code that is |
||||||
|
executed at compilation time and code needed at runtime. As such, it makes |
||||||
|
your code faster. |
||||||
|
|
||||||
|
Most of the time, it is useful to create a single extension for your project, |
||||||
|
to host all the specific tags and filters you want to add to Twig. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Before writing your own extensions, have a look at the Twig official |
||||||
|
extension repository: http://github.com/fabpot/Twig-extensions. |
||||||
|
|
||||||
|
An extension is a class that implements the following interface:: |
||||||
|
|
||||||
|
interface Twig_ExtensionInterface |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Initializes the runtime environment. |
||||||
|
* |
||||||
|
* This is where you can load some file that contains filter functions for instance. |
||||||
|
* |
||||||
|
* @param Twig_Environment $environment The current Twig_Environment instance |
||||||
|
*/ |
||||||
|
function initRuntime(Twig_Environment $environment); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the token parser instances to add to the existing list. |
||||||
|
* |
||||||
|
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances |
||||||
|
*/ |
||||||
|
function getTokenParsers(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the node visitor instances to add to the existing list. |
||||||
|
* |
||||||
|
* @return array An array of Twig_NodeVisitorInterface instances |
||||||
|
*/ |
||||||
|
function getNodeVisitors(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a list of filters to add to the existing list. |
||||||
|
* |
||||||
|
* @return array An array of filters |
||||||
|
*/ |
||||||
|
function getFilters(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a list of tests to add to the existing list. |
||||||
|
* |
||||||
|
* @return array An array of tests |
||||||
|
*/ |
||||||
|
function getTests(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a list of functions to add to the existing list. |
||||||
|
* |
||||||
|
* @return array An array of functions |
||||||
|
*/ |
||||||
|
function getFunctions(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a list of operators to add to the existing list. |
||||||
|
* |
||||||
|
* @return array An array of operators |
||||||
|
*/ |
||||||
|
function getOperators(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a list of global variables to add to the existing list. |
||||||
|
* |
||||||
|
* @return array An array of global variables |
||||||
|
*/ |
||||||
|
function getGlobals(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the name of the extension. |
||||||
|
* |
||||||
|
* @return string The extension name |
||||||
|
*/ |
||||||
|
function getName(); |
||||||
|
} |
||||||
|
|
||||||
|
To keep your extension class clean and lean, it can inherit from the built-in |
||||||
|
``Twig_Extension`` class instead of implementing the whole interface. That |
||||||
|
way, you just need to implement the ``getName()`` method as the |
||||||
|
``Twig_Extension`` provides empty implementations for all other methods. |
||||||
|
|
||||||
|
The ``getName()`` method must return a unique identifier for your extension. |
||||||
|
|
||||||
|
Now, with this information in mind, let's create the most basic extension |
||||||
|
possible:: |
||||||
|
|
||||||
|
class Project_Twig_Extension extends Twig_Extension |
||||||
|
{ |
||||||
|
public function getName() |
||||||
|
{ |
||||||
|
return 'project'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Of course, this extension does nothing for now. We will customize it in |
||||||
|
the next sections. |
||||||
|
|
||||||
|
Twig does not care where you save your extension on the filesystem, as all |
||||||
|
extensions must be registered explicitly to be available in your templates. |
||||||
|
|
||||||
|
You can register an extension by using the ``addExtension()`` method on your |
||||||
|
main ``Environment`` object:: |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->addExtension(new Project_Twig_Extension()); |
||||||
|
|
||||||
|
Of course, you need to first load the extension file by either using |
||||||
|
``require_once()`` or by using an autoloader (see `spl_autoload_register()`_). |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
The bundled extensions are great examples of how extensions work. |
||||||
|
|
||||||
|
Globals |
||||||
|
------- |
||||||
|
|
||||||
|
Global variables can be registered in an extension via the ``getGlobals()`` |
||||||
|
method:: |
||||||
|
|
||||||
|
class Project_Twig_Extension extends Twig_Extension |
||||||
|
{ |
||||||
|
public function getGlobals() |
||||||
|
{ |
||||||
|
return array( |
||||||
|
'text' => new Text(), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
Functions |
||||||
|
--------- |
||||||
|
|
||||||
|
Functions can be registered in an extension via the ``getFunctions()`` |
||||||
|
method:: |
||||||
|
|
||||||
|
class Project_Twig_Extension extends Twig_Extension |
||||||
|
{ |
||||||
|
public function getFunctions() |
||||||
|
{ |
||||||
|
return array( |
||||||
|
'lipsum' => new Twig_Function_Function('generate_lipsum'), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
Filters |
||||||
|
------- |
||||||
|
|
||||||
|
To add a filter to an extension, you need to override the ``getFilters()`` |
||||||
|
method. This method must return an array of filters to add to the Twig |
||||||
|
environment:: |
||||||
|
|
||||||
|
class Project_Twig_Extension extends Twig_Extension |
||||||
|
{ |
||||||
|
public function getFilters() |
||||||
|
{ |
||||||
|
return array( |
||||||
|
'rot13' => new Twig_Filter_Function('str_rot13'), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
As you can see in the above code, the ``getFilters()`` method returns an array |
||||||
|
where keys are the name of the filters (``rot13``) and the values the |
||||||
|
definition of the filter (``new Twig_Filter_Function('str_rot13')``). |
||||||
|
|
||||||
|
As seen in the previous chapter, you can also define filters as static methods |
||||||
|
on the extension class:: |
||||||
|
|
||||||
|
$twig->addFilter('rot13', new Twig_Filter_Function('Project_Twig_Extension::rot13Filter')); |
||||||
|
|
||||||
|
You can also use ``Twig_Filter_Method`` instead of ``Twig_Filter_Function`` |
||||||
|
when defining a filter to use a method:: |
||||||
|
|
||||||
|
class Project_Twig_Extension extends Twig_Extension |
||||||
|
{ |
||||||
|
public function getFilters() |
||||||
|
{ |
||||||
|
return array( |
||||||
|
'rot13' => new Twig_Filter_Method($this, 'rot13Filter'), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function rot13Filter($string) |
||||||
|
{ |
||||||
|
return str_rot13($string); |
||||||
|
} |
||||||
|
|
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
The first argument of the ``Twig_Filter_Method`` constructor is always |
||||||
|
``$this``, the current extension object. The second one is the name of the |
||||||
|
method to call. |
||||||
|
|
||||||
|
Using methods for filters is a great way to package your filter without |
||||||
|
polluting the global namespace. This also gives the developer more flexibility |
||||||
|
at the cost of a small overhead. |
||||||
|
|
||||||
|
Overriding default Filters |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
If some default core filters do not suit your needs, you can easily override |
||||||
|
them by creating your own core extension. Of course, you don't need to copy |
||||||
|
and paste the whole core extension code of Twig. Instead, you can just extends |
||||||
|
it and override the filter(s) you want by overriding the ``getFilters()`` |
||||||
|
method:: |
||||||
|
|
||||||
|
class MyCoreExtension extends Twig_Extension_Core |
||||||
|
{ |
||||||
|
public function getFilters() |
||||||
|
{ |
||||||
|
return array_merge(parent::getFilters(), array( |
||||||
|
'date' => new Twig_Filter_Method($this, 'dateFilter'), |
||||||
|
// ... |
||||||
|
)); |
||||||
|
} |
||||||
|
|
||||||
|
public function dateFilter($timestamp, $format = 'F j, Y H:i') |
||||||
|
{ |
||||||
|
return '...'.twig_date_format_filter($timestamp, $format); |
||||||
|
} |
||||||
|
|
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
Here, we override the ``date`` filter with a custom one. Using this new core |
||||||
|
extension is as simple as registering the ``MyCoreExtension`` extension by |
||||||
|
calling the ``addExtension()`` method on the environment instance:: |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->addExtension(new MyCoreExtension()); |
||||||
|
|
||||||
|
But I can already hear some people wondering how it can work as the Core |
||||||
|
extension is loaded by default. That's true, but the trick is that both |
||||||
|
extensions share the same unique identifier (``core`` - defined in the |
||||||
|
``getName()`` method). By registering an extension with the same name as an |
||||||
|
existing one, you have actually overridden the default one, even if it is |
||||||
|
already registered:: |
||||||
|
|
||||||
|
$twig->addExtension(new Twig_Extension_Core()); |
||||||
|
$twig->addExtension(new MyCoreExtension()); |
||||||
|
|
||||||
|
Tags |
||||||
|
---- |
||||||
|
|
||||||
|
Adding a tag in an extension can be done by overriding the |
||||||
|
``getTokenParsers()`` method. This method must return an array of tags to add |
||||||
|
to the Twig environment:: |
||||||
|
|
||||||
|
class Project_Twig_Extension extends Twig_Extension |
||||||
|
{ |
||||||
|
public function getTokenParsers() |
||||||
|
{ |
||||||
|
return array(new Project_Set_TokenParser()); |
||||||
|
} |
||||||
|
|
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
In the above code, we have added a single new tag, defined by the |
||||||
|
``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is |
||||||
|
responsible for parsing the tag and compiling it to PHP. |
||||||
|
|
||||||
|
Operators |
||||||
|
--------- |
||||||
|
|
||||||
|
The ``getOperators()`` methods allows to add new operators. Here is how to add |
||||||
|
``!``, ``||``, and ``&&`` operators:: |
||||||
|
|
||||||
|
class Project_Twig_Extension extends Twig_Extension |
||||||
|
{ |
||||||
|
public function getOperators() |
||||||
|
{ |
||||||
|
return array( |
||||||
|
array( |
||||||
|
'!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'), |
||||||
|
), |
||||||
|
array( |
||||||
|
'||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), |
||||||
|
'&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
Tests |
||||||
|
----- |
||||||
|
|
||||||
|
The ``getTests()`` methods allows to add new test functions:: |
||||||
|
|
||||||
|
class Project_Twig_Extension extends Twig_Extension |
||||||
|
{ |
||||||
|
public function getTests() |
||||||
|
{ |
||||||
|
return array( |
||||||
|
'even' => new Twig_Test_Function('twig_test_even'), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// ... |
||||||
|
} |
||||||
|
|
||||||
|
.. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register |
@ -0,0 +1,11 @@ |
|||||||
|
``capitalize`` |
||||||
|
============== |
||||||
|
|
||||||
|
The ``capitalize`` filter capitalizes a value. The first character will be |
||||||
|
uppercase, all others lowercase: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 'my first car'|capitalize }} |
||||||
|
|
||||||
|
{# outputs 'My first car' #} |
@ -0,0 +1,22 @@ |
|||||||
|
``convert_encoding`` |
||||||
|
==================== |
||||||
|
|
||||||
|
.. versionadded:: 1.4 |
||||||
|
The ``convert_encoding`` filter was added in Twig 1.4. |
||||||
|
|
||||||
|
The ``convert_encoding`` filter converts a string from one encoding to |
||||||
|
another. The first argument is the expected output charset and the second one |
||||||
|
is the input charset: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ data|convert_encoding('UTF-8', 'iso-2022-jp') }} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
This filter relies on the `iconv`_ or `mbstring`_ extension, so one of |
||||||
|
them must be installed. In case both are installed, `iconv`_ is used |
||||||
|
by default. |
||||||
|
|
||||||
|
.. _`iconv`: http://php.net/iconv |
||||||
|
.. _`mbstring`: http://php.net/mbstring |
@ -0,0 +1,65 @@ |
|||||||
|
``date`` |
||||||
|
======== |
||||||
|
|
||||||
|
.. versionadded:: 1.1 |
||||||
|
The timezone support has been added in Twig 1.1. |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
The default date format support has been added in Twig 1.5. |
||||||
|
|
||||||
|
.. versionadded:: 1.6.1 |
||||||
|
The default timezone support has been added in Twig 1.6.1. |
||||||
|
|
||||||
|
The ``date`` filter formats a date to a given format: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ post.published_at|date("m/d/Y") }} |
||||||
|
|
||||||
|
The ``date`` filter accepts strings (it must be in a format supported by the |
||||||
|
`date`_ function), `DateTime`_ instances, or `DateInterval`_ instances. For |
||||||
|
instance, to display the current date, filter the word "now": |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ "now"|date("m/d/Y") }} |
||||||
|
|
||||||
|
To escape words and characters in the date format use ``\\`` in front of each character: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ post.published_at|date("F jS \\a\\t g:ia") }} |
||||||
|
|
||||||
|
You can also specify a timezone: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ post.published_at|date("m/d/Y", "Europe/Paris") }} |
||||||
|
|
||||||
|
If no format is provided, Twig will use the default one: ``F j, Y H:i``. This |
||||||
|
default can be easily changed by calling the ``setDateFormat()`` method on the |
||||||
|
``core`` extension instance. The first argument is the default format for |
||||||
|
dates and the second one is the default format for date intervals: |
||||||
|
|
||||||
|
.. code-block:: php |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->getExtension('core')->setDateFormat('d/m/Y', '%d days'); |
||||||
|
|
||||||
|
The default timezone can also be set globally by calling ``setTimezone()``: |
||||||
|
|
||||||
|
.. code-block:: php |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->getExtension('core')->setTimezone('Europe/Paris'); |
||||||
|
|
||||||
|
.. _`date`: http://www.php.net/date |
||||||
|
.. _`DateTime`: http://www.php.net/DateTime |
||||||
|
.. _`DateInterval`: http://www.php.net/DateInterval |
||||||
|
|
||||||
|
If the value passed to the ``date`` filter is null, it will return the current date by default. |
||||||
|
If an empty string is desired instead of the current date, use a ternary operator: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ post.published_at is empty ? "" : post.published_at|date("m/d/Y") }} |
@ -0,0 +1,28 @@ |
|||||||
|
``default`` |
||||||
|
=========== |
||||||
|
|
||||||
|
The ``default`` filter returns the passed default value if the value is |
||||||
|
undefined or empty, otherwise the value of the variable: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ var|default('var is not defined') }} |
||||||
|
|
||||||
|
{{ var.foo|default('foo item on var is not defined') }} |
||||||
|
|
||||||
|
{{ var['foo']|default('foo item on var is not defined') }} |
||||||
|
|
||||||
|
{{ ''|default('passed var is empty') }} |
||||||
|
|
||||||
|
When using the ``default`` filter on an expression that uses variables in some |
||||||
|
method calls, be sure to use the ``default`` filter whenever a variable can be |
||||||
|
undefined: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ var.method(foo|default('foo'))|default('foo') }} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Read the documentation for the :doc:`defined<../tests/defined>` and |
||||||
|
:doc:`empty<../tests/empty>` tests to learn more about their semantics. |
@ -0,0 +1,30 @@ |
|||||||
|
``escape`` |
||||||
|
========== |
||||||
|
|
||||||
|
The ``escape`` filter converts the characters ``&``, ``<``, ``>``, ``'``, and |
||||||
|
``"`` in strings to HTML-safe sequences. Use this if you need to display text |
||||||
|
that might contain such characters in HTML: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ user.username|escape }} |
||||||
|
|
||||||
|
For convenience, the ``e`` filter is defined as an alias: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ user.username|e }} |
||||||
|
|
||||||
|
The ``escape`` filter can also be used in another context than HTML; for |
||||||
|
instance, to escape variables included in a JavaScript: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ user.username|escape('js') }} |
||||||
|
{{ user.username|e('js') }} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Internally, ``escape`` uses the PHP native `htmlspecialchars`_ function. |
||||||
|
|
||||||
|
.. _`htmlspecialchars`: http://php.net/htmlspecialchars |
@ -0,0 +1,16 @@ |
|||||||
|
``format`` |
||||||
|
========== |
||||||
|
|
||||||
|
The ``format`` filter formats a given string by replacing the placeholders |
||||||
|
(placeholders follows the `printf`_ notation): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ "I like %s and %s."|format(foo, "bar") }} |
||||||
|
|
||||||
|
{# returns I like foo and bar |
||||||
|
if the foo parameter equals to the foo string. #} |
||||||
|
|
||||||
|
.. _`printf`: http://www.php.net/printf |
||||||
|
|
||||||
|
.. seealso:: :doc:`replace<replace>` |
@ -0,0 +1,29 @@ |
|||||||
|
Filters |
||||||
|
======= |
||||||
|
|
||||||
|
.. toctree:: |
||||||
|
:maxdepth: 1 |
||||||
|
|
||||||
|
date |
||||||
|
format |
||||||
|
replace |
||||||
|
number_format |
||||||
|
url_encode |
||||||
|
json_encode |
||||||
|
convert_encoding |
||||||
|
title |
||||||
|
capitalize |
||||||
|
nl2br |
||||||
|
upper |
||||||
|
lower |
||||||
|
striptags |
||||||
|
join |
||||||
|
reverse |
||||||
|
length |
||||||
|
sort |
||||||
|
default |
||||||
|
keys |
||||||
|
escape |
||||||
|
raw |
||||||
|
merge |
||||||
|
slice |
@ -0,0 +1,18 @@ |
|||||||
|
``join`` |
||||||
|
======== |
||||||
|
|
||||||
|
The ``join`` filter returns a string which is the concatenation of the items |
||||||
|
of a sequence: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ [1, 2, 3]|join }} |
||||||
|
{# returns 123 #} |
||||||
|
|
||||||
|
The separator between elements is an empty string per default, but you can |
||||||
|
define it with the optional first parameter: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ [1, 2, 3]|join('|') }} |
||||||
|
{# returns 1|2|3 #} |
@ -0,0 +1,14 @@ |
|||||||
|
``json_encode`` |
||||||
|
=============== |
||||||
|
|
||||||
|
The ``json_encode`` filter returns the JSON representation of a string: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ data|json_encode() }} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Internally, Twig uses the PHP `json_encode`_ function. |
||||||
|
|
||||||
|
.. _`json_encode`: http://php.net/json_encode |
@ -0,0 +1,11 @@ |
|||||||
|
``keys`` |
||||||
|
======== |
||||||
|
|
||||||
|
The ``keys`` filter returns the keys of an array. It is useful when you want to |
||||||
|
iterate over the keys of an array: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for key in array|keys %} |
||||||
|
... |
||||||
|
{% endfor %} |
@ -0,0 +1,12 @@ |
|||||||
|
``length`` |
||||||
|
========== |
||||||
|
|
||||||
|
The ``length`` filters returns the number of items of a sequence or mapping, or |
||||||
|
the length of a string: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if users|length > 10 %} |
||||||
|
... |
||||||
|
{% endif %} |
||||||
|
|
@ -0,0 +1,10 @@ |
|||||||
|
``lower`` |
||||||
|
========= |
||||||
|
|
||||||
|
The ``lower`` filter converts a value to lowercase: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 'WELCOME'|lower }} |
||||||
|
|
||||||
|
{# outputs 'welcome' #} |
@ -0,0 +1,41 @@ |
|||||||
|
``merge`` |
||||||
|
========= |
||||||
|
|
||||||
|
The ``merge`` filter merges an array with the another array: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set values = [1, 2] %} |
||||||
|
|
||||||
|
{% set values = values|merge(['apple', 'orange']) %} |
||||||
|
|
||||||
|
{# values now contains [1, 2, 'apple', 'orange'] #} |
||||||
|
|
||||||
|
New values are added at the end of the existing ones. |
||||||
|
|
||||||
|
The ``merge`` filter also works on hashes: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set items = { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'unknown' } %} |
||||||
|
|
||||||
|
{% set items = items|merge({ 'peugeot': 'car', 'renault': 'car' }) %} |
||||||
|
|
||||||
|
{# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car', 'renault': 'car' } #} |
||||||
|
|
||||||
|
For hashes, the merging process occurs on the keys: if the key does not |
||||||
|
already exist, it is added but if the key already exists, its value is |
||||||
|
overridden. |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
If you want to ensure that some values are defined in an array (by given |
||||||
|
default values), reverse the two elements in the call: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set items = { 'apple': 'fruit', 'orange': 'fruit' } %} |
||||||
|
|
||||||
|
{% set items = { 'apple': 'unknown' }|merge(items) %} |
||||||
|
|
||||||
|
{# items now contains { 'apple': 'fruit', 'orange': 'fruit' } #} |
@ -0,0 +1,22 @@ |
|||||||
|
``nl2br`` |
||||||
|
========= |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
The nl2br filter was added in Twig 1.5. |
||||||
|
|
||||||
|
The ``nl2br`` filter inserts HTML line breaks before all newlines in a string: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ "I like Twig.\nYou will like it too."|nl2br }} |
||||||
|
{# outputs |
||||||
|
|
||||||
|
I like Twig.<br /> |
||||||
|
You will like it too. |
||||||
|
|
||||||
|
#} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
The ``nl2br`` filter pre-escapes the input before applying the |
||||||
|
transformation. |
@ -0,0 +1,38 @@ |
|||||||
|
``number_format`` |
||||||
|
================= |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
The number_format filter was added in Twig 1.5 |
||||||
|
|
||||||
|
The ``number_format`` filter formats numbers. It is a wrapper around PHP's |
||||||
|
`number_format`_ function: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 200.35|number_format }} |
||||||
|
|
||||||
|
You can control the number of decimal places, decimal point, and thousands |
||||||
|
separator using the additional arguments: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 9800.333|number_format(2, ',', '.') }} |
||||||
|
|
||||||
|
If no formatting options are provided then Twig will use the default formatting |
||||||
|
options of: |
||||||
|
|
||||||
|
- 0 decimal places. |
||||||
|
- ``.`` as the decimal point. |
||||||
|
- ``,`` as the thousands separator. |
||||||
|
|
||||||
|
These defaults can be easily changed through the core extension: |
||||||
|
|
||||||
|
.. code-block:: php |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->getExtension('core')->setNumberFormat(3, ',', '.'); |
||||||
|
|
||||||
|
The defaults set for ``number_format`` can be over-ridden upon each call using the |
||||||
|
additional parameters. |
||||||
|
|
||||||
|
.. _`number_format`: http://php.net/number_format |
@ -0,0 +1,12 @@ |
|||||||
|
``raw`` |
||||||
|
======= |
||||||
|
|
||||||
|
The ``raw`` filter marks the value as being "safe", which means that in an |
||||||
|
environment with automatic escaping enabled this variable will not be escaped |
||||||
|
if ``raw`` is the last filter applied to it: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% autoescape true %} |
||||||
|
{{ var|raw }} {# var won't be escaped #} |
||||||
|
{% endautoescape %} |
@ -0,0 +1,14 @@ |
|||||||
|
``replace`` |
||||||
|
=========== |
||||||
|
|
||||||
|
The ``replace`` filter formats a given string by replacing the placeholders |
||||||
|
(placeholders are free-form): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ "I like %this% and %that%."|replace({'%this%': foo, '%that%': "bar"}) }} |
||||||
|
|
||||||
|
{# returns I like foo and bar |
||||||
|
if the foo parameter equals to the foo string. #} |
||||||
|
|
||||||
|
.. seealso:: :doc:`format<format>` |
@ -0,0 +1,23 @@ |
|||||||
|
``reverse`` |
||||||
|
=========== |
||||||
|
|
||||||
|
.. versionadded:: 1.6 |
||||||
|
Support for strings has been added in Twig 1.6. |
||||||
|
|
||||||
|
The ``reverse`` filter reverses a sequence, a mapping, or a string: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for use in users|reverse %} |
||||||
|
... |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
{{ '1234'|reverse }} |
||||||
|
|
||||||
|
{# outputs 4321 #} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
It also works with objects implementing the `Traversable`_ interface. |
||||||
|
|
||||||
|
.. _`Traversable`: http://php.net/Traversable |
@ -0,0 +1,57 @@ |
|||||||
|
``slice`` |
||||||
|
=========== |
||||||
|
|
||||||
|
.. versionadded:: 1.6 |
||||||
|
The slice filter was added in Twig 1.6. |
||||||
|
|
||||||
|
The ``slice`` filter extracts a slice of a sequence, a mapping, or a string: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for i in [1, 2, 3, 4]|slice(1, 2) %} |
||||||
|
{# will iterate over 2 and 3 #} |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
{{ '1234'|slice(1, 2) }} |
||||||
|
|
||||||
|
{# outputs 23 #} |
||||||
|
|
||||||
|
You can use any valid expression for both the start and the length: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for i in [1, 2, 3, 4]|slice(start, length) %} |
||||||
|
{# ... #} |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
As syntactic sugar, you can also use the ``[]`` notation: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for i in [1, 2, 3, 4][start:length] %} |
||||||
|
{# ... #} |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
{{ '1234'[1:2] }} |
||||||
|
|
||||||
|
The ``slice`` filter works as the `array_slice`_ PHP function for arrays and |
||||||
|
`substr`_ for strings. |
||||||
|
|
||||||
|
If the start is non-negative, the sequence will start at that start in the |
||||||
|
variable. If start is negative, the sequence will start that far from the end |
||||||
|
of the variable. |
||||||
|
|
||||||
|
If length is given and is positive, then the sequence will have up to that |
||||||
|
many elements in it. If the variable is shorter than the length, then only the |
||||||
|
available variable elements will be present. If length is given and is |
||||||
|
negative then the sequence will stop that many elements from the end of the |
||||||
|
variable. If it is omitted, then the sequence will have everything from offset |
||||||
|
up until the end of the variable. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
It also works with objects implementing the `Traversable`_ interface. |
||||||
|
|
||||||
|
.. _`Traversable`: http://php.net/manual/en/class.traversable.php |
||||||
|
.. _`array_slice`: http://php.net/array_slice |
||||||
|
.. _`substr`: http://php.net/substr |
@ -0,0 +1,17 @@ |
|||||||
|
``sort`` |
||||||
|
======== |
||||||
|
|
||||||
|
The ``sort`` filter sorts an array: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for use in users|sort %} |
||||||
|
... |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Internally, Twig uses the PHP `asort`_ function to maintain index |
||||||
|
association. |
||||||
|
|
||||||
|
.. _`asort`: http://php.net/asort |
@ -0,0 +1,15 @@ |
|||||||
|
``striptags`` |
||||||
|
============= |
||||||
|
|
||||||
|
The ``striptags`` filter strips SGML/XML tags and replace adjacent whitespace |
||||||
|
by one space: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% some_html|striptags %} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Internally, Twig uses the PHP `strip_tags`_ function. |
||||||
|
|
||||||
|
.. _`strip_tags`: http://php.net/strip_tags |
@ -0,0 +1,11 @@ |
|||||||
|
``title`` |
||||||
|
========= |
||||||
|
|
||||||
|
The ``title`` filter returns a titlecased version of the value. Words will |
||||||
|
start with uppercase letters, all remaining characters are lowercase: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 'my first car'|title }} |
||||||
|
|
||||||
|
{# outputs 'My First Car' #} |
@ -0,0 +1,24 @@ |
|||||||
|
``trim`` |
||||||
|
======== |
||||||
|
|
||||||
|
.. versionadded:: 1.6.2 |
||||||
|
The trim filter was added in Twig 1.6.2. |
||||||
|
|
||||||
|
The ``trim`` filter strips whitespace (or other characters) from the beginning |
||||||
|
and end of a string: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ ' I like Twig. '|trim }} |
||||||
|
|
||||||
|
{# outputs 'I like Twig.' #} |
||||||
|
|
||||||
|
{{ ' I like Twig.'|trim('.') }} |
||||||
|
|
||||||
|
{# outputs ' I like Twig' #} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Internally, Twig uses the PHP `trim`_ function. |
||||||
|
|
||||||
|
.. _`trim`: http://php.net/trim |
@ -0,0 +1,10 @@ |
|||||||
|
``upper`` |
||||||
|
========= |
||||||
|
|
||||||
|
The ``upper`` filter converts a value to uppercase: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ 'welcome'|upper }} |
||||||
|
|
||||||
|
{# outputs 'WELCOME' #} |
@ -0,0 +1,14 @@ |
|||||||
|
``url_encode`` |
||||||
|
============== |
||||||
|
|
||||||
|
The ``url_encode`` filter URL encodes a given string: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ data|url_encode() }} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Internally, Twig uses the PHP `urlencode`_ function. |
||||||
|
|
||||||
|
.. _`urlencode`: http://php.net/urlencode |
@ -0,0 +1,18 @@ |
|||||||
|
``attribute`` |
||||||
|
============= |
||||||
|
|
||||||
|
.. versionadded:: 1.2 |
||||||
|
The ``attribute`` function was added in Twig 1.2. |
||||||
|
|
||||||
|
``attribute`` can be used to access a "dynamic" attribute of a variable: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ attribute(object, method) }} |
||||||
|
{{ attribute(object, method, arguments) }} |
||||||
|
{{ attribute(array, item) }} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
The resolution algorithm is the same as the one used for the ``.`` |
||||||
|
notation, except that the item can be any valid expression. |
@ -0,0 +1,15 @@ |
|||||||
|
``block`` |
||||||
|
========= |
||||||
|
|
||||||
|
When a template uses inheritance and if you want to print a block multiple |
||||||
|
times, use the ``block`` function: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<title>{% block title %}{% endblock %}</title> |
||||||
|
|
||||||
|
<h1>{{ block('title') }}</h1> |
||||||
|
|
||||||
|
{% block body %}{% endblock %} |
||||||
|
|
||||||
|
.. seealso:: :doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>` |
@ -0,0 +1,9 @@ |
|||||||
|
``constant`` |
||||||
|
============ |
||||||
|
|
||||||
|
``constant`` returns the constant value for a given string: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ some_date|date(constant('DATE_W3C')) }} |
||||||
|
{{ constant('Namespace\\Classname::CONSTANT_NAME') }} |
@ -0,0 +1,20 @@ |
|||||||
|
``cycle`` |
||||||
|
========= |
||||||
|
|
||||||
|
The ``cycle`` function cycles on an array of values: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for i in 0..10 %} |
||||||
|
{{ cycle(['odd', 'even'], i) }} |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
The array can contain any number of values: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set fruits = ['apple', 'orange', 'citrus'] %} |
||||||
|
|
||||||
|
{% for i in 0..10 %} |
||||||
|
{{ cycle(fruits, i) }} |
||||||
|
{% endfor %} |
@ -0,0 +1,46 @@ |
|||||||
|
``date`` |
||||||
|
======== |
||||||
|
|
||||||
|
.. versionadded:: 1.6 |
||||||
|
The date function has been added in Twig 1.6. |
||||||
|
|
||||||
|
.. versionadded:: 1.6.1 |
||||||
|
The default timezone support has been added in Twig 1.6.1. |
||||||
|
|
||||||
|
Converts an argument to a date to allow date comparison: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if date(user.created_at) < date('+2days') %} |
||||||
|
{# do something #} |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
The argument must be in a format supported by the `date`_ function. |
||||||
|
|
||||||
|
You can pass a timezone as the second argument: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if date(user.created_at) < date('+2days', 'Europe/Paris') %} |
||||||
|
{# do something #} |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
If no argument is passed, the function returns the current date: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if date(user.created_at) < date() %} |
||||||
|
{# always! #} |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
You can set the default timezone globally by calling ``setTimezone()`` on |
||||||
|
the ``core`` extension instance: |
||||||
|
|
||||||
|
.. code-block:: php |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
$twig->getExtension('core')->setTimezone('Europe/Paris'); |
||||||
|
|
||||||
|
.. _`date`: http://www.php.net/date |
@ -0,0 +1,58 @@ |
|||||||
|
``dump`` |
||||||
|
======== |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
The dump function was added in Twig 1.5. |
||||||
|
|
||||||
|
The ``dump`` function dumps information about a template variable. This is |
||||||
|
mostly useful to debug a template that does not behave as expected by |
||||||
|
introspecting its variables: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ dump(user) }} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
The ``debug`` function is not available by default. You must load it explicitly:: |
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader, $config); |
||||||
|
$twig->addExtension(new Twig_Extension_Debug()); |
||||||
|
|
||||||
|
Even when loaded explicitly, it won't do anything if the ``debug`` option |
||||||
|
is not enabled. |
||||||
|
|
||||||
|
In an HTML context, wrap the output with a ``pre`` tag to make it easier to |
||||||
|
read: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<pre> |
||||||
|
{{ dump(user) }} |
||||||
|
</pre> |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
Using a ``pre`` tag is not needed when `XDebug`_ is enabled and |
||||||
|
``html_errors`` is ``on``; as a bonus, the output is also nicer with |
||||||
|
XDebug enabled. |
||||||
|
|
||||||
|
You can debug several variables by passing them as additional arguments: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ dump(user, categories) }} |
||||||
|
|
||||||
|
If you don't pass any value, all variables from the current context are |
||||||
|
dumped: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ dump() }} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Internally, Twig uses the PHP `var_dump`_ function. |
||||||
|
|
||||||
|
.. _`XDebug`: http://xdebug.org/docs/display |
||||||
|
.. _`var_dump`: http://php.net/var_dump |
@ -0,0 +1,15 @@ |
|||||||
|
Functions |
||||||
|
========= |
||||||
|
|
||||||
|
.. toctree:: |
||||||
|
:maxdepth: 1 |
||||||
|
|
||||||
|
range |
||||||
|
cycle |
||||||
|
constant |
||||||
|
random |
||||||
|
attribute |
||||||
|
block |
||||||
|
parent |
||||||
|
dump |
||||||
|
date |
@ -0,0 +1,20 @@ |
|||||||
|
``parent`` |
||||||
|
========== |
||||||
|
|
||||||
|
When a template uses inheritance, it's possible to render the contents of the |
||||||
|
parent block when overriding a block by using the ``parent`` function: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends "base.html" %} |
||||||
|
|
||||||
|
{% block sidebar %} |
||||||
|
<h3>Table Of Contents</h3> |
||||||
|
... |
||||||
|
{{ parent() }} |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
The ``parent()`` call will return the content of the ``sidebar`` block as |
||||||
|
defined in the ``base.html`` template. |
||||||
|
|
||||||
|
.. seealso:: :doc:`extends<../tags/extends>`, :doc:`block<../functions/block>`, :doc:`block<../tags/block>` |
@ -0,0 +1,24 @@ |
|||||||
|
``random`` |
||||||
|
========== |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
The random function was added in Twig 1.5. |
||||||
|
|
||||||
|
.. versionadded:: 1.6 |
||||||
|
String and integer handling was added in Twig 1.6. |
||||||
|
|
||||||
|
The ``random`` function returns a random value depending on the supplied |
||||||
|
parameter type: |
||||||
|
|
||||||
|
* a random item from a sequence; |
||||||
|
* a random character from a string; |
||||||
|
* a random integer between 0 and the integer parameter (inclusive). |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ random(['apple', 'orange', 'citrus']) }} {# example output: orange #} |
||||||
|
{{ random('ABC') }} {# example output: C #} |
||||||
|
{{ random() }} {# example output: 15386094 (works as native PHP `mt_rand`_ function) #} |
||||||
|
{{ random(5) }} {# example output: 3 #} |
||||||
|
|
||||||
|
.. _`mt_rand`: http://php.net/mt_rand |
@ -0,0 +1,38 @@ |
|||||||
|
``range`` |
||||||
|
========= |
||||||
|
|
||||||
|
Returns a list containing an arithmetic progression of integers: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for i in range(0, 3) %} |
||||||
|
{{ i }}, |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
{# returns 0, 1, 2, 3 #} |
||||||
|
|
||||||
|
When step is given (as the third parameter), it specifies the increment (or |
||||||
|
decrement): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for i in range(0, 6, 2) %} |
||||||
|
{{ i }}, |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
{# returns 0, 2, 4, 6 #} |
||||||
|
|
||||||
|
The Twig built-in ``..`` operator is just syntactic sugar for the ``range`` |
||||||
|
function (with a step of 1): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for i in 0..3 %} |
||||||
|
{{ i }}, |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
The ``range`` function works as the native PHP `range`_ function. |
||||||
|
|
||||||
|
.. _`range`: http://php.net/range |
@ -0,0 +1,184 @@ |
|||||||
|
Hacking Twig |
||||||
|
============ |
||||||
|
|
||||||
|
Twig is very extensible and you can easily hack it. Keep in mind that you |
||||||
|
should probably try to create an extension before hacking the core, as most |
||||||
|
features and enhancements can be done with extensions. This chapter is also |
||||||
|
useful for people who want to understand how Twig works under the hood. |
||||||
|
|
||||||
|
How Twig works? |
||||||
|
--------------- |
||||||
|
|
||||||
|
The rendering of a Twig template can be summarized into four key steps: |
||||||
|
|
||||||
|
* **Load** the template: If the template is already compiled, load it and go |
||||||
|
to the *evaluation* step, otherwise: |
||||||
|
|
||||||
|
* First, the **lexer** tokenizes the template source code into small pieces |
||||||
|
for easier processing; |
||||||
|
* Then, the **parser** converts the token stream into a meaningful tree |
||||||
|
of nodes (the Abstract Syntax Tree); |
||||||
|
* Eventually, the *compiler* transforms the AST into PHP code; |
||||||
|
|
||||||
|
* **Evaluate** the template: It basically means calling the ``display()`` |
||||||
|
method of the compiled template and passing it the context. |
||||||
|
|
||||||
|
The Lexer |
||||||
|
--------- |
||||||
|
|
||||||
|
The Twig lexer goal is to tokenize a source code into a token stream (each |
||||||
|
token is of class ``Token``, and the stream is an instance of |
||||||
|
``Twig_TokenStream``). The default lexer recognizes nine different token types: |
||||||
|
|
||||||
|
* ``Twig_Token::TEXT_TYPE`` |
||||||
|
* ``Twig_Token::BLOCK_START_TYPE`` |
||||||
|
* ``Twig_Token::VAR_START_TYPE`` |
||||||
|
* ``Twig_Token::BLOCK_END_TYPE`` |
||||||
|
* ``Twig_Token::VAR_END_TYPE`` |
||||||
|
* ``Twig_Token::NAME_TYPE`` |
||||||
|
* ``Twig_Token::NUMBER_TYPE`` |
||||||
|
* ``Twig_Token::STRING_TYPE`` |
||||||
|
* ``Twig_Token::OPERATOR_TYPE`` |
||||||
|
* ``Twig_Token::EOF_TYPE`` |
||||||
|
|
||||||
|
You can manually convert a source code into a token stream by calling the |
||||||
|
``tokenize()`` of an environment:: |
||||||
|
|
||||||
|
$stream = $twig->tokenize($source, $identifier); |
||||||
|
|
||||||
|
As the stream has a ``__toString()`` method, you can have a textual |
||||||
|
representation of it by echoing the object:: |
||||||
|
|
||||||
|
echo $stream."\n"; |
||||||
|
|
||||||
|
Here is the output for the ``Hello {{ name }}`` template: |
||||||
|
|
||||||
|
.. code-block:: text |
||||||
|
|
||||||
|
TEXT_TYPE(Hello ) |
||||||
|
VAR_START_TYPE() |
||||||
|
NAME_TYPE(name) |
||||||
|
VAR_END_TYPE() |
||||||
|
EOF_TYPE() |
||||||
|
|
||||||
|
You can change the default lexer use by Twig (``Twig_Lexer``) by calling the |
||||||
|
``setLexer()`` method:: |
||||||
|
|
||||||
|
$twig->setLexer($lexer); |
||||||
|
|
||||||
|
Lexer classes must implement the ``Twig_LexerInterface``:: |
||||||
|
|
||||||
|
interface Twig_LexerInterface |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Tokenizes a source code. |
||||||
|
* |
||||||
|
* @param string $code The source code |
||||||
|
* @param string $filename A unique identifier for the source code |
||||||
|
* |
||||||
|
* @return Twig_TokenStream A token stream instance |
||||||
|
*/ |
||||||
|
function tokenize($code, $filename = 'n/a'); |
||||||
|
} |
||||||
|
|
||||||
|
The Parser |
||||||
|
---------- |
||||||
|
|
||||||
|
The parser converts the token stream into an AST (Abstract Syntax Tree), or a |
||||||
|
node tree (of class ``Twig_Node_Module``). The core extension defines the |
||||||
|
basic nodes like: ``for``, ``if``, ... and the expression nodes. |
||||||
|
|
||||||
|
You can manually convert a token stream into a node tree by calling the |
||||||
|
``parse()`` method of an environment:: |
||||||
|
|
||||||
|
$nodes = $twig->parse($stream); |
||||||
|
|
||||||
|
Echoing the node object gives you a nice representation of the tree:: |
||||||
|
|
||||||
|
echo $nodes."\n"; |
||||||
|
|
||||||
|
Here is the output for the ``Hello {{ name }}`` template: |
||||||
|
|
||||||
|
.. code-block:: text |
||||||
|
|
||||||
|
Twig_Node_Module( |
||||||
|
Twig_Node_Text(Hello ) |
||||||
|
Twig_Node_Print( |
||||||
|
Twig_Node_Expression_Name(name) |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
The default parser (``Twig_TokenParser``) can be also changed by calling the |
||||||
|
``setParser()`` method:: |
||||||
|
|
||||||
|
$twig->setParser($parser); |
||||||
|
|
||||||
|
All Twig parsers must implement the ``Twig_ParserInterface``:: |
||||||
|
|
||||||
|
interface Twig_ParserInterface |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Converts a token stream to a node tree. |
||||||
|
* |
||||||
|
* @param Twig_TokenStream $stream A token stream instance |
||||||
|
* |
||||||
|
* @return Twig_Node_Module A node tree |
||||||
|
*/ |
||||||
|
function parser(Twig_TokenStream $code); |
||||||
|
} |
||||||
|
|
||||||
|
The Compiler |
||||||
|
------------ |
||||||
|
|
||||||
|
The last step is done by the compiler. It takes a node tree as an input and |
||||||
|
generates PHP code usable for runtime execution of the templates. The default |
||||||
|
compiler generates PHP classes to ease the implementation of the template |
||||||
|
inheritance feature. |
||||||
|
|
||||||
|
You can call the compiler by hand with the ``compile()`` method of an |
||||||
|
environment:: |
||||||
|
|
||||||
|
$php = $twig->compile($nodes); |
||||||
|
|
||||||
|
The ``compile()`` method returns the PHP source code representing the node. |
||||||
|
|
||||||
|
The generated template for a ``Hello {{ name }}`` template reads as follows:: |
||||||
|
|
||||||
|
/* Hello {{ name }} */ |
||||||
|
class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712bceb extends Twig_Template |
||||||
|
{ |
||||||
|
public function display($context) |
||||||
|
{ |
||||||
|
$this->env->initRuntime(); |
||||||
|
|
||||||
|
// line 1 |
||||||
|
echo "Hello "; |
||||||
|
echo (isset($context['name']) ? $context['name'] : null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
As for the lexer and the parser, the default compiler (``Twig_Compiler``) can |
||||||
|
be changed by calling the ``setCompiler()`` method:: |
||||||
|
|
||||||
|
$twig->setCompiler($compiler); |
||||||
|
|
||||||
|
All Twig compilers must implement the ``Twig_CompilerInterface``:: |
||||||
|
|
||||||
|
interface Twig_CompilerInterface |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Compiles a node. |
||||||
|
* |
||||||
|
* @param Twig_Node $node The node to compile |
||||||
|
* |
||||||
|
* @return Twig_Compiler The current compiler instance |
||||||
|
*/ |
||||||
|
function compile(Twig_Node $node); |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the current PHP code after compilation. |
||||||
|
* |
||||||
|
* @return string The PHP code |
||||||
|
*/ |
||||||
|
function getSource(); |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
Twig |
||||||
|
==== |
||||||
|
|
||||||
|
.. toctree:: |
||||||
|
:maxdepth: 2 |
||||||
|
|
||||||
|
intro |
||||||
|
templates |
||||||
|
api |
||||||
|
advanced |
||||||
|
extensions |
||||||
|
hacking |
||||||
|
recipes |
||||||
|
coding_standards |
||||||
|
tags/index |
||||||
|
filters/index |
||||||
|
functions/index |
||||||
|
tests/index |
@ -0,0 +1,153 @@ |
|||||||
|
Introduction |
||||||
|
============ |
||||||
|
|
||||||
|
This is the documentation for Twig, the flexible, fast, and secure template |
||||||
|
engine for PHP. |
||||||
|
|
||||||
|
If you have any exposure to other text-based template languages, such as |
||||||
|
Smarty, Django, or Jinja, you should feel right at home with Twig. It's both |
||||||
|
designer and developer friendly by sticking to PHP's principles and adding |
||||||
|
functionality useful for templating environments. |
||||||
|
|
||||||
|
The key-features are... |
||||||
|
|
||||||
|
* *Fast*: Twig compiles templates down to plain optimized PHP code. The |
||||||
|
overhead compared to regular PHP code was reduced to the very minimum. |
||||||
|
|
||||||
|
* *Secure*: Twig has a sandbox mode to evaluate untrusted template code. This |
||||||
|
allows Twig to be used as a template language for applications where users |
||||||
|
may modify the template design. |
||||||
|
|
||||||
|
* *Flexible*: Twig is powered by a flexible lexer and parser. This allows the |
||||||
|
developer to define its own custom tags and filters, and create its own DSL. |
||||||
|
|
||||||
|
Prerequisites |
||||||
|
------------- |
||||||
|
|
||||||
|
Twig needs at least **PHP 5.2.4** to run. |
||||||
|
|
||||||
|
Installation |
||||||
|
------------ |
||||||
|
|
||||||
|
You have multiple ways to install Twig. If you are unsure what to do, go with |
||||||
|
the tarball. |
||||||
|
|
||||||
|
Installing from the tarball release |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
1. Download the most recent tarball from the `download page`_ |
||||||
|
2. Unpack the tarball |
||||||
|
3. Move the files somewhere in your project |
||||||
|
|
||||||
|
Installing the development version |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
1. Install Subversion or Git |
||||||
|
2. For Git: ``git clone git://github.com/fabpot/Twig.git`` |
||||||
|
3. For Subversion: ``svn co http://svn.twig-project.org/trunk/ twig`` |
||||||
|
|
||||||
|
Installing the PEAR package |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
1. Install PEAR |
||||||
|
2. ``pear channel-discover pear.twig-project.org`` |
||||||
|
3. ``pear install twig/Twig`` (or ``pear install twig/Twig-beta``) |
||||||
|
|
||||||
|
Installing via Composer |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
1. Install composer in your project: |
||||||
|
|
||||||
|
.. code-block:: bash |
||||||
|
|
||||||
|
curl -s http://getcomposer.org/installer | php`` |
||||||
|
|
||||||
|
2. Create a ``composer.json`` file in your project root: |
||||||
|
|
||||||
|
.. code-block:: javascript |
||||||
|
|
||||||
|
{ |
||||||
|
"require": { |
||||||
|
"twig/twig": "1.6.0" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
3. Install via composer |
||||||
|
|
||||||
|
.. code-block:: bash |
||||||
|
|
||||||
|
php composer.phar install |
||||||
|
|
||||||
|
Installing the C extension |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
.. versionadded:: 1.4 |
||||||
|
The C extension was added in Twig 1.4. |
||||||
|
|
||||||
|
Twig comes with a C extension that enhances the performance of the Twig |
||||||
|
runtime engine. You can install it like any other PHP extension: |
||||||
|
|
||||||
|
.. code-block:: bash |
||||||
|
|
||||||
|
$ cd ext/twig |
||||||
|
$ phpize |
||||||
|
$ ./configure |
||||||
|
$ make |
||||||
|
$ make install |
||||||
|
|
||||||
|
Finally, enable the extension in your ``php.ini`` configuration file: |
||||||
|
|
||||||
|
.. code-block:: ini |
||||||
|
|
||||||
|
extension=twig.so |
||||||
|
|
||||||
|
And from now on, Twig will automatically compile your templates to take |
||||||
|
advantage of the C extension. |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
On Windows, you can also simply download and install a `pre-build DLL`_. |
||||||
|
|
||||||
|
Basic API Usage |
||||||
|
--------------- |
||||||
|
|
||||||
|
This section gives you a brief introduction to the PHP API for Twig. |
||||||
|
|
||||||
|
The first step to use Twig is to register its autoloader:: |
||||||
|
|
||||||
|
require_once '/path/to/lib/Twig/Autoloader.php'; |
||||||
|
Twig_Autoloader::register(); |
||||||
|
|
||||||
|
Replace the ``/path/to/lib/`` path with the path you used for Twig |
||||||
|
installation. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Twig follows the PEAR convention names for its classes, which means you |
||||||
|
can easily integrate Twig classes loading in your own autoloader. |
||||||
|
|
||||||
|
.. code-block:: php |
||||||
|
|
||||||
|
$loader = new Twig_Loader_String(); |
||||||
|
$twig = new Twig_Environment($loader); |
||||||
|
|
||||||
|
echo $twig->render('Hello {{ name }}!', array('name' => 'Fabien')); |
||||||
|
|
||||||
|
Twig uses a loader (``Twig_Loader_String``) to locate templates, and an |
||||||
|
environment (``Twig_Environment``) to store the configuration. |
||||||
|
|
||||||
|
The ``render()`` method loads the template passed as a first argument and |
||||||
|
renders it with the variables passed as a second argument. |
||||||
|
|
||||||
|
As templates are generally stored on the filesystem, Twig also comes with a |
||||||
|
filesystem loader:: |
||||||
|
|
||||||
|
$loader = new Twig_Loader_Filesystem('/path/to/templates'); |
||||||
|
$twig = new Twig_Environment($loader, array( |
||||||
|
'cache' => '/path/to/compilation_cache', |
||||||
|
)); |
||||||
|
|
||||||
|
echo $twig->render('index.html', array('name' => 'Fabien')); |
||||||
|
|
||||||
|
.. _`download page`: https://github.com/fabpot/Twig/tags |
||||||
|
.. _`pre-build DLL`: https://github.com/stealth35/stealth35.github.com/downloads |
@ -0,0 +1,301 @@ |
|||||||
|
Recipes |
||||||
|
======= |
||||||
|
|
||||||
|
Making a Layout conditional |
||||||
|
--------------------------- |
||||||
|
|
||||||
|
Working with Ajax means that the same content is sometimes displayed as is, |
||||||
|
and sometimes decorated with a layout. As Twig layout template names can be |
||||||
|
any valid expression, you can pass a variable that evaluates to ``true`` when |
||||||
|
the request is made via Ajax and choose the layout accordingly: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends request.ajax ? "base_ajax.html" : "base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
This is the content to be displayed. |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
Making an Include dynamic |
||||||
|
------------------------- |
||||||
|
|
||||||
|
When including a template, its name does not need to be a string. For |
||||||
|
instance, the name can depend on the value of a variable: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% include var ~ '_foo.html' %} |
||||||
|
|
||||||
|
If ``var`` evaluates to ``index``, the ``index_foo.html`` template will be |
||||||
|
rendered. |
||||||
|
|
||||||
|
As a matter of fact, the template name can be any valid expression, such as |
||||||
|
the following: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% include var|default('index') ~ '_foo.html' %} |
||||||
|
|
||||||
|
Overriding a Template that also extends itself |
||||||
|
---------------------------------------------- |
||||||
|
|
||||||
|
A template can be customized in two different ways: |
||||||
|
|
||||||
|
* *Inheritance*: A template *extends* a parent template and overrides some |
||||||
|
blocks; |
||||||
|
|
||||||
|
* *Replacement*: If you use the filesystem loader, Twig loads the first |
||||||
|
template it finds in a list of configured directories; a template found in a |
||||||
|
directory *replaces* another one from a directory further in the list. |
||||||
|
|
||||||
|
But how do you combine both: *replace* a template that also extends itself |
||||||
|
(aka a template in a directory further in the list)? |
||||||
|
|
||||||
|
Let's say that your templates are loaded from both ``.../templates/mysite`` |
||||||
|
and ``.../templates/default`` in this order. The ``page.twig`` template, |
||||||
|
stored in ``.../templates/default`` reads as follows: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# page.twig #} |
||||||
|
{% extends "layout.twig" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
You can replace this template by putting a file with the same name in |
||||||
|
``.../templates/mysite``. And if you want to extend the original template, you |
||||||
|
might be tempted to write the following: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# page.twig in .../templates/mysite #} |
||||||
|
{% extends "page.twig" %} {# from .../templates/default #} |
||||||
|
|
||||||
|
Of course, this will not work as Twig will always load the template from |
||||||
|
``.../templates/mysite``. |
||||||
|
|
||||||
|
It turns out it is possible to get this to work, by adding a directory right |
||||||
|
at the end of your template directories, which is the parent of all of the |
||||||
|
other directories: ``.../templates`` in our case. This has the effect of |
||||||
|
making every template file within our system uniquely addressable. Most of the |
||||||
|
time you will use the "normal" paths, but in the special case of wanting to |
||||||
|
extend a template with an overriding version of itself we can reference its |
||||||
|
parent's full, unambiguous template path in the extends tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# page.twig in .../templates/mysite #} |
||||||
|
{% extends "default/page.twig" %} {# from .../templates #} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
This recipe was inspired by the following Django wiki page: |
||||||
|
http://code.djangoproject.com/wiki/ExtendingTemplates |
||||||
|
|
||||||
|
Customizing the Syntax |
||||||
|
---------------------- |
||||||
|
|
||||||
|
Twig allows some syntax customization for the block delimiters. It's not |
||||||
|
recommended to use this feature as templates will be tied with your custom |
||||||
|
syntax. But for specific projects, it can make sense to change the defaults. |
||||||
|
|
||||||
|
To change the block delimiters, you need to create your own lexer object:: |
||||||
|
|
||||||
|
$twig = new Twig_Environment(); |
||||||
|
|
||||||
|
$lexer = new Twig_Lexer($twig, array( |
||||||
|
'tag_comment' => array('{#', '#}'), |
||||||
|
'tag_block' => array('{%', '%}'), |
||||||
|
'tag_variable' => array('{{', '}}'), |
||||||
|
)); |
||||||
|
$twig->setLexer($lexer); |
||||||
|
|
||||||
|
Here are some configuration example that simulates some other template engines |
||||||
|
syntax:: |
||||||
|
|
||||||
|
// Ruby erb syntax |
||||||
|
$lexer = new Twig_Lexer($twig, array( |
||||||
|
'tag_comment' => array('<%#', '%>'), |
||||||
|
'tag_block' => array('<%', '%>'), |
||||||
|
'tag_variable' => array('<%=', '%>'), |
||||||
|
)); |
||||||
|
|
||||||
|
// SGML Comment Syntax |
||||||
|
$lexer = new Twig_Lexer($twig, array( |
||||||
|
'tag_comment' => array('<!--#', '-->'), |
||||||
|
'tag_block' => array('<!--', '-->'), |
||||||
|
'tag_variable' => array('${', '}'), |
||||||
|
)); |
||||||
|
|
||||||
|
// Smarty like |
||||||
|
$lexer = new Twig_Lexer($twig, array( |
||||||
|
'tag_comment' => array('{*', '*}'), |
||||||
|
'tag_block' => array('{', '}'), |
||||||
|
'tag_variable' => array('{$', '}'), |
||||||
|
)); |
||||||
|
|
||||||
|
Using dynamic Object Properties |
||||||
|
------------------------------- |
||||||
|
|
||||||
|
When Twig encounters a variable like ``article.title``, it tries to find a |
||||||
|
``title`` public property in the ``article`` object. |
||||||
|
|
||||||
|
It also works if the property does not exist but is rather defined dynamically |
||||||
|
thanks to the magic ``__get()`` method; you just need to also implement the |
||||||
|
``__isset()`` magic method like shown in the following snippet of code:: |
||||||
|
|
||||||
|
class Article |
||||||
|
{ |
||||||
|
public function __get($name) |
||||||
|
{ |
||||||
|
if ('title' == $name) |
||||||
|
{ |
||||||
|
return 'The title'; |
||||||
|
} |
||||||
|
|
||||||
|
// throw some kind of error |
||||||
|
} |
||||||
|
|
||||||
|
public function __isset($name) |
||||||
|
{ |
||||||
|
if ('title' == $name) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Accessing the parent Context in Nested Loops |
||||||
|
-------------------------------------------- |
||||||
|
|
||||||
|
Sometimes, when using nested loops, you need to access the parent context. The |
||||||
|
parent context is always accessible via the ``loop.parent`` variable. For |
||||||
|
instance, if you have the following template data:: |
||||||
|
|
||||||
|
$data = array( |
||||||
|
'topics' => array( |
||||||
|
'topic1' => array('Message 1 of topic 1', 'Message 2 of topic 1'), |
||||||
|
'topic2' => array('Message 1 of topic 2', 'Message 2 of topic 2'), |
||||||
|
), |
||||||
|
); |
||||||
|
|
||||||
|
And the following template to display all messages in all topics: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for topic, messages in topics %} |
||||||
|
* {{ loop.index }}: {{ topic }} |
||||||
|
{% for message in messages %} |
||||||
|
- {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }} |
||||||
|
{% endfor %} |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
The output will be similar to: |
||||||
|
|
||||||
|
.. code-block:: text |
||||||
|
|
||||||
|
* 1: topic1 |
||||||
|
- 1.1: The message 1 of topic 1 |
||||||
|
- 1.2: The message 2 of topic 1 |
||||||
|
* 2: topic2 |
||||||
|
- 2.1: The message 1 of topic 2 |
||||||
|
- 2.2: The message 2 of topic 2 |
||||||
|
|
||||||
|
In the inner loop, the ``loop.parent`` variable is used to access the outer |
||||||
|
context. So, the index of the current ``topic`` defined in the outer for loop |
||||||
|
is accessible via the ``loop.parent.loop.index`` variable. |
||||||
|
|
||||||
|
Defining undefined Functions and Filters on the Fly |
||||||
|
--------------------------------------------------- |
||||||
|
|
||||||
|
When a function (or a filter) is not defined, Twig defaults to throw a |
||||||
|
``Twig_Error_Syntax`` exception. However, it can also call a `callback`_ (any |
||||||
|
valid PHP callable) which should return a function (or a filter). |
||||||
|
|
||||||
|
For filters, register callbacks with ``registerUndefinedFilterCallback()``. |
||||||
|
For functions, use ``registerUndefinedFunctionCallback()``:: |
||||||
|
|
||||||
|
// auto-register all native PHP functions as Twig functions |
||||||
|
// don't try this at home as it's not secure at all! |
||||||
|
$twig->registerUndefinedFunctionCallback(function ($name) { |
||||||
|
if (function_exists($name)) { |
||||||
|
return new Twig_Function_Function($name); |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
}); |
||||||
|
|
||||||
|
If the callable is not able to return a valid function (or filter), it must |
||||||
|
return ``false``. |
||||||
|
|
||||||
|
If you register more than one callback, Twig will call them in turn until one |
||||||
|
does not return ``false``. |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
As the resolution of functions and filters is done during compilation, |
||||||
|
there is no overhead when registering these callbacks. |
||||||
|
|
||||||
|
Validating the Template Syntax |
||||||
|
------------------------------ |
||||||
|
|
||||||
|
When template code is providing by a third-party (through a web interface for |
||||||
|
instance), it might be interesting to validate the template syntax before |
||||||
|
saving it. If the template code is stored in a `$template` variable, here is |
||||||
|
how you can do it:: |
||||||
|
|
||||||
|
try { |
||||||
|
$twig->parse($twig->tokenize($template)); |
||||||
|
|
||||||
|
// the $template is valid |
||||||
|
} catch (Twig_Error_Syntax $e) { |
||||||
|
// $template contains one or more syntax errors |
||||||
|
} |
||||||
|
|
||||||
|
Refreshing modified Templates when APC is enabled and apc.stat = 0 |
||||||
|
------------------------------------------------------------------ |
||||||
|
|
||||||
|
When using APC with ``apc.stat`` set to ``0`` and Twig cache enabled, clearing |
||||||
|
the template cache won't update the APC cache. To get around this, one can |
||||||
|
extend ``Twig_Environment`` and force the update of the APC cache when Twig |
||||||
|
rewrites the cache:: |
||||||
|
|
||||||
|
class Twig_Environment_APC extends Twig_Environment |
||||||
|
{ |
||||||
|
protected function writeCacheFile($file, $content) |
||||||
|
{ |
||||||
|
parent::writeCacheFile($file, $content); |
||||||
|
|
||||||
|
// Compile cached file into bytecode cache |
||||||
|
apc_compile_file($file); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Reusing a stateful Node Visitor |
||||||
|
------------------------------- |
||||||
|
|
||||||
|
When attaching a visitor to a ``Twig_Environment`` instance, Twig uses it to |
||||||
|
visit *all* templates it compiles. If you need to keep some state information |
||||||
|
around, you probably want to reset it when visiting a new template. |
||||||
|
|
||||||
|
This can be easily achieved with the following code:: |
||||||
|
|
||||||
|
protected $someTemplateState = array(); |
||||||
|
|
||||||
|
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) |
||||||
|
{ |
||||||
|
if ($node instanceof Twig_Node_Module) { |
||||||
|
// reset the state as we are entering a new template |
||||||
|
$this->someTemplateState = array(); |
||||||
|
} |
||||||
|
|
||||||
|
// ... |
||||||
|
|
||||||
|
return $node; |
||||||
|
} |
||||||
|
|
||||||
|
.. _callback: http://www.php.net/manual/en/function.is-callable.php |
@ -0,0 +1,43 @@ |
|||||||
|
``autoescape`` |
||||||
|
============== |
||||||
|
|
||||||
|
Whether automatic escaping is enabled or not, you can mark a section of a |
||||||
|
template to be escaped or not by using the ``autoescape`` tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% autoescape true %} |
||||||
|
Everything will be automatically escaped in this block |
||||||
|
{% endautoescape %} |
||||||
|
|
||||||
|
{% autoescape false %} |
||||||
|
Everything will be outputed as is in this block |
||||||
|
{% endautoescape %} |
||||||
|
|
||||||
|
{% autoescape true js %} |
||||||
|
Everything will be automatically escaped in this block |
||||||
|
using the js escaping strategy |
||||||
|
{% endautoescape %} |
||||||
|
|
||||||
|
When automatic escaping is enabled everything is escaped by default except for |
||||||
|
values explicitly marked as safe. Those can be marked in the template by using |
||||||
|
the :doc:`raw<../filters/raw>` filter: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% autoescape true %} |
||||||
|
{{ safe_value|raw }} |
||||||
|
{% endautoescape %} |
||||||
|
|
||||||
|
Functions returning template data (like :doc:`macros<macro>` and |
||||||
|
:doc:`parent<../functions/parent>`) always return safe markup. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Twig is smart enough to not escape an already escaped value by the |
||||||
|
:doc:`escape<../filters/escape>` filter. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
The chapter :doc:`Twig for Developers<../api>` gives more information |
||||||
|
about when and how automatic escaping is applied. |
@ -0,0 +1,11 @@ |
|||||||
|
``block`` |
||||||
|
========= |
||||||
|
|
||||||
|
Blocks are used for inheritance and act as placeholders and replacements at |
||||||
|
the same time. They are documented in detail in the documentation for the |
||||||
|
:doc:`extends<../tags/extends>` tag. |
||||||
|
|
||||||
|
Block names should consist of alphanumeric characters, and underscores. Dashes |
||||||
|
are not permitted. |
||||||
|
|
||||||
|
.. seealso:: :doc:`block<../functions/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`, :doc:`extends<../tags/extends>` |
@ -0,0 +1,12 @@ |
|||||||
|
``do`` |
||||||
|
====== |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
The do tag was added in Twig 1.5. |
||||||
|
|
||||||
|
The ``do`` tag works exactly like the regular variable expression (``{{ ... |
||||||
|
}}``) just that it doesn't print anything: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% do 1 + 2 %} |
@ -0,0 +1,187 @@ |
|||||||
|
``extends`` |
||||||
|
=========== |
||||||
|
|
||||||
|
The ``extends`` tag can be used to extend a template from another one. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Like PHP, Twig does not support multiple inheritance. So you can only have |
||||||
|
one extends tag called per rendering. However, Twig supports horizontal |
||||||
|
:doc:`reuse<use>`. |
||||||
|
|
||||||
|
Let's define a base template, ``base.html``, which defines a simple HTML |
||||||
|
skeleton document: |
||||||
|
|
||||||
|
.. code-block:: html+jinja |
||||||
|
|
||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
{% block head %} |
||||||
|
<link rel="stylesheet" href="style.css" /> |
||||||
|
<title>{% block title %}{% endblock %} - My Webpage</title> |
||||||
|
{% endblock %} |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="content">{% block content %}{% endblock %}</div> |
||||||
|
<div id="footer"> |
||||||
|
{% block footer %} |
||||||
|
© Copyright 2011 by <a href="http://domain.invalid/">you</a>. |
||||||
|
{% endblock %} |
||||||
|
</div> |
||||||
|
</body> |
||||||
|
</html> |
||||||
|
|
||||||
|
In this example, the :doc:`block<block>` tags define four blocks that child |
||||||
|
templates can fill in. All the ``block`` tag does is to tell the template |
||||||
|
engine that a child template may override those portions of the template. |
||||||
|
|
||||||
|
Child Template |
||||||
|
-------------- |
||||||
|
|
||||||
|
A child template might look like this: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends "base.html" %} |
||||||
|
|
||||||
|
{% block title %}Index{% endblock %} |
||||||
|
{% block head %} |
||||||
|
{{ parent() }} |
||||||
|
<style type="text/css"> |
||||||
|
.important { color: #336699; } |
||||||
|
</style> |
||||||
|
{% endblock %} |
||||||
|
{% block content %} |
||||||
|
<h1>Index</h1> |
||||||
|
<p class="important"> |
||||||
|
Welcome on my awesome homepage. |
||||||
|
</p> |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
The ``extends`` tag is the key here. It tells the template engine that this |
||||||
|
template "extends" another template. When the template system evaluates this |
||||||
|
template, first it locates the parent. The extends tag should be the first tag |
||||||
|
in the template. |
||||||
|
|
||||||
|
Note that since the child template doesn't define the ``footer`` block, the |
||||||
|
value from the parent template is used instead. |
||||||
|
|
||||||
|
You can't define multiple ``block`` tags with the same name in the same |
||||||
|
template. This limitation exists because a block tag works in "both" |
||||||
|
directions. That is, a block tag doesn't just provide a hole to fill - it also |
||||||
|
defines the content that fills the hole in the *parent*. If there were two |
||||||
|
similarly-named ``block`` tags in a template, that template's parent wouldn't |
||||||
|
know which one of the blocks' content to use. |
||||||
|
|
||||||
|
If you want to print a block multiple times you can however use the |
||||||
|
``block`` function: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<title>{% block title %}{% endblock %}</title> |
||||||
|
<h1>{{ block('title') }}</h1> |
||||||
|
{% block body %}{% endblock %} |
||||||
|
|
||||||
|
Parent Blocks |
||||||
|
------------- |
||||||
|
|
||||||
|
It's possible to render the contents of the parent block by using the |
||||||
|
:doc:`parent<../functions/parent>` function. This gives back the results of |
||||||
|
the parent block: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% block sidebar %} |
||||||
|
<h3>Table Of Contents</h3> |
||||||
|
... |
||||||
|
{{ parent() }} |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
Named Block End-Tags |
||||||
|
-------------------- |
||||||
|
|
||||||
|
Twig allows you to put the name of the block after the end tag for better |
||||||
|
readability: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% block sidebar %} |
||||||
|
{% block inner_sidebar %} |
||||||
|
... |
||||||
|
{% endblock inner_sidebar %} |
||||||
|
{% endblock sidebar %} |
||||||
|
|
||||||
|
Of course, the name after the ``endblock`` word must match the block name. |
||||||
|
|
||||||
|
Block Nesting and Scope |
||||||
|
----------------------- |
||||||
|
|
||||||
|
Blocks can be nested for more complex layouts. Per default, blocks have access |
||||||
|
to variables from outer scopes: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for item in seq %} |
||||||
|
<li>{% block loop_item %}{{ item }}{% endblock %}</li> |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
Block Shortcuts |
||||||
|
--------------- |
||||||
|
|
||||||
|
For blocks with few content, it's possible to use a shortcut syntax. The |
||||||
|
following constructs do the same: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% block title %} |
||||||
|
{{ page_title|title }} |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% block title page_title|title %} |
||||||
|
|
||||||
|
Dynamic Inheritance |
||||||
|
------------------- |
||||||
|
|
||||||
|
Twig supports dynamic inheritance by using a variable as the base template: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends some_var %} |
||||||
|
|
||||||
|
If the variable evaluates to a ``Twig_Template`` object, Twig will use it as |
||||||
|
the parent template:: |
||||||
|
|
||||||
|
// {% extends layout %} |
||||||
|
|
||||||
|
$layout = $twig->loadTemplate('some_layout_template.twig'); |
||||||
|
|
||||||
|
$twig->display('template.twig', array('layout' => $layout)); |
||||||
|
|
||||||
|
.. versionadded:: 1.2 |
||||||
|
The possibility to pass an array of templates has been added in Twig 1.2. |
||||||
|
|
||||||
|
You can also provide a list of templates that are checked for existence. The |
||||||
|
first template that exists will be used as a parent: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends ['layout.html', 'base_layout.html'] %} |
||||||
|
|
||||||
|
Conditional Inheritance |
||||||
|
----------------------- |
||||||
|
|
||||||
|
As the template name for the parent can be any valid Twig expression, it's |
||||||
|
possible to make the inheritance mechanism conditional: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends standalone ? "minimum.html" : "base.html" %} |
||||||
|
|
||||||
|
In this example, the template will extend the "minimum.html" layout template |
||||||
|
if the ``standalone`` variable evaluates to ``true``, and "base.html" |
||||||
|
otherwise. |
||||||
|
|
||||||
|
.. seealso:: :doc:`block<../functions/block>`, :doc:`block<../tags/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>` |
@ -0,0 +1,21 @@ |
|||||||
|
``filter`` |
||||||
|
========== |
||||||
|
|
||||||
|
Filter sections allow you to apply regular Twig filters on a block of template |
||||||
|
data. Just wrap the code in the special ``filter`` section: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% filter upper %} |
||||||
|
This text becomes uppercase |
||||||
|
{% endfilter %} |
||||||
|
|
||||||
|
You can also chain filters: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% filter lower|escape %} |
||||||
|
<strong>SOME TEXT</strong> |
||||||
|
{% endfilter %} |
||||||
|
|
||||||
|
{# outputs "<strong>some text</strong>" #} |
@ -0,0 +1,17 @@ |
|||||||
|
``flush`` |
||||||
|
========= |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
The flush tag was added in Twig 1.5. |
||||||
|
|
||||||
|
The ``flush`` tag tells Twig to flush the output buffer: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% flush %} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Internally, Twig uses the PHP `flush`_ function. |
||||||
|
|
||||||
|
.. _`flush`: http://php.net/flush |
@ -0,0 +1,149 @@ |
|||||||
|
``for`` |
||||||
|
======= |
||||||
|
|
||||||
|
Loop over each item in a sequence. For example, to display a list of users |
||||||
|
provided in a variable called ``users``: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<h1>Members</h1> |
||||||
|
<ul> |
||||||
|
{% for user in users %} |
||||||
|
<li>{{ user.username|e }}</li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
A sequence can be either an array or an object implementing the |
||||||
|
``Traversable`` interface. |
||||||
|
|
||||||
|
If you do need to iterate over a sequence of numbers, you can use the ``..`` |
||||||
|
operator: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for i in 0..10 %} |
||||||
|
* {{ i }} |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
The above snippet of code would print all numbers from 0 to 10. |
||||||
|
|
||||||
|
It can be also useful with letters: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for letter in 'a'..'z' %} |
||||||
|
* {{ letter }} |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
The ``..`` operator can take any expression at both sides: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for letter in 'a'|upper..'z'|upper %} |
||||||
|
* {{ letter }} |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
.. tip: |
||||||
|
|
||||||
|
If you need a step different from 1, you can use the ``range`` function |
||||||
|
instead. |
||||||
|
|
||||||
|
The `loop` variable |
||||||
|
------------------- |
||||||
|
|
||||||
|
Inside of a ``for`` loop block you can access some special variables: |
||||||
|
|
||||||
|
===================== ============================================================= |
||||||
|
Variable Description |
||||||
|
===================== ============================================================= |
||||||
|
``loop.index`` The current iteration of the loop. (1 indexed) |
||||||
|
``loop.index0`` The current iteration of the loop. (0 indexed) |
||||||
|
``loop.revindex`` The number of iterations from the end of the loop (1 indexed) |
||||||
|
``loop.revindex0`` The number of iterations from the end of the loop (0 indexed) |
||||||
|
``loop.first`` True if first iteration |
||||||
|
``loop.last`` True if last iteration |
||||||
|
``loop.length`` The number of items in the sequence |
||||||
|
``loop.parent`` The parent context |
||||||
|
===================== ============================================================= |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
The ``loop.length``, ``loop.revindex``, ``loop.revindex0``, and |
||||||
|
``loop.last`` variables are only available for PHP arrays, or objects that |
||||||
|
implement the ``Countable`` interface. |
||||||
|
|
||||||
|
.. versionadded:: 1.2 |
||||||
|
The ``if`` modifier support has been added in Twig 1.2. |
||||||
|
|
||||||
|
Adding a condition |
||||||
|
------------------ |
||||||
|
|
||||||
|
Unlike in PHP, it's not possible to ``break`` or ``continue`` in a loop. You |
||||||
|
can however filter the sequence during iteration which allows you to skip |
||||||
|
items. The following example skips all the users which are not active: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<ul> |
||||||
|
{% for user in users if user.active %} |
||||||
|
<li>{{ user.username|e }}</li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
|
||||||
|
The advantage is that the special loop variable will count correctly thus not |
||||||
|
counting the users not iterated over. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Using the ``loop`` variable within the condition is not recommended as it |
||||||
|
will probably not be doing what you expect it to. For instance, adding a |
||||||
|
condition like ``loop.index > 4`` won't work as the index is only |
||||||
|
incremented when the condition is true (so the condition will never |
||||||
|
match). |
||||||
|
|
||||||
|
The `else` Clause |
||||||
|
----------------- |
||||||
|
|
||||||
|
If no iteration took place because the sequence was empty, you can render a |
||||||
|
replacement block by using ``else``: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<ul> |
||||||
|
{% for user in users %} |
||||||
|
<li>{{ user.username|e }}</li> |
||||||
|
{% else %} |
||||||
|
<li><em>no user found</em></li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
|
||||||
|
Iterating over Keys |
||||||
|
------------------- |
||||||
|
|
||||||
|
By default, a loop iterates over the values of the sequence. You can iterate |
||||||
|
on keys by using the ``keys`` filter: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<h1>Members</h1> |
||||||
|
<ul> |
||||||
|
{% for key in users|keys %} |
||||||
|
<li>{{ key }}</li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
|
||||||
|
Iterating over Keys and Values |
||||||
|
------------------------------ |
||||||
|
|
||||||
|
You can also access both keys and values: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<h1>Members</h1> |
||||||
|
<ul> |
||||||
|
{% for key, user in users %} |
||||||
|
<li>{{ key }}: {{ user.username|e }}</li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
@ -0,0 +1,8 @@ |
|||||||
|
``from`` |
||||||
|
======== |
||||||
|
|
||||||
|
The ``from`` tags import :doc:`macro<../tags/macro>` names into the current |
||||||
|
namespace. The tag is documented in detail in the documentation for the |
||||||
|
:doc:`import<../tags/import>` tag. |
||||||
|
|
||||||
|
.. seealso:: :doc:`macro<../tags/macro>`, :doc:`import<../tags/import>` |
@ -0,0 +1,43 @@ |
|||||||
|
``if`` |
||||||
|
====== |
||||||
|
|
||||||
|
The ``if`` statement in Twig is comparable with the if statements of PHP. |
||||||
|
|
||||||
|
In the simplest form you can use it to test if an expression evaluates to |
||||||
|
``true``: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if online == false %} |
||||||
|
<p>Our website is in maintenance mode. Please, come back later.</p> |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
You can also test if an array is not empty: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if users %} |
||||||
|
<ul> |
||||||
|
{% for user in users %} |
||||||
|
<li>{{ user.username|e }}</li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
If you want to test if the variable is defined, use ``if users is |
||||||
|
defined`` instead. |
||||||
|
|
||||||
|
For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can use |
||||||
|
more complex ``expressions`` there too: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if kenny.sick %} |
||||||
|
Kenny is sick. |
||||||
|
{% elseif kenny.dead %} |
||||||
|
You killed Kenny! You bastard!!! |
||||||
|
{% else %} |
||||||
|
Kenny looks okay --- so far |
||||||
|
{% endif %} |
@ -0,0 +1,79 @@ |
|||||||
|
``import`` |
||||||
|
========== |
||||||
|
|
||||||
|
Twig supports putting often used code into :doc:`macros<../tags/macro>`. These |
||||||
|
macros can go into different templates and get imported from there. |
||||||
|
|
||||||
|
There are two ways to import templates. You can import the complete template |
||||||
|
into a variable or request specific macros from it. |
||||||
|
|
||||||
|
Imagine we have a helper module that renders forms (called ``forms.html``): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% macro input(name, value, type, size) %} |
||||||
|
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" /> |
||||||
|
{% endmacro %} |
||||||
|
|
||||||
|
{% macro textarea(name, value, rows) %} |
||||||
|
<textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea> |
||||||
|
{% endmacro %} |
||||||
|
|
||||||
|
The easiest and most flexible is importing the whole module into a variable. |
||||||
|
That way you can access the attributes: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% import 'forms.html' as forms %} |
||||||
|
|
||||||
|
<dl> |
||||||
|
<dt>Username</dt> |
||||||
|
<dd>{{ forms.input('username') }}</dd> |
||||||
|
<dt>Password</dt> |
||||||
|
<dd>{{ forms.input('password', null, 'password') }}</dd> |
||||||
|
</dl> |
||||||
|
<p>{{ forms.textarea('comment') }}</p> |
||||||
|
|
||||||
|
Alternatively you can import names from the template into the current |
||||||
|
namespace: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% from 'forms.html' import input as input_field, textarea %} |
||||||
|
|
||||||
|
<dl> |
||||||
|
<dt>Username</dt> |
||||||
|
<dd>{{ input_field('username') }}</dd> |
||||||
|
<dt>Password</dt> |
||||||
|
<dd>{{ input_field('password', '', 'password') }}</dd> |
||||||
|
</dl> |
||||||
|
<p>{{ textarea('comment') }}</p> |
||||||
|
|
||||||
|
Importing is not needed if the macros and the template are defined in the same |
||||||
|
file; use the special ``_self`` variable instead: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# index.html template #} |
||||||
|
|
||||||
|
{% macro textarea(name, value, rows) %} |
||||||
|
<textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea> |
||||||
|
{% endmacro %} |
||||||
|
|
||||||
|
<p>{{ _self.textarea('comment') }}</p> |
||||||
|
|
||||||
|
But you can still create an alias by importing from the ``_self`` variable: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# index.html template #} |
||||||
|
|
||||||
|
{% macro textarea(name, value, rows) %} |
||||||
|
<textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea> |
||||||
|
{% endmacro %} |
||||||
|
|
||||||
|
{% import _self as forms %} |
||||||
|
|
||||||
|
<p>{{ forms.textarea('comment') }}</p> |
||||||
|
|
||||||
|
.. seealso:: :doc:`macro<../tags/macro>`, :doc:`from<../tags/from>` |
@ -0,0 +1,86 @@ |
|||||||
|
``include`` |
||||||
|
=========== |
||||||
|
|
||||||
|
The ``include`` statement includes a template and return the rendered content |
||||||
|
of that file into the current namespace: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% include 'header.html' %} |
||||||
|
Body |
||||||
|
{% include 'footer.html' %} |
||||||
|
|
||||||
|
Included templates have access to the variables of the active context. |
||||||
|
|
||||||
|
If you are using the filesystem loader, the templates are looked for in the |
||||||
|
paths defined by it. |
||||||
|
|
||||||
|
You can add additional variables by passing them after the ``with`` keyword: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# the foo template will have access to the variables from the current context and the foo one #} |
||||||
|
{% include 'foo' with {'foo': 'bar'} %} |
||||||
|
|
||||||
|
{% set vars = {'foo': 'bar'} %} |
||||||
|
{% include 'foo' with vars %} |
||||||
|
|
||||||
|
You can disable access to the context by appending the ``only`` keyword: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# only the foo variable will be accessible #} |
||||||
|
{% include 'foo' with {'foo': 'bar'} only %} |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# no variable will be accessible #} |
||||||
|
{% include 'foo' only %} |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
When including a template created by an end user, you should consider |
||||||
|
sandboxing it. More information in the :doc:`Twig for Developers<../api>` |
||||||
|
chapter. |
||||||
|
|
||||||
|
The template name can be any valid Twig expression: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% include some_var %} |
||||||
|
{% include ajax ? 'ajax.html' : 'not_ajax.html' %} |
||||||
|
|
||||||
|
And if the expression evaluates to a ``Twig_Template`` object, Twig will use it |
||||||
|
directly:: |
||||||
|
|
||||||
|
// {% include template %} |
||||||
|
|
||||||
|
$template = $twig->loadTemplate('some_template.twig'); |
||||||
|
|
||||||
|
$twig->loadTemplate('template.twig')->display(array('template' => $template)); |
||||||
|
|
||||||
|
.. versionadded:: 1.2 |
||||||
|
The ``ignore missing`` feature has been added in Twig 1.2. |
||||||
|
|
||||||
|
You can mark an include with ``ignore missing`` in which case Twig will ignore |
||||||
|
the statement if the template to be ignored does not exist. It has to be |
||||||
|
placed just after the template name. Here some valid examples: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% include "sidebar.html" ignore missing %} |
||||||
|
{% include "sidebar.html" ignore missing with {'foo': 'bar} %} |
||||||
|
{% include "sidebar.html" ignore missing only %} |
||||||
|
|
||||||
|
.. versionadded:: 1.2 |
||||||
|
The possibility to pass an array of templates has been added in Twig 1.2. |
||||||
|
|
||||||
|
You can also provide a list of templates that are checked for existence before |
||||||
|
inclusion. The first template that exists will be included: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% include ['page_detailed.html', 'page.html'] %} |
||||||
|
|
||||||
|
If ``ignore missing`` is given, it will fall back to rendering nothing if none |
||||||
|
of the templates exist, otherwise it will throw an exception. |
@ -0,0 +1,22 @@ |
|||||||
|
Tags |
||||||
|
==== |
||||||
|
|
||||||
|
.. toctree:: |
||||||
|
:maxdepth: 1 |
||||||
|
|
||||||
|
for |
||||||
|
if |
||||||
|
macro |
||||||
|
filter |
||||||
|
set |
||||||
|
extends |
||||||
|
block |
||||||
|
include |
||||||
|
import |
||||||
|
from |
||||||
|
use |
||||||
|
spaceless |
||||||
|
autoescape |
||||||
|
raw |
||||||
|
flush |
||||||
|
do |
@ -0,0 +1,91 @@ |
|||||||
|
``macro`` |
||||||
|
========= |
||||||
|
|
||||||
|
Macros are comparable with functions in regular programming languages. They |
||||||
|
are useful to put often used HTML idioms into reusable elements to not repeat |
||||||
|
yourself. |
||||||
|
|
||||||
|
Here is a small example of a macro that renders a form element: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% macro input(name, value, type, size) %} |
||||||
|
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" /> |
||||||
|
{% endmacro %} |
||||||
|
|
||||||
|
Macros differs from native PHP functions in a few ways: |
||||||
|
|
||||||
|
* Default argument values are defined by using the ``default`` filter in the |
||||||
|
macro body; |
||||||
|
|
||||||
|
* Arguments of a macro are always optional. |
||||||
|
|
||||||
|
But as PHP functions, macros don't have access to the current template |
||||||
|
variables. |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
You can pass the whole context as an argument by using the special |
||||||
|
``_context`` variable. |
||||||
|
|
||||||
|
Macros can be defined in any template, and need to be "imported" before being |
||||||
|
used (see the documentation for the :doc:`import<../tags/import>` tag for more |
||||||
|
information): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% import "forms.html" as forms %} |
||||||
|
|
||||||
|
The above ``import`` call imports the "forms.html" file (which can contain only |
||||||
|
macros, or a template and some macros), and import the functions as items of |
||||||
|
the ``forms`` variable. |
||||||
|
|
||||||
|
The macro can then be called at will: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<p>{{ forms.input('username') }}</p> |
||||||
|
<p>{{ forms.input('password', null, 'password') }}</p> |
||||||
|
|
||||||
|
If macros are defined and used in the same template, you can use the |
||||||
|
special ``_self`` variable, without importing them: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<p>{{ _self.input('username') }}</p> |
||||||
|
|
||||||
|
When you want to use a macro in another one from the same file, use the ``_self`` |
||||||
|
variable: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% macro input(name, value, type, size) %} |
||||||
|
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" /> |
||||||
|
{% endmacro %} |
||||||
|
|
||||||
|
{% macro wrapped_input(name, value, type, size) %} |
||||||
|
<div class="field"> |
||||||
|
{{ _self.input(name, value, type, size) }} |
||||||
|
</div> |
||||||
|
{% endmacro %} |
||||||
|
|
||||||
|
When the macro is defined in another file, you need to import it: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# forms.html #} |
||||||
|
|
||||||
|
{% macro input(name, value, type, size) %} |
||||||
|
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" /> |
||||||
|
{% endmacro %} |
||||||
|
|
||||||
|
{# shortcuts.html #} |
||||||
|
|
||||||
|
{% macro wrapped_input(name, value, type, size) %} |
||||||
|
{% import "forms.html" as forms %} |
||||||
|
<div class="field"> |
||||||
|
{{ forms.input(name, value, type, size) }} |
||||||
|
</div> |
||||||
|
{% endmacro %} |
||||||
|
|
||||||
|
.. seealso:: :doc:`from<../tags/from>`, :doc:`import<../tags/import>` |
@ -0,0 +1,16 @@ |
|||||||
|
``raw`` |
||||||
|
======= |
||||||
|
|
||||||
|
The ``raw`` tag marks sections as being raw text that should not be parsed. |
||||||
|
For example to put Twig syntax as example into a template you can use this |
||||||
|
snippet: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% raw %} |
||||||
|
<ul> |
||||||
|
{% for item in seq %} |
||||||
|
<li>{{ item }}</li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
{% endraw %} |
@ -0,0 +1,32 @@ |
|||||||
|
``set`` |
||||||
|
======= |
||||||
|
|
||||||
|
Inside code blocks you can also assign values to variables. Assignments use |
||||||
|
the ``set`` tag and can have multiple targets: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set foo = 'foo' %} |
||||||
|
|
||||||
|
{% set foo = [1, 2] %} |
||||||
|
|
||||||
|
{% set foo = {'foo': 'bar'} %} |
||||||
|
|
||||||
|
{% set foo = 'foo' ~ 'bar' %} |
||||||
|
|
||||||
|
{% set foo, bar = 'foo', 'bar' %} |
||||||
|
|
||||||
|
The ``set`` tag can also be used to 'capture' chunks of text: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set foo %} |
||||||
|
<div id="pagination"> |
||||||
|
... |
||||||
|
</div> |
||||||
|
{% endset %} |
||||||
|
|
||||||
|
.. caution:: |
||||||
|
|
||||||
|
If you enable automatic output escaping, Twig will only consider the |
||||||
|
content to be safe when capturing chunks of text. |
@ -0,0 +1,37 @@ |
|||||||
|
``spaceless`` |
||||||
|
============= |
||||||
|
|
||||||
|
Use the ``spaceless`` tag to remove whitespace *between HTML tags*, not |
||||||
|
whitespace within HTML tags or whitespace in plain text: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% spaceless %} |
||||||
|
<div> |
||||||
|
<strong>foo</strong> |
||||||
|
</div> |
||||||
|
{% endspaceless %} |
||||||
|
|
||||||
|
{# output will be <div><strong>foo</strong></div> #} |
||||||
|
|
||||||
|
This tag is not meant to "optimize" the size of the generated HTML content but |
||||||
|
merely to avoid extra whitespace between HTML tags to avoid browser rendering |
||||||
|
quirks under some circumstances. |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
If you want to optimize the size of the generated HTML content, gzip |
||||||
|
compress the output instead. |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
If you want to create a tag that actually removes all extra whitespace in |
||||||
|
an HTML string, be warned that this is not as easy as it seems to be |
||||||
|
(think of ``textarea`` or ``pre`` tags for instance). Using a third-party |
||||||
|
library like Tidy is probably a better idea. |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
For more information on whitespace control, read the |
||||||
|
:doc:`dedicated<../templates>` section of the documentation and learn how |
||||||
|
you can also use the whitespace control modifier on your tags. |
@ -0,0 +1,123 @@ |
|||||||
|
``use`` |
||||||
|
======= |
||||||
|
|
||||||
|
.. versionadded:: 1.1 |
||||||
|
Horizontal reuse was added in Twig 1.1. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Horizontal reuse is an advanced Twig feature that is hardly ever needed in |
||||||
|
regular templates. It is mainly used by projects that need to make |
||||||
|
template blocks reusable without using inheritance. |
||||||
|
|
||||||
|
Template inheritance is one of the most powerful Twig's feature but it is |
||||||
|
limited to single inheritance; a template can only extend one other template. |
||||||
|
This limitation makes template inheritance simple to understand and easy to |
||||||
|
debug: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends "base.html" %} |
||||||
|
|
||||||
|
{% block title %}{% endblock %} |
||||||
|
{% block content %}{% endblock %} |
||||||
|
|
||||||
|
Horizontal reuse is a way to achieve the same goal as multiple inheritance, |
||||||
|
but without the associated complexity: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends "base.html" %} |
||||||
|
|
||||||
|
{% use "blocks.html" %} |
||||||
|
|
||||||
|
{% block title %}{% endblock %} |
||||||
|
{% block content %}{% endblock %} |
||||||
|
|
||||||
|
The ``use`` statement tells Twig to import the blocks defined in |
||||||
|
```blocks.html`` into the current template (it's like macros, but for blocks): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
# blocks.html |
||||||
|
{% block sidebar %}{% endblock %} |
||||||
|
|
||||||
|
In this example, the ``use`` statement imports the ``sidebar`` block into the |
||||||
|
main template. The code is mostly equivalent to the following one (the |
||||||
|
imported blocks are not outputted automatically): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends "base.html" %} |
||||||
|
|
||||||
|
{% block sidebar %}{% endblock %} |
||||||
|
{% block title %}{% endblock %} |
||||||
|
{% block content %}{% endblock %} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
The ``use`` tag only imports a template if it does not extend another |
||||||
|
template, if it does not define macros, and if the body is empty. But it |
||||||
|
can *use* other templates. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Because ``use`` statements are resolved independently of the context |
||||||
|
passed to the template, the template reference cannot be an expression. |
||||||
|
|
||||||
|
The main template can also override any imported block. If the template |
||||||
|
already defines the ``sidebar`` block, then the one defined in ``blocks.html`` |
||||||
|
is ignored. To avoid name conflicts, you can rename imported blocks: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends "base.html" %} |
||||||
|
|
||||||
|
{% use "blocks.html" with sidebar as base_sidebar %} |
||||||
|
|
||||||
|
{% block sidebar %}{% endblock %} |
||||||
|
{% block title %}{% endblock %} |
||||||
|
{% block content %}{% endblock %} |
||||||
|
|
||||||
|
.. versionadded:: 1.3 |
||||||
|
The ``parent()`` support was added in Twig 1.3. |
||||||
|
|
||||||
|
The ``parent()`` function automatically determines the correct inheritance |
||||||
|
tree, so it can be used when overriding a block defined in an imported |
||||||
|
template: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends "base.html" %} |
||||||
|
|
||||||
|
{% use "blocks.html" %} |
||||||
|
|
||||||
|
{% block sidebar %} |
||||||
|
{{ parent() }} |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
{% block title %}{% endblock %} |
||||||
|
{% block content %}{% endblock %} |
||||||
|
|
||||||
|
In this example, ``parent()`` will correctly call the ``sidebar`` block from |
||||||
|
the ``blocks.html`` template. |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
In Twig 1.2, renaming allows you to simulate inheritance by calling the |
||||||
|
"parent" block: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends "base.html" %} |
||||||
|
|
||||||
|
{% use "blocks.html" with sidebar as parent_sidebar %} |
||||||
|
|
||||||
|
{% block sidebar %} |
||||||
|
{{ block('parent_sidebar') }} |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
You can use as many ``use`` statements as you want in any given template. |
||||||
|
If two imported templates define the same block, the latest one wins. |
@ -0,0 +1,719 @@ |
|||||||
|
Twig for Template Designers |
||||||
|
=========================== |
||||||
|
|
||||||
|
This document describes the syntax and semantics of the template engine and |
||||||
|
will be most useful as reference to those creating Twig templates. |
||||||
|
|
||||||
|
Synopsis |
||||||
|
-------- |
||||||
|
|
||||||
|
A template is simply a text file. It can generate any text-based format (HTML, |
||||||
|
XML, CSV, LaTeX, etc.). It doesn't have a specific extension, ``.html`` or |
||||||
|
``.xml`` are just fine. |
||||||
|
|
||||||
|
A template contains **variables** or **expressions**, which get replaced with |
||||||
|
values when the template is evaluated, and **tags**, which control the logic |
||||||
|
of the template. |
||||||
|
|
||||||
|
Below is a minimal template that illustrates a few basics. We will cover the |
||||||
|
details later on: |
||||||
|
|
||||||
|
.. code-block:: html+jinja |
||||||
|
|
||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>My Webpage</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<ul id="navigation"> |
||||||
|
{% for item in navigation %} |
||||||
|
<li><a href="{{ item.href }}">{{ item.caption }}</a></li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
|
||||||
|
<h1>My Webpage</h1> |
||||||
|
{{ a_variable }} |
||||||
|
</body> |
||||||
|
</html> |
||||||
|
|
||||||
|
There are two kinds of delimiters: ``{% ... %}`` and ``{{ ... }}``. The first |
||||||
|
one is used to execute statements such as for-loops, the latter prints the |
||||||
|
result of an expression to the template. |
||||||
|
|
||||||
|
IDEs Integration |
||||||
|
---------------- |
||||||
|
|
||||||
|
Many IDEs support syntax highlighting and auto-completion for Twig: |
||||||
|
|
||||||
|
* *Textmate* via the `Twig bundle`_ |
||||||
|
* *Vim* via the `Jinja syntax plugin`_ |
||||||
|
* *Netbeans* via the `Twig syntax plugin`_ |
||||||
|
* *PhpStorm* (native as of 2.1) |
||||||
|
* *Eclipse* via the `Twig plugin`_ |
||||||
|
* *Sublime Text* via the `Twig bundle`_ |
||||||
|
* *GtkSourceView* via the `Twig language definition`_ (used by gedit and other projects) |
||||||
|
* *Coda* and *SubEthaEdit* via the `Twig syntax mode`_ |
||||||
|
|
||||||
|
Variables |
||||||
|
--------- |
||||||
|
|
||||||
|
The application passes variables to the templates you can mess around in the |
||||||
|
template. Variables may have attributes or elements on them you can access |
||||||
|
too. How a variable looks like heavily depends on the application providing |
||||||
|
those. |
||||||
|
|
||||||
|
You can use a dot (``.``) to access attributes of a variable (methods or |
||||||
|
properties of a PHP object, or items of a PHP array), or the so-called |
||||||
|
"subscript" syntax (``[]``): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ foo.bar }} |
||||||
|
{{ foo['bar'] }} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
It's important to know that the curly braces are *not* part of the |
||||||
|
variable but the print statement. If you access variables inside tags |
||||||
|
don't put the braces around. |
||||||
|
|
||||||
|
If a variable or attribute does not exist you will get back a ``null`` value. |
||||||
|
|
||||||
|
.. sidebar:: Implementation |
||||||
|
|
||||||
|
For convenience sake ``foo.bar`` does the following things on the PHP |
||||||
|
layer: |
||||||
|
|
||||||
|
* check if ``foo`` is an array and ``bar`` a valid element; |
||||||
|
* if not, and if ``foo`` is an object, check that ``bar`` is a valid property; |
||||||
|
* if not, and if ``foo`` is an object, check that ``bar`` is a valid method |
||||||
|
(even if ``bar`` is the constructor - use ``__construct()`` instead); |
||||||
|
* if not, and if ``foo`` is an object, check that ``getBar`` is a valid method; |
||||||
|
* if not, and if ``foo`` is an object, check that ``isBar`` is a valid method; |
||||||
|
* if not, return a ``null`` value. |
||||||
|
|
||||||
|
``foo['bar']`` on the other hand only works with PHP arrays: |
||||||
|
|
||||||
|
* check if ``foo`` is an array and ``bar`` a valid element; |
||||||
|
* if not, return a ``null`` value. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
If you want to get a dynamic attribute on a variable, use the |
||||||
|
:doc:`attribute<functions/attribute>` function instead. |
||||||
|
|
||||||
|
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. |
||||||
|
|
||||||
|
Setting Variables |
||||||
|
~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
You can assign values to variables inside code blocks. Assignments use the |
||||||
|
:doc:`set<tags/set>` tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set foo = 'foo' %} |
||||||
|
{% set foo = [1, 2] %} |
||||||
|
{% set foo = {'foo': 'bar'} %} |
||||||
|
|
||||||
|
Filters |
||||||
|
------- |
||||||
|
|
||||||
|
Variables can be modified by **filters**. Filters are separated from the |
||||||
|
variable by a pipe symbol (``|``) and may have optional arguments in |
||||||
|
parentheses. Multiple filters can be chained. The output of one filter is |
||||||
|
applied to the next. |
||||||
|
|
||||||
|
The following example removes all HTML tags from the ``name`` and title-cases |
||||||
|
it: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ name|striptags|title }} |
||||||
|
|
||||||
|
Filters that accept arguments have parentheses around the arguments. This |
||||||
|
example will join a list by commas: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ list|join(', ') }} |
||||||
|
|
||||||
|
To apply a filter on a section of code, wrap it with the |
||||||
|
:doc:`filter<tags/filter>` tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% filter upper %} |
||||||
|
This text becomes uppercase |
||||||
|
{% endfilter %} |
||||||
|
|
||||||
|
Go to the :doc:`filters<filters/index>` page to learn more about the built-in |
||||||
|
filters. |
||||||
|
|
||||||
|
Functions |
||||||
|
--------- |
||||||
|
|
||||||
|
Functions can be called to generate content. Functions are called by their |
||||||
|
name followed by parentheses (``()``) and may have arguments. |
||||||
|
|
||||||
|
For instance, the ``range`` function returns a list containing an arithmetic |
||||||
|
progression of integers: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for i in range(0, 3) %} |
||||||
|
{{ i }}, |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
Go to the :doc:`functions<functions/index>` page to learn more about the |
||||||
|
built-in functions. |
||||||
|
|
||||||
|
Control Structure |
||||||
|
----------------- |
||||||
|
|
||||||
|
A control structure refers to all those things that control the flow of a |
||||||
|
program - conditionals (i.e. ``if``/``elseif``/``else``), ``for``-loops, as |
||||||
|
well as things like blocks. Control structures appear inside ``{% ... %}`` |
||||||
|
blocks. |
||||||
|
|
||||||
|
For example, to display a list of users provided in a variable called |
||||||
|
``users``, use the :doc:`for<tags/for>` tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
<h1>Members</h1> |
||||||
|
<ul> |
||||||
|
{% for user in users %} |
||||||
|
<li>{{ user.username|e }}</li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
|
||||||
|
The :doc:`if<tags/if>` tag can be used to test an expression: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if users|length > 0 %} |
||||||
|
<ul> |
||||||
|
{% for user in users %} |
||||||
|
<li>{{ user.username|e }}</li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
Go to the :doc:`tags<tags/index>` page to learn more about the built-in tags. |
||||||
|
|
||||||
|
Comments |
||||||
|
-------- |
||||||
|
|
||||||
|
To comment-out part of a line in a template, use the comment syntax ``{# ... |
||||||
|
#}``. This is useful for debugging or to add information for other template |
||||||
|
designers or yourself: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# note: disabled template because we no longer use this |
||||||
|
{% for user in users %} |
||||||
|
... |
||||||
|
{% endfor %} |
||||||
|
#} |
||||||
|
|
||||||
|
Including other Templates |
||||||
|
------------------------- |
||||||
|
|
||||||
|
The :doc:`include<tags/include>` tag is useful to include a template and |
||||||
|
return the rendered content of that template into the current one: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% include 'sidebar.html' %} |
||||||
|
|
||||||
|
Per default included templates are passed the current context. |
||||||
|
|
||||||
|
The context that is passed to the included template includes variables defined |
||||||
|
in the template: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% for box in boxes %} |
||||||
|
{% include "render_box.html" %} |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
The included template ``render_box.html`` is able to access ``box``. |
||||||
|
|
||||||
|
The filename of the template depends on the template loader. For instance, the |
||||||
|
``Twig_Loader_Filesystem`` allows you to access other templates by giving the |
||||||
|
filename. You can access templates in subdirectories with a slash: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% include "sections/articles/sidebar.html" %} |
||||||
|
|
||||||
|
This behavior depends on the application embedding Twig. |
||||||
|
|
||||||
|
Template Inheritance |
||||||
|
-------------------- |
||||||
|
|
||||||
|
The most powerful part of Twig is template inheritance. Template inheritance |
||||||
|
allows you to build a base "skeleton" template that contains all the common |
||||||
|
elements of your site and defines **blocks** that child templates can |
||||||
|
override. |
||||||
|
|
||||||
|
Sounds complicated but is very basic. It's easiest to understand it by |
||||||
|
starting with an example. |
||||||
|
|
||||||
|
Let's define a base template, ``base.html``, which defines a simple HTML |
||||||
|
skeleton document that you might use for a simple two-column page: |
||||||
|
|
||||||
|
.. code-block:: html+jinja |
||||||
|
|
||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
{% block head %} |
||||||
|
<link rel="stylesheet" href="style.css" /> |
||||||
|
<title>{% block title %}{% endblock %} - My Webpage</title> |
||||||
|
{% endblock %} |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="content">{% block content %}{% endblock %}</div> |
||||||
|
<div id="footer"> |
||||||
|
{% block footer %} |
||||||
|
© Copyright 2011 by <a href="http://domain.invalid/">you</a>. |
||||||
|
{% endblock %} |
||||||
|
</div> |
||||||
|
</body> |
||||||
|
</html> |
||||||
|
|
||||||
|
In this example, the :doc:`block<tags/block>` tags define four blocks that |
||||||
|
child templates can fill in. All the ``block`` tag does is to tell the |
||||||
|
template engine that a child template may override those portions of the |
||||||
|
template. |
||||||
|
|
||||||
|
A child template might look like this: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% extends "base.html" %} |
||||||
|
|
||||||
|
{% block title %}Index{% endblock %} |
||||||
|
{% block head %} |
||||||
|
{{ parent() }} |
||||||
|
<style type="text/css"> |
||||||
|
.important { color: #336699; } |
||||||
|
</style> |
||||||
|
{% endblock %} |
||||||
|
{% block content %} |
||||||
|
<h1>Index</h1> |
||||||
|
<p class="important"> |
||||||
|
Welcome on my awesome homepage. |
||||||
|
</p> |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
The :doc:`extends<tags/extends>` tag is the key here. It tells the template |
||||||
|
engine that this template "extends" another template. When the template system |
||||||
|
evaluates this template, first it locates the parent. The extends tag should |
||||||
|
be the first tag in the template. |
||||||
|
|
||||||
|
Note that since the child template doesn't define the ``footer`` block, the |
||||||
|
value from the parent template is used instead. |
||||||
|
|
||||||
|
It's possible to render the contents of the parent block by using the |
||||||
|
:doc:`parent<functions/parent>` function. This gives back the results of the |
||||||
|
parent block: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% block sidebar %} |
||||||
|
<h3>Table Of Contents</h3> |
||||||
|
... |
||||||
|
{{ parent() }} |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
The documentation page for the :doc:`extends<tags/extends>` tag describes |
||||||
|
more advanced features like block nesting, scope, dynamic inheritance, and |
||||||
|
conditional inheritance. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Twig also supports multiple inheritance with the so called horizontal reuse |
||||||
|
with the help of the :doc:`use<tags/use>` tag. This is an advanced feature |
||||||
|
hardly ever needed in regular templates. |
||||||
|
|
||||||
|
HTML Escaping |
||||||
|
------------- |
||||||
|
|
||||||
|
When generating HTML from templates, there's always a risk that a variable |
||||||
|
will include characters that affect the resulting HTML. There are two |
||||||
|
approaches: manually escaping each variable or automatically escaping |
||||||
|
everything by default. |
||||||
|
|
||||||
|
Twig supports both, automatic escaping is enabled by default. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
Automatic escaping is only supported if the *escaper* extension has been |
||||||
|
enabled (which is the default). |
||||||
|
|
||||||
|
Working with Manual Escaping |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
If manual escaping is enabled it's **your** responsibility to escape variables |
||||||
|
if needed. What to escape? If you have a variable that *may* include any of |
||||||
|
the following chars (``>``, ``<``, ``&``, or ``"``) you **have to** escape it |
||||||
|
unless the variable contains well-formed and trusted HTML. Escaping works by |
||||||
|
piping the variable through the :doc:`escape<filters/escape>` or ``e`` filter: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ user.username|e }} |
||||||
|
{{ user.username|e('js') }} |
||||||
|
|
||||||
|
Working with Automatic Escaping |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Whether automatic escaping is enabled or not, you can mark a section of a |
||||||
|
template to be escaped or not by using the :doc:`autoescape<tags/autoescape>` |
||||||
|
tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% autoescape true %} |
||||||
|
Everything will be automatically escaped in this block |
||||||
|
{% endautoescape %} |
||||||
|
|
||||||
|
Escaping |
||||||
|
-------- |
||||||
|
|
||||||
|
It is sometimes desirable or even necessary to have Twig ignore parts it would |
||||||
|
otherwise handle as variables or blocks. For example if the default syntax is |
||||||
|
used and you want to use ``{{`` as raw string in the template and not start a |
||||||
|
variable you have to use a trick. |
||||||
|
|
||||||
|
The easiest way is to output the variable delimiter (``{{``) by using a variable |
||||||
|
expression: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ '{{' }} |
||||||
|
|
||||||
|
For bigger sections it makes sense to mark a block :doc:`raw<tags/raw>`. |
||||||
|
|
||||||
|
Macros |
||||||
|
------ |
||||||
|
|
||||||
|
Macros are comparable with functions in regular programming languages. They |
||||||
|
are useful to put often used HTML idioms into reusable elements to not repeat |
||||||
|
yourself. |
||||||
|
|
||||||
|
A macro is defined via the :doc:`macro<tags/macro>` tag. Here is a small |
||||||
|
example of a macro that renders a form element: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% macro input(name, value, type, size) %} |
||||||
|
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" /> |
||||||
|
{% endmacro %} |
||||||
|
|
||||||
|
Macros can be defined in any template, and need to be "imported" before being |
||||||
|
used via the :doc:`import<tags/import>` tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% import "forms.html" as forms %} |
||||||
|
|
||||||
|
<p>{{ forms.input('username') }}</p> |
||||||
|
|
||||||
|
Alternatively you can import names from the template into the current |
||||||
|
namespace via the :doc:`from<tags/from>` tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% from 'forms.html' import input as input_field, textarea %} |
||||||
|
|
||||||
|
<dl> |
||||||
|
<dt>Username</dt> |
||||||
|
<dd>{{ input_field('username') }}</dd> |
||||||
|
<dt>Password</dt> |
||||||
|
<dd>{{ input_field('password', type='password') }}</dd> |
||||||
|
</dl> |
||||||
|
<p>{{ textarea('comment') }}</p> |
||||||
|
|
||||||
|
Expressions |
||||||
|
----------- |
||||||
|
|
||||||
|
Twig allows expressions everywhere. These work very similar to regular PHP and |
||||||
|
even if you're not working with PHP you should feel comfortable with it. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
The operator precedence is as follows, with the lowest-precedence |
||||||
|
operators listed first: ``&``, ``^``, ``|``, ``or``, ``and``, ``==``, |
||||||
|
``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, ``..``, ``+``, ``-``, ``~``, |
||||||
|
``*``, ``/``, ``//``, ``%``, ``is``, and ``**``. |
||||||
|
|
||||||
|
Literals |
||||||
|
~~~~~~~~ |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
Support for hash keys as names and expressions was added in Twig 1.5. |
||||||
|
|
||||||
|
The simplest form of expressions are literals. Literals are representations |
||||||
|
for PHP types such as strings, numbers, and arrays. The following literals |
||||||
|
exist: |
||||||
|
|
||||||
|
* ``"Hello World"``: Everything between two double or single quotes is a |
||||||
|
string. They are useful whenever you need a string in the template (for |
||||||
|
example as arguments to function calls, filters or just to extend or |
||||||
|
include a template). |
||||||
|
|
||||||
|
* ``42`` / ``42.23``: Integers and floating point numbers are created by just |
||||||
|
writing the number down. If a dot is present the number is a float, |
||||||
|
otherwise an integer. |
||||||
|
|
||||||
|
* ``["foo", "bar"]``: Arrays are defined by a sequence of expressions |
||||||
|
separated by a comma (``,``) and wrapped with squared brackets (``[]``). |
||||||
|
|
||||||
|
* ``{"foo": "bar"}``: Hashes are defined by a list of keys and values |
||||||
|
separated by a comma (``,``) and wrapped with curly braces (``{}``): |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# keys as string #} |
||||||
|
{ 'foo': 'foo', 'bar': 'bar' } |
||||||
|
|
||||||
|
{# keys as names (equivalent to the previous hash) -- as of Twig 1.5 #} |
||||||
|
{ foo: 'foo', bar: 'bar' } |
||||||
|
|
||||||
|
{# keys as integer #} |
||||||
|
{ 2: 'foo', 4: 'bar' } |
||||||
|
|
||||||
|
{# keys as expressions (the expression must be enclosed into parentheses) -- as of Twig 1.5 #} |
||||||
|
{ (1 + 1): 'foo', (a ~ 'b'): 'bar' } |
||||||
|
|
||||||
|
* ``true`` / ``false``: ``true`` represents the true value, ``false`` |
||||||
|
represents the false value. |
||||||
|
|
||||||
|
* ``null``: ``null`` represents no specific value. This is the value returned |
||||||
|
when a variable does not exist. ``none`` is an alias for ``null``. |
||||||
|
|
||||||
|
Arrays and hashes can be nested: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set foo = [1, {"foo": "bar"}] %} |
||||||
|
|
||||||
|
Math |
||||||
|
~~~~ |
||||||
|
|
||||||
|
Twig allows you to calculate with values. This is rarely useful in templates |
||||||
|
but exists for completeness' sake. The following operators are supported: |
||||||
|
|
||||||
|
* ``+``: Adds two objects together (the operands are casted to numbers). ``{{ |
||||||
|
1 + 1 }}`` is ``2``. |
||||||
|
|
||||||
|
* ``-``: Substracts the second number from the first one. ``{{ 3 - 2 }}`` is |
||||||
|
``1``. |
||||||
|
|
||||||
|
* ``/``: Divides two numbers. The return value will be a floating point |
||||||
|
number. ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``. |
||||||
|
|
||||||
|
* ``%``: Calculates the remainder of an integer division. ``{{ 11 % 7 }}`` is |
||||||
|
``4``. |
||||||
|
|
||||||
|
* ``//``: Divides two numbers and returns the truncated integer result. ``{{ |
||||||
|
20 // 7 }}`` is ``2``. |
||||||
|
|
||||||
|
* ``*``: Multiplies the left operand with the right one. ``{{ 2 * 2 }}`` would |
||||||
|
return ``4``. |
||||||
|
|
||||||
|
* ``**``: Raises the left operand to the power of the right operand. ``{{ 2 ** |
||||||
|
3 }}`` would return ``8``. |
||||||
|
|
||||||
|
Logic |
||||||
|
~~~~~ |
||||||
|
|
||||||
|
You can combine multiple expressions with the following operators: |
||||||
|
|
||||||
|
* ``and``: Returns true if the left and the right operands are both true. |
||||||
|
|
||||||
|
* ``or``: Returns true if the left or the right operand is true. |
||||||
|
|
||||||
|
* ``not``: Negates a statement. |
||||||
|
|
||||||
|
* ``(expr)``: Groups an expression. |
||||||
|
|
||||||
|
Comparisons |
||||||
|
~~~~~~~~~~~ |
||||||
|
|
||||||
|
The following comparison operators are supported in any expression: ``==``, |
||||||
|
``!=``, ``<``, ``>``, ``>=``, and ``<=``. |
||||||
|
|
||||||
|
Containment Operator |
||||||
|
~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
The ``in`` operator performs containment test. |
||||||
|
|
||||||
|
It returns ``true`` if the left operand is contained in the right: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# returns true #} |
||||||
|
|
||||||
|
{{ 1 in [1, 2, 3] }} |
||||||
|
|
||||||
|
{{ 'cd' in 'abcde' }} |
||||||
|
|
||||||
|
.. tip:: |
||||||
|
|
||||||
|
You can use this filter to perform a containment test on strings, arrays, |
||||||
|
or objects implementing the ``Traversable`` interface. |
||||||
|
|
||||||
|
To perform a negative test, use the ``not in`` operator: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if 1 not in [1, 2, 3] %} |
||||||
|
|
||||||
|
{# is equivalent to #} |
||||||
|
{% if not (1 in [1, 2, 3]) %} |
||||||
|
|
||||||
|
Test Operator |
||||||
|
~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
The ``is`` operator performs tests. Tests can be used to test a variable against |
||||||
|
a common expression. The right operand is name of the test: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# find out if a variable is odd #} |
||||||
|
|
||||||
|
{{ name is odd }} |
||||||
|
|
||||||
|
Tests can accept arguments too: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if loop.index is divisibleby(3) %} |
||||||
|
|
||||||
|
Tests can be negated by using the ``is not`` operator: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if loop.index is not divisibleby(3) %} |
||||||
|
|
||||||
|
{# is equivalent to #} |
||||||
|
{% if not (loop.index is divisibleby(3)) %} |
||||||
|
|
||||||
|
Go to the :doc:`tests<tests/index>` page to learn more about the built-in |
||||||
|
tests. |
||||||
|
|
||||||
|
Other Operators |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
The following operators are very useful but don't fit into any of the other |
||||||
|
categories: |
||||||
|
|
||||||
|
* ``..``: Creates a sequence based on the operand before and after the |
||||||
|
operator (this is just syntactic sugar for the :doc:`range<functions/range>` |
||||||
|
function). |
||||||
|
|
||||||
|
* ``|``: Applies a filter. |
||||||
|
|
||||||
|
* ``~``: Converts all operands into strings and concatenates them. ``{{ "Hello |
||||||
|
" ~ name ~ "!" }}`` would return (assuming ``name`` is ``'John'``) ``Hello |
||||||
|
John!``. |
||||||
|
|
||||||
|
* ``.``, ``[]``: Gets an attribute of an object. |
||||||
|
|
||||||
|
* ``?:``: The PHP ternary operator: ``{{ foo ? 'yes' : 'no' }}`` |
||||||
|
|
||||||
|
String Interpolation |
||||||
|
~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
.. versionadded:: 1.5 |
||||||
|
String interpolation was added in Twig 1.5. |
||||||
|
|
||||||
|
String interpolation (`#{expression}`) allows any valid expression to appear |
||||||
|
within a string. The result of evaluating that expression is inserted into the |
||||||
|
string: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ "foo #{bar} baz" }} |
||||||
|
{{ "foo #{1 + 2} baz" }} |
||||||
|
|
||||||
|
Whitespace Control |
||||||
|
------------------ |
||||||
|
|
||||||
|
.. versionadded:: 1.1 |
||||||
|
Tag level whitespace control was added in Twig 1.1. |
||||||
|
|
||||||
|
The first newline after a template tag is removed automatically (like in PHP.) |
||||||
|
Whitespace is not further modified by the template engine, so each whitespace |
||||||
|
(spaces, tabs, newlines etc.) is returned unchanged. |
||||||
|
|
||||||
|
Use the ``spaceless`` tag to remove whitespace *between HTML tags*: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% spaceless %} |
||||||
|
<div> |
||||||
|
<strong>foo</strong> |
||||||
|
</div> |
||||||
|
{% endspaceless %} |
||||||
|
|
||||||
|
{# output will be <div><strong>foo</strong></div> #} |
||||||
|
|
||||||
|
In addition to the spaceless tag you can also control whitespace on a per tag |
||||||
|
level. By using the whitespace control modifier on your tags, you can trim |
||||||
|
leading and or trailing whitespace: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set value = 'no spaces' %} |
||||||
|
{#- No leading/trailing whitespace -#} |
||||||
|
{%- if true -%} |
||||||
|
{{- value -}} |
||||||
|
{%- endif -%} |
||||||
|
|
||||||
|
{# output 'no spaces' #} |
||||||
|
|
||||||
|
The above sample shows the default whitespace control modifier, and how you can |
||||||
|
use it to remove whitespace around tags. Trimming space will consume all whitespace |
||||||
|
for that side of the tag. It is possible to use whitespace trimming on one side |
||||||
|
of a tag: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% set value = 'no spaces' %} |
||||||
|
<li> {{- value }} </li> |
||||||
|
|
||||||
|
{# outputs '<li>no spaces </li>' #} |
||||||
|
|
||||||
|
Extensions |
||||||
|
---------- |
||||||
|
|
||||||
|
Twig can be easily extended. |
||||||
|
|
||||||
|
If you are looking for new tags, filters, or functions, have a look at the Twig official |
||||||
|
`extension repository`_. |
||||||
|
|
||||||
|
If you want to create your own, read :doc:`extensions`. |
||||||
|
|
||||||
|
.. _`Twig bundle`: https://github.com/Anomareh/PHP-Twig.tmbundle |
||||||
|
.. _`Jinja syntax plugin`: http://jinja.pocoo.org/2/documentation/integration |
||||||
|
.. _`Twig syntax plugin`: http://plugins.netbeans.org/plugin/37069/php-twig |
||||||
|
.. _`Twig plugin`: https://github.com/pulse00/Twig-Eclipse-Plugin |
||||||
|
.. _`Twig language definition`: https://github.com/gabrielcorpse/gedit-twig-template-language |
||||||
|
.. _`extension repository`: http://github.com/fabpot/Twig-extensions |
||||||
|
.. _`Twig syntax mode`: https://github.com/bobthecow/Twig-HTML.mode |
@ -0,0 +1,11 @@ |
|||||||
|
``constant`` |
||||||
|
============ |
||||||
|
|
||||||
|
``constant`` checks if a variable has the exact same value as a constant. You |
||||||
|
can use either global constants or class constants: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if post.status is constant('Post::PUBLISHED') %} |
||||||
|
the status attribute is exactly the same as Post::PUBLISHED |
||||||
|
{% endif %} |
@ -0,0 +1,30 @@ |
|||||||
|
``defined`` |
||||||
|
=========== |
||||||
|
|
||||||
|
``defined`` checks if a variable is defined in the current context. This is very |
||||||
|
useful if you use the ``strict_variables`` option: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# defined works with variable names #} |
||||||
|
{% if foo is defined %} |
||||||
|
... |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
{# and attributes on variables names #} |
||||||
|
{% if foo.bar is defined %} |
||||||
|
... |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
{% if foo['bar'] is defined %} |
||||||
|
... |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
When using the ``defined`` test on an expression that uses variables in some |
||||||
|
method calls, be sure that they are all defined first: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if var is defined and foo.method(var) is defined %} |
||||||
|
... |
||||||
|
{% endif %} |
@ -0,0 +1,10 @@ |
|||||||
|
``divisibleby`` |
||||||
|
=============== |
||||||
|
|
||||||
|
``divisibleby`` checks if a variable is divisible by a number: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if loop.index is divisibleby(3) %} |
||||||
|
... |
||||||
|
{% endif %} |
@ -0,0 +1,11 @@ |
|||||||
|
``empty`` |
||||||
|
========= |
||||||
|
|
||||||
|
``empty`` checks if a variable is empty: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{# evaluates to true if the foo variable is null, false, or the empty string #} |
||||||
|
{% if foo is empty %} |
||||||
|
... |
||||||
|
{% endif %} |
@ -0,0 +1,10 @@ |
|||||||
|
``even`` |
||||||
|
======== |
||||||
|
|
||||||
|
``even`` returns ``true`` if the given number is even: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ var is even }} |
||||||
|
|
||||||
|
.. seealso:: :doc:`odd<../tests/odd>` |
@ -0,0 +1,14 @@ |
|||||||
|
Tests |
||||||
|
===== |
||||||
|
|
||||||
|
.. toctree:: |
||||||
|
:maxdepth: 1 |
||||||
|
|
||||||
|
divisibleby |
||||||
|
null |
||||||
|
even |
||||||
|
odd |
||||||
|
sameas |
||||||
|
constant |
||||||
|
defined |
||||||
|
empty |
@ -0,0 +1,12 @@ |
|||||||
|
``null`` |
||||||
|
======== |
||||||
|
|
||||||
|
``null`` returns ``true`` if the variable is ``null``: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ var is null }} |
||||||
|
|
||||||
|
.. note:: |
||||||
|
|
||||||
|
``none`` is an alias for ``null``. |
@ -0,0 +1,10 @@ |
|||||||
|
``odd`` |
||||||
|
======= |
||||||
|
|
||||||
|
``odd`` returns ``true`` if the given number is odd: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{{ var is odd }} |
||||||
|
|
||||||
|
.. seealso:: :doc:`even<../tests/even>` |
@ -0,0 +1,11 @@ |
|||||||
|
``sameas`` |
||||||
|
========== |
||||||
|
|
||||||
|
``sameas`` checks if a variable points to the same memory address than another |
||||||
|
variable: |
||||||
|
|
||||||
|
.. code-block:: jinja |
||||||
|
|
||||||
|
{% if foo.attribute is sameas(false) %} |
||||||
|
the foo attribute really is the ``false`` PHP value |
||||||
|
{% endif %} |
@ -0,0 +1,30 @@ |
|||||||
|
*.sw* |
||||||
|
.deps |
||||||
|
Makefile |
||||||
|
Makefile.fragments |
||||||
|
Makefile.global |
||||||
|
Makefile.objects |
||||||
|
acinclude.m4 |
||||||
|
aclocal.m4 |
||||||
|
build/ |
||||||
|
config.cache |
||||||
|
config.guess |
||||||
|
config.h |
||||||
|
config.h.in |
||||||
|
config.log |
||||||
|
config.nice |
||||||
|
config.status |
||||||
|
config.sub |
||||||
|
configure |
||||||
|
configure.in |
||||||
|
install-sh |
||||||
|
libtool |
||||||
|
ltmain.sh |
||||||
|
missing |
||||||
|
mkinstalldirs |
||||||
|
run-tests.php |
||||||
|
twig.loT |
||||||
|
.libs/ |
||||||
|
modules/ |
||||||
|
twig.la |
||||||
|
twig.lo |
@ -0,0 +1,22 @@ |
|||||||
|
Copyright (c) 2011, Derick Rethans <derick@derickrethans.nl> |
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, |
||||||
|
this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer in the |
||||||
|
documentation and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,8 @@ |
|||||||
|
dnl config.m4 for extension twig |
||||||
|
|
||||||
|
PHP_ARG_ENABLE(twig, whether to enable twig support, |
||||||
|
[ --enable-twig Enable twig support]) |
||||||
|
|
||||||
|
if test "$PHP_TWIG" != "no"; then |
||||||
|
PHP_NEW_EXTENSION(twig, twig.c, $ext_shared) |
||||||
|
fi |
@ -0,0 +1,8 @@ |
|||||||
|
// vim:ft=javascript |
||||||
|
|
||||||
|
ARG_ENABLE("twig", "Twig support", "no"); |
||||||
|
|
||||||
|
if (PHP_TWIG != "no") { |
||||||
|
AC_DEFINE('HAVE_TWIG', 1); |
||||||
|
EXTENSION('twig', 'twig.c'); |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
/*
|
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
| Twig Extension | |
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
| Copyright (c) 2011 Derick Rethans | |
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
| Redistribution and use in source and binary forms, with or without | |
||||||
|
| modification, are permitted provided that the conditions mentioned | |
||||||
|
| in the accompanying LICENSE file are met (BSD, revised). | |
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
| Author: Derick Rethans <derick@derickrethans.nl> | |
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef PHP_TWIG_H |
||||||
|
#define PHP_TWIG_H |
||||||
|
|
||||||
|
#define PHP_TWIG_VERSION "1.6.4" |
||||||
|
|
||||||
|
#include "php.h" |
||||||
|
|
||||||
|
extern zend_module_entry twig_module_entry; |
||||||
|
#define phpext_twig_ptr &twig_module_entry |
||||||
|
|
||||||
|
#ifdef PHP_WIN32 |
||||||
|
#define PHP_TWIG_API __declspec(dllexport) |
||||||
|
#else |
||||||
|
#define PHP_TWIG_API |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef ZTS |
||||||
|
#include "TSRM.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
PHP_FUNCTION(twig_template_get_attributes); |
||||||
|
|
||||||
|
PHP_MINIT_FUNCTION(twig); |
||||||
|
PHP_MSHUTDOWN_FUNCTION(twig); |
||||||
|
PHP_RINIT_FUNCTION(twig); |
||||||
|
PHP_RSHUTDOWN_FUNCTION(twig); |
||||||
|
PHP_MINFO_FUNCTION(twig); |
||||||
|
|
||||||
|
#ifdef ZTS |
||||||
|
#define TWIG_G(v) TSRMG(twig_globals_id, zend_twig_globals *, v) |
||||||
|
#else |
||||||
|
#define TWIG_G(v) (twig_globals.v) |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,46 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* |
||||||
|
* This file is part of Twig. |
||||||
|
* |
||||||
|
* (c) 2009 Fabien Potencier |
||||||
|
* |
||||||
|
* For the full copyright and license information, please view the LICENSE |
||||||
|
* file that was distributed with this source code. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Autoloads Twig classes. |
||||||
|
* |
||||||
|
* @package twig |
||||||
|
* @author Fabien Potencier <fabien@symfony.com> |
||||||
|
*/ |
||||||
|
class Twig_Autoloader |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Registers Twig_Autoloader as an SPL autoloader. |
||||||
|
*/ |
||||||
|
static public function register() |
||||||
|
{ |
||||||
|
ini_set('unserialize_callback_func', 'spl_autoload_call'); |
||||||
|
spl_autoload_register(array(new self, 'autoload')); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Handles autoloading of classes. |
||||||
|
* |
||||||
|
* @param string $class A class name. |
||||||
|
* |
||||||
|
* @return boolean Returns true if the class has been loaded |
||||||
|
*/ |
||||||
|
static public function autoload($class) |
||||||
|
{ |
||||||
|
if (0 !== strpos($class, 'Twig')) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (is_file($file = dirname(__FILE__).'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php')) { |
||||||
|
require $file; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,242 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* |
||||||
|
* This file is part of Twig. |
||||||
|
* |
||||||
|
* (c) 2009 Fabien Potencier |
||||||
|
* (c) 2009 Armin Ronacher |
||||||
|
* |
||||||
|
* For the full copyright and license information, please view the LICENSE |
||||||
|
* file that was distributed with this source code. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Compiles a node to PHP code. |
||||||
|
* |
||||||
|
* @package twig |
||||||
|
* @author Fabien Potencier <fabien@symfony.com> |
||||||
|
*/ |
||||||
|
class Twig_Compiler implements Twig_CompilerInterface |
||||||
|
{ |
||||||
|
protected $lastLine; |
||||||
|
protected $source; |
||||||
|
protected $indentation; |
||||||
|
protected $env; |
||||||
|
protected $debugInfo; |
||||||
|
protected $sourceOffset; |
||||||
|
protected $sourceLine; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param Twig_Environment $env The twig environment instance |
||||||
|
*/ |
||||||
|
public function __construct(Twig_Environment $env) |
||||||
|
{ |
||||||
|
$this->env = $env; |
||||||
|
$this->debugInfo = array(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the environment instance related to this compiler. |
||||||
|
* |
||||||
|
* @return Twig_Environment The environment instance |
||||||
|
*/ |
||||||
|
public function getEnvironment() |
||||||
|
{ |
||||||
|
return $this->env; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the current PHP code after compilation. |
||||||
|
* |
||||||
|
* @return string The PHP code |
||||||
|
*/ |
||||||
|
public function getSource() |
||||||
|
{ |
||||||
|
return $this->source; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Compiles a node. |
||||||
|
* |
||||||
|
* @param Twig_NodeInterface $node The node to compile |
||||||
|
* @param integer $indentation The current indentation |
||||||
|
* |
||||||
|
* @return Twig_Compiler The current compiler instance |
||||||
|
*/ |
||||||
|
public function compile(Twig_NodeInterface $node, $indentation = 0) |
||||||
|
{ |
||||||
|
$this->lastLine = null; |
||||||
|
$this->source = ''; |
||||||
|
$this->sourceOffset = 0; |
||||||
|
$this->sourceLine = 0; |
||||||
|
$this->indentation = $indentation; |
||||||
|
|
||||||
|
$node->compile($this); |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function subcompile(Twig_NodeInterface $node, $raw = true) |
||||||
|
{ |
||||||
|
if (false === $raw) { |
||||||
|
$this->addIndentation(); |
||||||
|
} |
||||||
|
|
||||||
|
$node->compile($this); |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a raw string to the compiled code. |
||||||
|
* |
||||||
|
* @param string $string The string |
||||||
|
* |
||||||
|
* @return Twig_Compiler The current compiler instance |
||||||
|
*/ |
||||||
|
public function raw($string) |
||||||
|
{ |
||||||
|
$this->source .= $string; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes a string to the compiled code by adding indentation. |
||||||
|
* |
||||||
|
* @return Twig_Compiler The current compiler instance |
||||||
|
*/ |
||||||
|
public function write() |
||||||
|
{ |
||||||
|
$strings = func_get_args(); |
||||||
|
foreach ($strings as $string) { |
||||||
|
$this->addIndentation(); |
||||||
|
$this->source .= $string; |
||||||
|
} |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function addIndentation() |
||||||
|
{ |
||||||
|
$this->source .= str_repeat(' ', $this->indentation * 4); |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a quoted string to the compiled code. |
||||||
|
* |
||||||
|
* @param string $value The string |
||||||
|
* |
||||||
|
* @return Twig_Compiler The current compiler instance |
||||||
|
*/ |
||||||
|
public function string($value) |
||||||
|
{ |
||||||
|
$this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\")); |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a PHP representation of a given value. |
||||||
|
* |
||||||
|
* @param mixed $value The value to convert |
||||||
|
* |
||||||
|
* @return Twig_Compiler The current compiler instance |
||||||
|
*/ |
||||||
|
public function repr($value) |
||||||
|
{ |
||||||
|
if (is_int($value) || is_float($value)) { |
||||||
|
if (false !== $locale = setlocale(LC_NUMERIC, 0)) { |
||||||
|
setlocale(LC_NUMERIC, 'C'); |
||||||
|
} |
||||||
|
|
||||||
|
$this->raw($value); |
||||||
|
|
||||||
|
if (false !== $locale) { |
||||||
|
setlocale(LC_NUMERIC, $locale); |
||||||
|
} |
||||||
|
} elseif (null === $value) { |
||||||
|
$this->raw('null'); |
||||||
|
} elseif (is_bool($value)) { |
||||||
|
$this->raw($value ? 'true' : 'false'); |
||||||
|
} elseif (is_array($value)) { |
||||||
|
$this->raw('array('); |
||||||
|
$i = 0; |
||||||
|
foreach ($value as $key => $value) { |
||||||
|
if ($i++) { |
||||||
|
$this->raw(', '); |
||||||
|
} |
||||||
|
$this->repr($key); |
||||||
|
$this->raw(' => '); |
||||||
|
$this->repr($value); |
||||||
|
} |
||||||
|
$this->raw(')'); |
||||||
|
} else { |
||||||
|
$this->string($value); |
||||||
|
} |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds debugging information. |
||||||
|
* |
||||||
|
* @param Twig_NodeInterface $node The related twig node |
||||||
|
* |
||||||
|
* @return Twig_Compiler The current compiler instance |
||||||
|
*/ |
||||||
|
public function addDebugInfo(Twig_NodeInterface $node) |
||||||
|
{ |
||||||
|
if ($node->getLine() != $this->lastLine) { |
||||||
|
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset); |
||||||
|
$this->sourceOffset = strlen($this->source); |
||||||
|
$this->debugInfo[$this->sourceLine] = $node->getLine(); |
||||||
|
|
||||||
|
$this->lastLine = $node->getLine(); |
||||||
|
$this->write("// line {$node->getLine()}\n"); |
||||||
|
} |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function getDebugInfo() |
||||||
|
{ |
||||||
|
return $this->debugInfo; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indents the generated code. |
||||||
|
* |
||||||
|
* @param integer $step The number of indentation to add |
||||||
|
* |
||||||
|
* @return Twig_Compiler The current compiler instance |
||||||
|
*/ |
||||||
|
public function indent($step = 1) |
||||||
|
{ |
||||||
|
$this->indentation += $step; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Outdents the generated code. |
||||||
|
* |
||||||
|
* @param integer $step The number of indentation to remove |
||||||
|
* |
||||||
|
* @return Twig_Compiler The current compiler instance |
||||||
|
*/ |
||||||
|
public function outdent($step = 1) |
||||||
|
{ |
||||||
|
$this->indentation -= $step; |
||||||
|
|
||||||
|
if ($this->indentation < 0) { |
||||||
|
throw new Twig_Error('Unable to call outdent() as the indentation would become negative'); |
||||||
|
} |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* |
||||||
|
* This file is part of Twig. |
||||||
|
* |
||||||
|
* (c) 2009 Fabien Potencier |
||||||
|
* |
||||||
|
* For the full copyright and license information, please view the LICENSE |
||||||
|
* file that was distributed with this source code. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Interface implemented by compiler classes. |
||||||
|
* |
||||||
|
* @package twig |
||||||
|
* @author Fabien Potencier <fabien@symfony.com> |
||||||
|
*/ |
||||||
|
interface Twig_CompilerInterface |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Compiles a node. |
||||||
|
* |
||||||
|
* @param Twig_NodeInterface $node The node to compile |
||||||
|
* |
||||||
|
* @return Twig_CompilerInterface The current compiler instance |
||||||
|
*/ |
||||||
|
function compile(Twig_NodeInterface $node); |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the current PHP code after compilation. |
||||||
|
* |
||||||
|
* @return string The PHP code |
||||||
|
*/ |
||||||
|
function getSource(); |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,176 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* |
||||||
|
* This file is part of Twig. |
||||||
|
* |
||||||
|
* (c) 2009 Fabien Potencier |
||||||
|
* |
||||||
|
* For the full copyright and license information, please view the LICENSE |
||||||
|
* file that was distributed with this source code. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Twig base exception. |
||||||
|
* |
||||||
|
* @package twig |
||||||
|
* @author Fabien Potencier <fabien@symfony.com> |
||||||
|
*/ |
||||||
|
class Twig_Error extends Exception |
||||||
|
{ |
||||||
|
protected $lineno; |
||||||
|
protected $filename; |
||||||
|
protected $rawMessage; |
||||||
|
protected $previous; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param string $message The error message |
||||||
|
* @param integer $lineno The template line where the error occurred |
||||||
|
* @param string $filename The template file name where the error occurred |
||||||
|
* @param Exception $previous The previous exception |
||||||
|
*/ |
||||||
|
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null) |
||||||
|
{ |
||||||
|
if (-1 === $lineno || null === $filename) { |
||||||
|
if ($trace = $this->getTemplateTrace()) { |
||||||
|
if (-1 === $lineno) { |
||||||
|
$lineno = $this->guessTemplateLine($trace); |
||||||
|
} |
||||||
|
|
||||||
|
if (null === $filename) { |
||||||
|
$filename = $trace['object']->getTemplateName(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$this->lineno = $lineno; |
||||||
|
$this->filename = $filename; |
||||||
|
$this->rawMessage = $message; |
||||||
|
|
||||||
|
$this->updateRepr(); |
||||||
|
|
||||||
|
if (version_compare(PHP_VERSION, '5.3.0', '<')) { |
||||||
|
$this->previous = $previous; |
||||||
|
parent::__construct($this->message); |
||||||
|
} else { |
||||||
|
parent::__construct($this->message, 0, $previous); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the raw message. |
||||||
|
* |
||||||
|
* @return string The raw message |
||||||
|
*/ |
||||||
|
public function getRawMessage() |
||||||
|
{ |
||||||
|
return $this->rawMessage; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the filename where the error occurred. |
||||||
|
* |
||||||
|
* @return string The filename |
||||||
|
*/ |
||||||
|
public function getTemplateFile() |
||||||
|
{ |
||||||
|
return $this->filename; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the filename where the error occurred. |
||||||
|
* |
||||||
|
* @param string $filename The filename |
||||||
|
*/ |
||||||
|
public function setTemplateFile($filename) |
||||||
|
{ |
||||||
|
$this->filename = $filename; |
||||||
|
|
||||||
|
$this->updateRepr(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the template line where the error occurred. |
||||||
|
* |
||||||
|
* @return integer The template line |
||||||
|
*/ |
||||||
|
public function getTemplateLine() |
||||||
|
{ |
||||||
|
return $this->lineno; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the template line where the error occurred. |
||||||
|
* |
||||||
|
* @param integer $lineno The template line |
||||||
|
*/ |
||||||
|
public function setTemplateLine($lineno) |
||||||
|
{ |
||||||
|
$this->lineno = $lineno; |
||||||
|
|
||||||
|
$this->updateRepr(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* For PHP < 5.3.0, provides access to the getPrevious() method. |
||||||
|
* |
||||||
|
* @param string $method The method name |
||||||
|
* @param array $arguments The parameters to be passed to the method |
||||||
|
* |
||||||
|
* @return Exception The previous exception or null |
||||||
|
*/ |
||||||
|
public function __call($method, $arguments) |
||||||
|
{ |
||||||
|
if ('getprevious' == strtolower($method)) { |
||||||
|
return $this->previous; |
||||||
|
} |
||||||
|
|
||||||
|
throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method)); |
||||||
|
} |
||||||
|
|
||||||
|
protected function updateRepr() |
||||||
|
{ |
||||||
|
$this->message = $this->rawMessage; |
||||||
|
|
||||||
|
$dot = false; |
||||||
|
if ('.' === substr($this->message, -1)) { |
||||||
|
$this->message = substr($this->message, 0, -1); |
||||||
|
$dot = true; |
||||||
|
} |
||||||
|
|
||||||
|
if (null !== $this->filename) { |
||||||
|
$this->message .= sprintf(' in %s', is_string($this->filename) ? '"'.$this->filename.'"' : json_encode($this->filename)); |
||||||
|
} |
||||||
|
|
||||||
|
if ($this->lineno >= 0) { |
||||||
|
$this->message .= sprintf(' at line %d', $this->lineno); |
||||||
|
} |
||||||
|
|
||||||
|
if ($dot) { |
||||||
|
$this->message .= '.'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function getTemplateTrace() |
||||||
|
{ |
||||||
|
foreach (debug_backtrace() as $trace) { |
||||||
|
if (isset($trace['object']) && $trace['object'] instanceof Twig_Template) { |
||||||
|
return $trace; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function guessTemplateLine($trace) |
||||||
|
{ |
||||||
|
if (isset($trace['line'])) { |
||||||
|
foreach ($trace['object']->getDebugInfo() as $codeLine => $templateLine) { |
||||||
|
if ($codeLine <= $trace['line']) { |
||||||
|
return $templateLine; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* |
||||||
|
* This file is part of Twig. |
||||||
|
* |
||||||
|
* (c) 2010 Fabien Potencier |
||||||
|
* |
||||||
|
* For the full copyright and license information, please view the LICENSE |
||||||
|
* file that was distributed with this source code. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Exception thrown when an error occurs during template loading. |
||||||
|
* |
||||||
|
* @package twig |
||||||
|
* @author Fabien Potencier <fabien@symfony.com> |
||||||
|
*/ |
||||||
|
class Twig_Error_Loader extends Twig_Error |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* |
||||||
|
* This file is part of Twig. |
||||||
|
* |
||||||
|
* (c) 2009 Fabien Potencier |
||||||
|
* (c) 2009 Armin Ronacher |
||||||
|
* |
||||||
|
* For the full copyright and license information, please view the LICENSE |
||||||
|
* file that was distributed with this source code. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Exception thrown when an error occurs at runtime. |
||||||
|
* |
||||||
|
* @package twig |
||||||
|
* @author Fabien Potencier <fabien@symfony.com> |
||||||
|
*/ |
||||||
|
class Twig_Error_Runtime extends Twig_Error |
||||||
|
{ |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue