added support for method calls with arguments
authorfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Mon, 12 Oct 2009 17:05:12 +0000 (17:05 +0000)
committerfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Mon, 12 Oct 2009 17:05:12 +0000 (17:05 +0000)
git-svn-id: http://svn.twig-project.org/trunk@35 93ef8e89-cb99-4229-a87c-7fa0fa45744b

lib/Twig/ExpressionParser.php
lib/Twig/Node/Expression/GetAttr.php
lib/Twig/Template.php
test/fixtures/expressions/array_call.test [new file with mode: 0644]
test/fixtures/expressions/method_call.test [new file with mode: 0644]
test/unit/integrationTest.php

index 6cb5441..fbcd1c3 100644 (file)
@@ -313,12 +313,15 @@ class Twig_ExpressionParser
   {
     $token = $this->parser->getStream()->next();
     $lineno = $token->getLine();
+    $arguments = array();
     if ($token->getValue() == '.')
     {
       $token = $this->parser->getStream()->next();
       if ($token->getType() == Twig_Token::NAME_TYPE || $token->getType() == Twig_Token::NUMBER_TYPE)
       {
         $arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
+
+        $arguments = $this->parseArguments();
       }
       else
       {
@@ -331,7 +334,7 @@ class Twig_ExpressionParser
       $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ']');
     }
 
-    return new Twig_Node_Expression_GetAttr($node, $arg, $lineno, $token->getValue());
+    return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $lineno, $token->getValue());
   }
 
   public function parseFilterExpression($node)
@@ -342,24 +345,33 @@ class Twig_ExpressionParser
     {
       $this->parser->getStream()->next();
       $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE);
-      $args = array();
-      if ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '('))
+
+      $filters[] = array($token->getValue(), $this->parseArguments());
+    }
+
+    return new Twig_Node_Expression_Filter($node, $filters, $lineno);
+  }
+
+  public function parseArguments()
+  {
+    if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '('))
+    {
+      return array();
+    }
+
+    $args = array();
+    $this->parser->getStream()->next();
+    while (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ')'))
+    {
+      if (!empty($args))
       {
-        $this->parser->getStream()->next();
-        while (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ')'))
-        {
-          if (!empty($args))
-          {
-            $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ',');
-          }
-          $args[] = $this->parseExpression();
-        }
-        $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ')');
+        $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ',');
       }
-      $filters[] = array($token->getValue(), $args);
+      $args[] = $this->parseExpression();
     }
+    $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ')');
 
-    return new Twig_Node_Expression_Filter($node, $filters, $lineno);
+    return $args;
   }
 
   public function parseAssignmentExpression()
index 1a03623..ad59f2f 100644 (file)
@@ -13,12 +13,14 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression implements Twig_
 {
   protected $node;
   protected $attr;
+  protected $arguments;
 
-  public function __construct(Twig_Node $node, $attr, $lineno, $token_value)
+  public function __construct(Twig_Node $node, $attr, $arguments, $lineno, $token_value)
   {
     parent::__construct($lineno);
     $this->node = $node;
     $this->attr = $attr;
+    $this->arguments = $arguments;
     $this->token_value = $token_value;
   }
 
@@ -44,11 +46,22 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression implements Twig_
       ->subcompile($this->node)
       ->raw(', ')
       ->subcompile($this->attr)
+      ->raw(', array(')
     ;
 
-    if ('[' == $this->token_value) # Don't look for functions if they're using foo[bar]
+    foreach ($this->arguments as $node)
     {
-      $compiler->raw(', false');
+      $compiler
+        ->subcompile($node)
+        ->raw(', ')
+      ;
+    }
+
+    $compiler->raw(')');
+
+    if ('[' == $this->token_value) // Don't look for functions if they're using foo[bar]
+    {
+      $compiler->raw(', true');
     }
 
     $compiler->raw(')');
index 43a4918..f22b0ce 100644 (file)
@@ -43,15 +43,24 @@ abstract class Twig_Template implements Twig_TemplateInterface
     throw new Twig_RuntimeError(sprintf('The filter "%s" does not exist', $name));
   }
 
-  protected function getAttribute($object, $item)
+  protected function getAttribute($object, $item, array $arguments = array(), $arrayOnly = false)
   {
     $item = (string) $item;
 
-    if (is_array($object) && isset($object[$item]))
+    if (
+      is_array($object) && isset($object[$item])
+      ||
+      is_object($object) && $object instanceof ArrayAccess && isset($object[$item])
+    )
     {
       return $object[$item];
     }
 
+    if ($arrayOnly)
+    {
+      return null;
+    }
+
     if (
       !is_object($object) ||
       (
@@ -68,6 +77,6 @@ abstract class Twig_Template implements Twig_TemplateInterface
       $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
     }
 
-    return $object->$method();
+    return call_user_func_array(array($object, $method), $arguments);
   }
 }
diff --git a/test/fixtures/expressions/array_call.test b/test/fixtures/expressions/array_call.test
new file mode 100644 (file)
index 0000000..f3df328
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+Twig supports method calls
+--TEMPLATE--
+{{ items.foo }}
+{{ items['foo'] }}
+{{ items[foo] }}
+{{ items[items[foo]] }}
+--DATA--
+return array('foo' => 'bar', 'items' => array('foo' => 'bar', 'bar' => 'foo'))
+--EXPECT--
+bar
+bar
+foo
+bar
diff --git a/test/fixtures/expressions/method_call.test b/test/fixtures/expressions/method_call.test
new file mode 100644 (file)
index 0000000..adea3b3
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+Twig supports method calls
+--TEMPLATE--
+{{ items.foo.foo }}
+{{ items.foo.bar }}
+{{ items.foo['bar'] }}
+{{ items.foo.bar('a', 43) }}
+{{ items.foo.bar(foo) }}
+--DATA--
+return array('foo' => 'bar', 'items' => array('foo' => new Foo(), 'bar' => 'foo'))
+--EXPECT--
+foo
+bar
+
+bar_a-43
+bar_bar
index 3f72464..cb5e14e 100644 (file)
@@ -17,7 +17,20 @@ Twig_Autoloader::register();
 
 require_once dirname(__FILE__).'/../lib/Twig_Loader_Var.php';
 
-$t = new LimeTest(42);
+class Foo
+{
+  public function bar($param1 = null, $param2 = null)
+  {
+    return 'bar'.($param1 ? '_'.$param1 : '').($param2 ? '-'.$param2 : '');
+  }
+
+  public function getFoo()
+  {
+    return 'foo';
+  }
+}
+
+$t = new LimeTest(44);
 $fixturesDir = realpath(dirname(__FILE__).'/../fixtures/');
 
 foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file)