made Resource::getAttribute() more generic (WIP)
authorFabien Potencier <fabien.potencier@gmail.com>
Mon, 7 Jun 2010 08:41:15 +0000 (10:41 +0200)
committerFabien Potencier <fabien.potencier@gmail.com>
Mon, 7 Jun 2010 08:41:15 +0000 (10:41 +0200)
lib/Twig/ExpressionParser.php
lib/Twig/Node/Expression/GetAttr.php
lib/Twig/Resource.php
test/Twig/Tests/Node/Expression/GetAttrTest.php

index 53c49fd..a86d7c1 100644 (file)
@@ -370,21 +370,28 @@ class Twig_ExpressionParser
         $token = $this->parser->getStream()->next();
         $lineno = $token->getLine();
         $arguments = new Twig_Node();
+        $type = Twig_Node_Expression_GetAttr::TYPE_ANY;
         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);
 
+                if ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '(')) {
+                    $type = Twig_Node_Expression_GetAttr::TYPE_METHOD;
+                }
+
                 $arguments = $this->parseArguments();
             } else {
                 throw new Twig_SyntaxError('Expected name or number', $lineno);
             }
         } else {
+            $type = Twig_Node_Expression_GetAttr::TYPE_ARRAY;
+
             $arg = $this->parseExpression();
             $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ']');
         }
 
-        return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $lineno, $token->getValue());
+        return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $lineno, $type);
     }
 
     public function parseFilterExpression($node)
index 3803849..583c890 100644 (file)
  */
 class Twig_Node_Expression_GetAttr extends Twig_Node_Expression
 {
-    public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_NodeInterface $arguments, $lineno, $token_value = null)
+    const TYPE_ANY = 'any';
+    const TYPE_ARRAY = 'array';
+    const TYPE_METHOD = 'method';
+
+    public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_NodeInterface $arguments, $lineno, $type = self::TYPE_ANY)
     {
-        parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('token_value' => $token_value), $lineno);
+        parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type), $lineno);
     }
 
     public function compile($compiler)
@@ -33,13 +37,9 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression
             ;
         }
 
-        $compiler->raw(')');
-
-        // Don't look for functions if they're using foo[bar]
-        if ('[' == $this['token_value']) {
-            $compiler->raw(', true');
-        }
-
-        $compiler->raw(')');
+        $compiler
+            ->raw('), ')
+            ->repr($this['type'])
+            ->raw(')');
     }
 }
index c30a115..0386a0b 100644 (file)
@@ -37,30 +37,43 @@ abstract class Twig_Resource
         throw new InvalidArgumentException(sprintf('Item "%s" from context does not exist.', $item));
     }
 
-    protected function getAttribute($object, $item, array $arguments = array(), $arrayOnly = false)
+    protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_Node_Expression_GetAttr::TYPE_ANY)
     {
-        $item = (string) $item;
+        // array
+        if (Twig_Node_Expression_GetAttr::TYPE_METHOD !== $type) {
+            if ((is_array($object) || is_object($object) && $object instanceof ArrayAccess) && isset($object[$item])) {
+                return $object[$item];
+            }
+
+            if (Twig_Node_Expression_GetAttr::TYPE_ARRAY === $type) {
+                if (!$this->env->isStrictVariables()) {
+                    return null;
+                }
 
-        if ((is_array($object) || is_object($object) && $object instanceof ArrayAccess) && isset($object[$item])) {
-            return $object[$item];
+                throw new InvalidArgumentException(sprintf('Key "%s" for array "%s" does not exist.', $item, $object));
+            }
         }
 
-        if ($arrayOnly || !is_object($object)) {
+        if (!is_object($object)) {
             if (!$this->env->isStrictVariables()) {
                 return null;
             }
 
-            throw new InvalidArgumentException(sprintf('Key "%s" for array "%s" does not exist.', $item, $object));
+            throw new InvalidArgumentException(sprintf('Item "%s" for "%s" does not exist.', $item, $object));
         }
 
-        if (isset($object->$item)) {
-            if ($this->env->hasExtension('sandbox')) {
-                $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
-            }
+        // object property
+        if (Twig_Node_Expression_GetAttr::TYPE_METHOD !== $type) {
+            if (isset($object->$item)) {
+                if ($this->env->hasExtension('sandbox')) {
+                    $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
+                }
 
-            return $object->$item;
+                return $object->$item;
+            }
         }
 
+        // object method
         $class = get_class($object);
 
         if (!isset($this->cache[$class])) {
index b7f4393..4694ae2 100644 (file)
@@ -24,12 +24,12 @@ class Twig_Tests_Node_Expression_GetAttrTest extends Twig_Tests_Node_TestCase
             new Twig_Node_Expression_Name('foo', 0),
             new Twig_Node_Expression_Constant('bar', 0),
         ));
-        $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, 0, '[');
+        $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, 0, Twig_Node_Expression_GetAttr::TYPE_ARRAY);
 
         $this->assertEquals($expr, $node->node);
         $this->assertEquals($attr, $node->attribute);
         $this->assertEquals($args, $node->arguments);
-        $this->assertEquals('[', $node['token_value']);
+        $this->assertEquals(Twig_Node_Expression_GetAttr::TYPE_ARRAY, $node['type']);
     }
 
     /**
@@ -49,17 +49,18 @@ class Twig_Tests_Node_Expression_GetAttrTest extends Twig_Tests_Node_TestCase
         $attr = new Twig_Node_Expression_Constant('bar', 0);
         $args = new Twig_Node();
         $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, 0);
-        $tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array())');
+        $tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array(), "any")');
+
+        $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, 0, Twig_Node_Expression_GetAttr::TYPE_ARRAY);
+        $tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array(), "array")');
 
-        $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, 0, '[');
-        $tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array(), true)');
 
         $args = new Twig_Node(array(
             new Twig_Node_Expression_Name('foo', 0),
             new Twig_Node_Expression_Constant('bar', 0),
         ));
-        $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, 0);
-        $tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array($this->getContext($context, \'foo\'), "bar", ))');
+        $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, 0, Twig_Node_Expression_GetAttr::TYPE_METHOD);
+        $tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array($this->getContext($context, \'foo\'), "bar", ), "method")');
 
         return $tests;
     }