protected $visitors;
protected $filters;
protected $tests;
+ protected $functions;
protected $globals;
protected $runtimeInitialized;
protected $loadedTemplates;
return $this->tests;
}
+ public function addFunction($name, Twig_Function $function)
+ {
+ if (null === $this->functions) {
+ $this->loadFunctions();
+ }
+ $this->functions[$name] = $function;
+ }
+
+ /**
+ * Get a function by name
+ *
+ * Subclasses may override getFunction($name) and load functions differently;
+ * so no list of functions is available.
+ *
+ * @param string $name function name
+ * @return Twig_Function|null A Twig_Function instance or null if the function does not exists
+ */
+ public function getFunction($name)
+ {
+ if (null === $this->functions) {
+ $this->loadFunctions();
+ }
+
+ if (isset($this->functions[$name])) {
+ return $this->functions[$name];
+ }
+
+ return null;
+ }
+
+ protected function loadFunctions() {
+ $this->functions = array();
+ foreach ($this->getExtensions() as $extension) {
+ $this->functions = array_merge($this->functions, $extension->getFunctions());
+ }
+ }
+
public function addGlobal($name, $value)
{
if (null === $this->globals) {
{
return array();
}
+
+ /**
+ * Returns a list of functions to add to the existing list.
+ *
+ * @return array An array of functions
+ */
+ public function getFunctions()
+ {
+ return array();
+ }
/**
* Returns a list of operators to add to the existing list.
*
* @return array An array of global functions
*/
- public function getGlobals()
+ public function getFunctions()
{
return array(
- 'fn_range' => new Twig_Function($this, 'getRange'),
- 'fn_constant' => new Twig_Function($this, 'getConstant'),
- 'fn_cycle' => new Twig_Function($this, 'getCycle'),
+ 'range' => new Twig_Function_Method($this, 'getRange'),
+ 'constant' => new Twig_Function_Method($this, 'getConstant'),
+ 'cycle' => new Twig_Function_Method($this, 'getCycle'),
);
}
public function getTests();
/**
+ * Returns a list of functions to add to the existing list.
+ *
+ * @return array An array of functions
+ */
+ public function getFunctions();
+
+ /**
* Returns a list of operators to add to the existing list.
*
* @return array An array of operators
*/
/**
- * Defines a new Twig function.
+ * Represents a template function.
*
* @package twig
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
-class Twig_Function extends Exception
+abstract class Twig_Function implements Twig_FunctionInterface
{
- protected $object;
- protected $method;
+ protected $options;
- public function __construct($object, $method)
+ public function __construct(array $options = array())
{
- $this->object = $object;
- $this->method = $method;
+ $this->options = array_merge(array(
+ 'needs_environment' => false,
+ ), $options);
}
- public function getObject()
+ public function needsEnvironment()
{
- return $this->object;
+ return $this->options['needs_environment'];
}
- public function getMethod()
+ public function getSafe(Twig_Node $functionArgs)
{
- return $this->method;
+ if (isset($this->options['is_safe'])) {
+ return $this->options['is_safe'];
+ }
+
+ if (isset($this->options['is_safe_callback'])) {
+ return call_user_func($this->options['is_safe_callback'], $functionArgs);
+ }
+
+ return array();
}
}
--- /dev/null
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2009 Fabien Potencier
+ * (c) 2010 Arnaud Le Blanc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Represents a function template function.
+ *
+ * @package twig
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
+ */
+class Twig_Function_Function extends Twig_Function
+{
+ 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
+ * (c) 2010 Arnaud Le Blanc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Represents a method template function.
+ *
+ * @package twig
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
+ */
+class Twig_Function_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->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2010 Fabien Potencier
+ * (c) 2010 Arnaud Le Blanc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Represents a template function.
+ *
+ * @package twig
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
+ */
+interface Twig_FunctionInterface
+{
+ public function compile();
+ public function needsEnvironment();
+ public function getSafe(Twig_Node $filterArgs);
+}
public function compile($compiler)
{
- // functions must be prefixed with fn_
- $this->getNode('name')->setAttribute('name', 'fn_'.$this->getNode('name')->getAttribute('name'));
+ $function = $compiler->getEnvironment()->getFunction($this->getNode('name')->getAttribute('name'));
+ if (!$function) {
+ throw new Twig_Error_Syntax(sprintf('The function "%s" does not exist', $this->getNode('name')->getAttribute('name')), $this->getLine());
+ }
- $compiler
- ->raw('$this->callFunction($context, ')
- ->subcompile($this->getNode('name'))
- ->raw(', array(')
- ;
+ $compiler->raw($function->compile().($function->needsEnvironment() ? '($this->env, ' : '('));
+ $first = true;
foreach ($this->getNode('arguments') as $node) {
- $compiler
- ->subcompile($node)
- ->raw(', ')
- ;
+ if (!$first) {
+ $compiler->raw(', ');
+ } else {
+ $first = false;
+ }
+ $compiler->subcompile($node);
}
- $compiler->raw('))');
+ $compiler->raw(')');
}
}
+++ /dev/null
---TEST--
-Twig supports calling variables
---TEMPLATE--
-{{ lower('FOO') }}
-{{ lower1('FOO') }}
---DATA--
-return array(
- 'foo' => new Foo(),
- 'fn_lower' => new Twig_Function('foo', 'strToLower'),
- 'fn_lower1' => new Twig_Function(new Foo(), 'strToLower'))
---EXPECT--
-foo
-foo
+++ /dev/null
---TEST--
-global variables
---TEMPLATE--
-{{ foo() }}
-{{ bar() }}
-{% include "included.twig" %}
-{% from "included.twig" import foobar %}
-{{ foobar() }}
---TEMPLATE(included.twig)--
-{% macro foobar() %}
-{{ foo() }}
-
-{% endmacro %}
-{{ foo() }}
-
---DATA--
-$twig->addGlobal('fn_foo', new Twig_Function(new Foo(), 'getFoo'));
-$twig->addGlobal('fn_bar', new Twig_Function('barObj', 'getFoo'));
-return array('barObj' => new Foo());
---EXPECT--
-foo
-foo
-
-foo
-foo
--- /dev/null
+--TEST--
+global variables
+--TEMPLATE--
+{% include "included.twig" %}
+{% from "included.twig" import foobar %}
+{{ foobar() }}
+--TEMPLATE(included.twig)--
+{% macro foobar() %}
+called foobar
+{% endmacro %}
+--DATA--
+return array();
+--EXPECT--
+called foobar
--DATA--
return array('foo' => 'bar')
--EXPECT--
-fn_range,fn_constant,fn_cycle,foo,_parent,
-fn_range,fn_constant,fn_cycle,_parent,
-fn_range,fn_constant,fn_cycle,foo,foo1,_parent,
-fn_range,fn_constant,fn_cycle,foo1,_parent,
+foo,_parent,
+_parent,
+foo,foo1,_parent,
+foo1,_parent,
);
}
+ public function getFunctions()
+ {
+ return array(
+ 'safe_br' => new Twig_Function_Method($this, 'br', array('is_safe' => array('html'))),
+ 'unsafe_br' => new Twig_Function_Method($this, 'br'),
+ );
+ }
+
/**
* nl2br which also escapes, for testing escaper filters
*/
return strtoupper($value);
}
+ public function br()
+ {
+ return '<br />';
+ }
+
public function getName()
{
return 'test';