Changes:
+ * 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
$node = $this->parseSubscriptExpression($node);
} elseif ('|' == $token->getValue()) {
$node = $this->parseFilterExpression($node);
+ } elseif ($node instanceof Twig_Node_Expression_Name && '(' == $token->getValue()) {
+ return new Twig_Node_Expression_Function($node, $this->parseArguments(), $node->getLine());
} else {
break;
}
--- /dev/null
+<?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.
+ */
+
+/**
+ * Defines a new Twig function.
+ *
+ * @package twig
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class Twig_Function extends Exception
+{
+ protected $object;
+ protected $method;
+
+ public function __construct($object, $method)
+ {
+ $this->object = $object;
+ $this->method = $method;
+ }
+
+ public function getObject()
+ {
+ return $this->object;
+ }
+
+ public function getMethod()
+ {
+ return $this->method;
+ }
+}
--- /dev/null
+<?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.
+ */
+class Twig_Node_Expression_Function extends Twig_Node_Expression
+{
+ public function __construct(Twig_Node_Expression_Name $name, Twig_NodeInterface $arguments, $lineno)
+ {
+ parent::__construct(array('name' => $name, 'arguments' => $arguments), array(), $lineno);
+ }
+
+ public function compile($compiler)
+ {
+ $compiler
+ ->raw('$this->callFunction($context, ')
+ ->subcompile($this->getNode('name'))
+ ->raw(', array(')
+ ;
+
+ foreach ($this->getNode('arguments') as $node) {
+ $compiler
+ ->subcompile($node)
+ ->raw(', ')
+ ;
+ }
+
+ $compiler->raw('))');
+ }
+}
return $context[$item];
}
+ protected function callFunction($context, $function, array $arguments = array())
+ {
+ if (!$function instanceof Twig_Function) {
+ throw new Twig_Error_Runtime('Called a non-function', -1, $this->getTemplateName());
+ }
+
+ $object = $function->getObject();
+ if (!is_object($object)) {
+ $object = $this->getContext($context, $object);
+ }
+
+ return $this->getAttribute($object, $function->getMethod(), $arguments, Twig_Node_Expression_GetAttr::TYPE_METHOD, false);
+ }
+
protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_Node_Expression_GetAttr::TYPE_ANY, $noStrictCheck = false)
{
// array
--- /dev/null
+--TEST--
+Twig supports calling variables
+--TEMPLATE--
+{{ lower('FOO') }}
+{{ lower1('FOO') }}
+--DATA--
+return array(
+ 'foo' => new Foo(),
+ 'lower' => new Twig_Function('foo', 'strToLower'),
+ 'lower1' => new Twig_Function(new Foo(), 'strToLower'))
+--EXPECT--
+foo
+foo
}
}
+function test_foo($value = 'foo')
+{
+ return $value;
+}
+
class Foo
{
const BAR_NAME = 'bar';
{
return 'not';
}
+
+ public function strToLower($value)
+ {
+ return strtolower($value);
+ }
}
class TestExtension extends Twig_Extension