added support for functions
authorFabien Potencier <fabien.potencier@gmail.com>
Sat, 18 Dec 2010 15:20:05 +0000 (16:20 +0100)
committerFabien Potencier <fabien.potencier@gmail.com>
Sun, 19 Dec 2010 18:48:02 +0000 (19:48 +0100)
CHANGELOG
lib/Twig/ExpressionParser.php
lib/Twig/Function.php [new file with mode: 0644]
lib/Twig/Node/Expression/Function.php [new file with mode: 0644]
lib/Twig/Template.php
test/Twig/Tests/Fixtures/expressions/function.test [new file with mode: 0644]
test/Twig/Tests/integrationTest.php

index a5e1528..baf47ee 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -6,6 +6,7 @@ Backward incompatibilities:
 
 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
index 323c420..e0e323e 100644 (file)
@@ -216,6 +216,8 @@ class Twig_ExpressionParser
                     $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;
                 }
diff --git a/lib/Twig/Function.php b/lib/Twig/Function.php
new file mode 100644 (file)
index 0000000..dd29601
--- /dev/null
@@ -0,0 +1,38 @@
+<?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;
+    }
+}
diff --git a/lib/Twig/Node/Expression/Function.php b/lib/Twig/Node/Expression/Function.php
new file mode 100644 (file)
index 0000000..0cf5e95
--- /dev/null
@@ -0,0 +1,35 @@
+<?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('))');
+    }
+}
index 4f9eab6..6073e35 100644 (file)
@@ -101,6 +101,20 @@ abstract class Twig_Template implements Twig_TemplateInterface
         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
diff --git a/test/Twig/Tests/Fixtures/expressions/function.test b/test/Twig/Tests/Fixtures/expressions/function.test
new file mode 100644 (file)
index 0000000..90409ad
--- /dev/null
@@ -0,0 +1,13 @@
+--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
index 7383735..183a56c 100644 (file)
@@ -79,6 +79,11 @@ class Twig_Tests_IntegrationTest extends PHPUnit_Framework_TestCase
     }
 }
 
+function test_foo($value = 'foo')
+{
+    return $value;
+}
+
 class Foo
 {
     const BAR_NAME = 'bar';
@@ -112,6 +117,11 @@ class Foo
     {
         return 'not';
     }
+
+    public function strToLower($value)
+    {
+        return strtolower($value);
+    }
 }
 
 class TestExtension extends Twig_Extension