* 0.9.5-DEV
+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)),
+
+ * 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 (as a syntactic sugar for the range filter when the step is 1)
inherit from the `Twig_Extension` class, which provides empty implementations
of all the above methods to keep your extension clean.
+The `getName()` method must always be implemented to return a unique
+identifier for the extension.
+
>**TIP**
>The bundled extensions are great examples of how extensions work.
+Registering a custom extension is like registering any other core extension:
+
+ [php]
+ $twig->addExtension(new Project_Twig_Extension());
+
Defining new Filters
--------------------
The most common element you will want to add to Twig is filters. A filter is
-just a regular PHP function that takes the left side of the filter as first
-argument and the arguments passed to the filter as extra arguments.
+just a regular PHP function or method that takes the left side of the filter
+as first argument and the arguments passed to the filter as extra arguments.
+
+### Function Filters
Let's create a filter, named `rot13`, which returns the
[rot13](http://www.php.net/manual/en/function.str-rot13.php) transformation of
{# should displays Gjvt #}
-Here is the simplest extension class you can create to add this filter:
+A filter is defined as a sub-class of the `Twig_Filter` class. The
+`Twig_Filter_Function` class can be used to define a filter implemented as a
+function:
+
+ $filter = new Twig_Filter_Function('str_rot13');
+
+The first argument is the name of the function to call, here `str_rot13`, a
+native PHP function.
+
+Registering the filter in an extension means implementing the `getFilters()`
+method:
[php]
class Project_Twig_Extension extends Twig_Extension
public function getFilters()
{
return array(
- 'rot13' => array('str_rot13', false),
+ 'rot13' => new Twig_Filter_Function('str_rot13'),
);
}
}
}
-Registering the new extension is like registering core extensions:
+The `Twig_Filter` classes take flags 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`:
[php]
- $twig->addExtension(new Project_Twig_Extension());
+ $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true));
-Filters can also be passed the current environment. You might have noticed
-that a filter is defined by a function name and a Boolean. If you change the
-Boolean to `true`, Twig will pass the current environment as the first
-argument to the filter call:
+Twig will then pass the current environment as the first argument to the
+filter call:
[php]
- class Project_Twig_Extension extends Twig_Extension
- {
- public function getFilters()
- {
- return array(
- 'rot13' => array('twig_compute_rot13', true),
- );
- }
-
- // ...
- }
-
function twig_compute_rot13(Twig_Environment $env, $string)
{
// get the current charset for instance
return $prefix.str_rot13($string);
}
+### Class Method Filters
+
+The `Twig_Filter_Function` class can also be used to register static method as
+filters:
+
+ [php]
+ class Project_Twig_Extension extends Twig_Extension
+ {
+ public function getFilters()
+ {
+ return array(
+ 'rot13' => new Twig_Filter_Function('Project_Twig_Extension::rot13Filter'),
+ );
+ }
+
+ static public function rot13Filter($string)
+ {
+ return str_rot13($string);
+ }
+
+ public function getName()
+ {
+ return 'project';
+ }
+ }
+
+### Object Method Filters
+
+You can also register methods as filters by using the `Twig_Filter_Method`
+class:
+
+ [php]
+ 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);
+ }
+
+ public function getName()
+ {
+ return 'project';
+ }
+ }
+
+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
--------------------------
return array_merge(
parent::getFilters(),
array(
- 'date' => array('my_date_format_filter', false)
+ 'date' => Twig_Filter_Method($this, 'dateFilter')
)
);
}
- }
- function my_date_format_filter($timestamp, $format = 'F j, Y H:i')
- {
- return '...'.twig_date_format_filter($timestamp, $format);
+ 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
{
$filters = array(
// formatting filters
- 'date' => array('twig_date_format_filter', false),
- 'format' => array('sprintf', false),
+ 'date' => new Twig_Filter_Function('twig_date_format_filter'),
+ 'format' => new Twig_Filter_Function('sprintf'),
// numbers
- 'even' => array('twig_is_even_filter', false),
- 'odd' => array('twig_is_odd_filter', false),
+ 'even' => new Twig_Filter_Function('twig_is_even_filter'),
+ 'odd' => new Twig_Filter_Function('twig_is_odd_filter'),
// encoding
- 'urlencode' => array('twig_urlencode_filter', false),
+ 'urlencode' => new Twig_Filter_Function('twig_urlencode_filter'),
// string filters
- 'title' => array('twig_title_string_filter', true),
- 'capitalize' => array('twig_capitalize_string_filter', true),
- 'upper' => array('strtoupper', false),
- 'lower' => array('strtolower', false),
- 'striptags' => array('strip_tags', false),
+ 'title' => new Twig_Filter_Function('twig_title_string_filter', array('needs_environment' => true)),
+ 'capitalize' => new Twig_Filter_Function('twig_capitalize_string_filter', array('needs_environment' => true)),
+ 'upper' => new Twig_Filter_Function('strtoupper'),
+ 'lower' => new Twig_Filter_Function('strtolower'),
+ 'striptags' => new Twig_Filter_Function('strip_tags'),
// array helpers
- 'join' => array('twig_join_filter', false),
- 'reverse' => array('twig_reverse_filter', false),
- 'length' => array('twig_length_filter', false),
- 'sort' => array('twig_sort_filter', false),
- 'in' => array('twig_in_filter', false),
- 'range' => array('twig_range_filter', false),
+ 'join' => new Twig_Filter_Function('twig_join_filter'),
+ 'reverse' => new Twig_Filter_Function('twig_reverse_filter'),
+ 'length' => new Twig_Filter_Function('twig_length_filter'),
+ 'sort' => new Twig_Filter_Function('twig_sort_filter'),
+ 'in' => new Twig_Filter_Function('twig_in_filter'),
+ 'range' => new Twig_Filter_Function('twig_range_filter'),
// iteration and runtime
- 'default' => array('twig_default_filter', false),
- 'keys' => array('twig_get_array_keys_filter', false),
- 'items' => array('twig_get_array_items_filter', false),
+ 'default' => new Twig_Filter_Function('twig_default_filter'),
+ 'keys' => new Twig_Filter_Function('twig_get_array_keys_filter'),
+ 'items' => new Twig_Filter_Function('twig_get_array_items_filter'),
// escaping
- 'escape' => array('twig_escape_filter', true),
- 'e' => array('twig_escape_filter', true),
+ 'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
+ 'e' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
);
if (function_exists('mb_get_info'))
{
- $filters['upper'] = array('twig_upper_filter', true);
- $filters['lower'] = array('twig_lower_filter', true);
+ $filters['upper'] = new Twig_Filter_Function('twig_upper_filter', array('needs_environment' => true));
+ $filters['lower'] = new Twig_Filter_Function('twig_lower_filter', array('needs_environment' => true));
}
return $filters;
public function getFilters()
{
return array(
- 'safe' => array('twig_safe_filter', false),
+ 'safe' => new Twig_Filter_Function('twig_safe_filter'),
);
}
--- /dev/null
+<?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.
+ */
+
+/**
+ * Represents a template filter.
+ *
+ * @package twig
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id$
+ */
+abstract class Twig_Filter
+{
+ protected $options;
+
+ public function __construct(array $options = array())
+ {
+ $this->options = array_merge(array(
+ 'needs_environment' => false,
+ ), $options);
+ }
+
+ abstract public function compile();
+
+ public function needsEnvironment()
+ {
+ return $this->options['needs_environment'];
+ }
+}
--- /dev/null
+<?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.
+ */
+
+/**
+ * Represents a function template filter.
+ *
+ * @package twig
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id$
+ */
+class Twig_Filter_Function extends Twig_Filter
+{
+ protected $function;
+
+ public function __construct($function, array $options = array())
+ {
+ parent::__construct($options);
+
+ $this->function = $function;
+ }
+
+ public function compile()
+ {
+ return $this->function;
+ }
+}
--- /dev/null
+<?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.
+ */
+
+/**
+ * Represents a method template filter.
+ *
+ * @package twig
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id$
+ */
+class Twig_Filter_Method extends Twig_Filter
+{
+ protected $extension, $method;
+
+ public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
+ {
+ parent::__construct($options);
+
+ $this->extension = $extension;
+ $this->method = $method;
+ }
+
+ public function compile()
+ {
+ return sprintf('$this->getEnvironment()->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
+ }
+}
}
else
{
- $compiler->raw($filterMap[$name][0].($filterMap[$name][1] ? '($this->getEnvironment(), ' : '('));
+ $compiler->raw($filterMap[$name]->compile().($filterMap[$name]->needsEnvironment() ? '($this->getEnvironment(), ' : '('));
}
$postponed[] = $attrs;
}