diff --git a/CHANGELOG b/CHANGELOG index de94a0afe..30d661735 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ * 2.0.0 (2016-XX-XX) + * moved Twig_Environment::getAttribute() to twig_get_attribute() * removed Twig_Environment::getLexer(), Twig_Environment::getParser(), Twig_Environment::getCompiler() * removed Twig_Compiler::getFilename() * added hasser support in Twig_Template::getAttribute() diff --git a/lib/Twig/Environment.php b/lib/Twig/Environment.php index 6cbc1a7e0..c56ca8042 100644 --- a/lib/Twig/Environment.php +++ b/lib/Twig/Environment.php @@ -926,7 +926,6 @@ class Twig_Environment { $this->optionsHash = implode(':', array( $this->extensionSet->getSignature(), - (int) function_exists('twig_template_get_attributes'), PHP_MAJOR_VERSION, PHP_MINOR_VERSION, self::VERSION, diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index f48a24e46..8e8bcada6 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -1337,3 +1337,188 @@ function twig_array_batch($items, $size, $fill = null) return $result; } + +/** + * Returns the attribute value for a given array/object. + * + * @param mixed $object The object or array from where to get the item + * @param mixed $item The item to get from the array or object + * @param array $arguments An array of arguments to pass if the item is an object method + * @param string $type The type of attribute (@see Twig_Template constants) + * @param bool $isDefinedTest Whether this is only a defined check + * @param bool $ignoreStrictCheck Whether to ignore the strict attribute check or not + * + * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true + * + * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false + * + * @internal + */ +function twig_get_attribute(Twig_Environment $env, Twig_Source $source, $object, $item, array $arguments = array(), $type = Twig_Template::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false) +{ + static $cache = array(); + + // array + if (Twig_Template::METHOD_CALL !== $type) { + $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item; + + if ((is_array($object) && (isset($object[$arrayItem]) || array_key_exists($arrayItem, $object))) + || ($object instanceof ArrayAccess && isset($object[$arrayItem])) + ) { + if ($isDefinedTest) { + return true; + } + + return $object[$arrayItem]; + } + + if (Twig_Template::ARRAY_CALL === $type || !is_object($object)) { + if ($isDefinedTest) { + return false; + } + + if ($ignoreStrictCheck || !$env->isStrictVariables()) { + return; + } + + if ($object instanceof ArrayAccess) { + $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist.', $arrayItem, get_class($object)); + } elseif (is_object($object)) { + $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, get_class($object)); + } elseif (is_array($object)) { + if (empty($object)) { + $message = sprintf('Key "%s" does not exist as the array is empty.', $arrayItem); + } else { + $message = sprintf('Key "%s" for array with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object))); + } + } elseif (Twig_Template::ARRAY_CALL === $type) { + if (null === $object) { + $message = sprintf('Impossible to access a key ("%s") on a null variable.', $item); + } else { + $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, gettype($object), $object); + } + } elseif (null === $object) { + $message = sprintf('Impossible to access an attribute ("%s") on a null variable.', $item); + } else { + $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, gettype($object), $object); + } + + throw new Twig_Error_Runtime($message, -1, $source); + } + } + + if (!is_object($object)) { + if ($isDefinedTest) { + return false; + } + + if ($ignoreStrictCheck || !$env->isStrictVariables()) { + return; + } + + if (null === $object) { + $message = sprintf('Impossible to invoke a method ("%s") on a null variable.', $item); + } else { + $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, gettype($object), $object); + } + + throw new Twig_Error_Runtime($message, -1, $source); + } + + if ($object instanceof Twig_Template) { + throw new Twig_Error_Runtime('Accessing Twig_Template attributes is forbidden.'); + } + + // object property + if (Twig_Template::METHOD_CALL !== $type) { + if (isset($object->$item) || array_key_exists((string) $item, $object)) { + if ($isDefinedTest) { + return true; + } + + if ($env->hasExtension('Twig_Extension_Sandbox')) { + $env->getExtension('Twig_Extension_Sandbox')->checkPropertyAllowed($object, $item); + } + + return $object->$item; + } + } + + $class = get_class($object); + + // object method + // precedence: getXxx() > isXxx() > hasXxx() + if (!isset($cache[$class])) { + $methods = get_class_methods($object); + sort($methods); + $lcMethods = array_map('strtolower', $methods); + $cache = array(); + foreach ($methods as $i => $method) { + $cache[$method] = $method; + $cache[$lcName = $lcMethods[$i]] = $method; + + if ('g' === $lcName[0] && 0 === strpos($lcName, 'get')) { + $name = substr($method, 3); + $lcName = substr($lcName, 3); + } elseif ('i' === $lcName[0] && 0 === strpos($lcName, 'is')) { + $name = substr($method, 2); + $lcName = substr($lcName, 2); + } elseif ('h' === $lcName[0] && 0 === strpos($lcName, 'has')) { + $name = substr($method, 3); + $lcName = substr($lcName, 3); + if (in_array('is'.$lcName, $lcMethods)) { + continue; + } + } else { + continue; + } + + if (!isset($cache[$name])) { + $cache[$name] = $method; + $cache[$lcName] = $method; + } + } + $cache[$class] = $cache; + } + + $call = false; + if (isset($cache[$class][$item])) { + $method = $cache[$class][$item]; + } elseif (isset($cache[$class][$lcItem = strtolower($item)])) { + $method = $cache[$class][$lcItem]; + } elseif (isset($cache[$class]['__call'])) { + $method = $item; + $call = true; + } else { + if ($isDefinedTest) { + return false; + } + + if ($ignoreStrictCheck || !$env->isStrictVariables()) { + return; + } + + throw new Twig_Error_Runtime(sprintf('Neither the property "%1$s" nor one of the methods "%1$s()", "get%1$s()"/"is%1$s()"/"has%1$s()" or "__call()" exist and have public access in class "%2$s".', $item, $class), -1, $source); + } + + if ($isDefinedTest) { + return true; + } + + if ($env->hasExtension('Twig_Extension_Sandbox')) { + $env->getExtension('Twig_Extension_Sandbox')->checkMethodAllowed($object, $method); + } + + // Some objects throw exceptions when they have __call, and the method we try + // to call is not supported. If ignoreStrictCheck is true, we should return null. + try { + $ret = $object->$method(...$arguments); + } catch (BadMethodCallException $e) { + if ($call && ($ignoreStrictCheck || !$env->isStrictVariables())) { + return; + } + throw $e; + } + + return $ret; +} diff --git a/lib/Twig/Node/Expression/GetAttr.php b/lib/Twig/Node/Expression/GetAttr.php index 1cadbe2cc..0cb78f638 100644 --- a/lib/Twig/Node/Expression/GetAttr.php +++ b/lib/Twig/Node/Expression/GetAttr.php @@ -23,11 +23,7 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression public function compile(Twig_Compiler $compiler) { - if (function_exists('twig_template_get_attributes')) { - $compiler->raw('twig_template_get_attributes($this, '); - } else { - $compiler->raw('$this->getAttribute('); - } + $compiler->raw('twig_get_attribute($this->env, $this->getSourceContext(), '); if ($this->getAttribute('ignore_strict_check')) { $this->getNode('node')->setAttribute('ignore_strict_check', true); diff --git a/lib/Twig/Template.php b/lib/Twig/Template.php index b4e98a8b3..f978bdcad 100644 --- a/lib/Twig/Template.php +++ b/lib/Twig/Template.php @@ -416,192 +416,4 @@ abstract class Twig_Template * @param array $blocks An array of blocks to pass to the template */ abstract protected function doDisplay(array $context, array $blocks = array()); - - /** - * Returns the attribute value for a given array/object. - * - * @param mixed $object The object or array from where to get the item - * @param mixed $item The item to get from the array or object - * @param array $arguments An array of arguments to pass if the item is an object method - * @param string $type The type of attribute (@see Twig_Template constants) - * @param bool $isDefinedTest Whether this is only a defined check - * @param bool $ignoreStrictCheck Whether to ignore the strict attribute check or not - * - * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true - * - * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false - * - * @internal - */ - protected function getAttribute($object, $item, array $arguments = array(), $type = self::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false) - { - // array - if (self::METHOD_CALL !== $type) { - $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item; - - if ((is_array($object) && (isset($object[$arrayItem]) || array_key_exists($arrayItem, $object))) - || ($object instanceof ArrayAccess && isset($object[$arrayItem])) - ) { - if ($isDefinedTest) { - return true; - } - - return $object[$arrayItem]; - } - - if (self::ARRAY_CALL === $type || !is_object($object)) { - if ($isDefinedTest) { - return false; - } - - if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { - return; - } - - if ($object instanceof ArrayAccess) { - $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist.', $arrayItem, get_class($object)); - } elseif (is_object($object)) { - $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, get_class($object)); - } elseif (is_array($object)) { - if (empty($object)) { - $message = sprintf('Key "%s" does not exist as the array is empty.', $arrayItem); - } else { - $message = sprintf('Key "%s" for array with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object))); - } - } elseif (self::ARRAY_CALL === $type) { - if (null === $object) { - $message = sprintf('Impossible to access a key ("%s") on a null variable.', $item); - } else { - $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, gettype($object), $object); - } - } elseif (null === $object) { - $message = sprintf('Impossible to access an attribute ("%s") on a null variable.', $item); - } else { - $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, gettype($object), $object); - } - - throw new Twig_Error_Runtime($message, -1, $this->getSourceContext()); - } - } - - if (!is_object($object)) { - if ($isDefinedTest) { - return false; - } - - if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { - return; - } - - if (null === $object) { - $message = sprintf('Impossible to invoke a method ("%s") on a null variable.', $item); - } else { - $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, gettype($object), $object); - } - - throw new Twig_Error_Runtime($message, -1, $this->getSourceContext()); - } - - // object property - if (self::METHOD_CALL !== $type && !$object instanceof self) { // Twig_Template does not have public properties, and we don't want to allow access to internal ones - if (isset($object->$item) || array_key_exists((string) $item, $object)) { - if ($isDefinedTest) { - return true; - } - - if ($this->env->hasExtension('Twig_Extension_Sandbox')) { - $this->env->getExtension('Twig_Extension_Sandbox')->checkPropertyAllowed($object, $item); - } - - return $object->$item; - } - } - - $class = get_class($object); - - // object method - if (!isset(self::$cache[$class])) { - // get_class_methods returns all methods accessible in the scope, but we only want public ones to be accessible in templates - if ($object instanceof self) { - $ref = new ReflectionClass($class); - $methods = array(); - foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod) { - $methods[] = $refMethod->name; - } - } else { - $methods = get_class_methods($object); - } - // sort values to have consistent behavior, so that "get" methods win precedence over "is" methods - sort($methods); - - $cache = array(); - - foreach ($methods as $method) { - $cache[$method] = $method; - $cache[$lcName = strtolower($method)] = $method; - - if ('g' === $lcName[0] && 0 === strpos($lcName, 'get')) { - $name = substr($method, 3); - $lcName = substr($lcName, 3); - } elseif ('i' === $lcName[0] && 0 === strpos($lcName, 'is')) { - $name = substr($method, 2); - $lcName = substr($lcName, 2); - } elseif ('h' === $lcName[0] && 0 === strpos($lcName, 'has')) { - $name = substr($method, 3); - $lcName = substr($lcName, 3); - } else { - continue; - } - - if (!isset($cache[$name])) { - $cache[$name] = $method; - } - if (!isset($cache[$lcName])) { - $cache[$lcName] = $method; - } - } - self::$cache[$class] = $cache; - } - - $call = false; - if (isset(self::$cache[$class][$item])) { - $method = self::$cache[$class][$item]; - } elseif (isset(self::$cache[$class][$lcItem = strtolower($item)])) { - $method = self::$cache[$class][$lcItem]; - } elseif (isset(self::$cache[$class]['__call'])) { - $method = $item; - $call = true; - } else { - if ($isDefinedTest) { - return false; - } - - if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { - return; - } - - throw new Twig_Error_Runtime(sprintf('Neither the property "%1$s" nor one of the methods "%1$s()", "get%1$s()"/"is%1$s()" or "__call()" exist and have public access in class "%2$s".', $item, $class), -1, $this->getSourceContext()); - } - - if ($isDefinedTest) { - return true; - } - - if ($this->env->hasExtension('Twig_Extension_Sandbox')) { - $this->env->getExtension('Twig_Extension_Sandbox')->checkMethodAllowed($object, $method); - } - - // Some objects throw exceptions when they have __call, and the method we try - // to call is not supported. If ignoreStrictCheck is true, we should return null. - try { - $ret = $object->$method(...$arguments); - } catch (BadMethodCallException $e) { - if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) { - return; - } - throw $e; - } - - return $ret; - } } diff --git a/lib/Twig/Test/NodeTestCase.php b/lib/Twig/Test/NodeTestCase.php index a360aa17e..53334f19c 100644 --- a/lib/Twig/Test/NodeTestCase.php +++ b/lib/Twig/Test/NodeTestCase.php @@ -55,10 +55,6 @@ abstract class Twig_Test_NodeTestCase extends PHPUnit_Framework_TestCase protected function getAttributeGetter() { - if (function_exists('twig_template_get_attributes')) { - return 'twig_template_get_attributes($this, '; - } - - return '$this->getAttribute('; + return 'twig_get_attribute($this->env, $this->getSourceContext(), '; } } diff --git a/test/Twig/Tests/TemplateTest.php b/test/Twig/Tests/TemplateTest.php index 43722a96c..820f7f79f 100644 --- a/test/Twig/Tests/TemplateTest.php +++ b/test/Twig/Tests/TemplateTest.php @@ -61,8 +61,8 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase array('{{ empty_array.a }}', 'Key "a" does not exist as the array is empty in "%s" at line 1.'), array('{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1.'), array('{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1.'), - array('{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()"/"isa()" or "__call()" exist and have public access in class "Twig_TemplateArrayAccessObject" in "%s" at line 1.'), - array('{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()"/"ismissing_method()" or "__call()" exist and have public access in class "Twig_TemplateArrayAccessObject" in "%s" at line 1.'), + array('{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()"/"isa()"/"hasa()" or "__call()" exist and have public access in class "Twig_TemplateArrayAccessObject" in "%s" at line 1.'), + array('{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()"/"ismissing_method()"/"hasmissing_method()" or "__call()" exist and have public access in class "Twig_TemplateArrayAccessObject" in "%s" at line 1.'), array('{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.'), array('{{ object["a"] }}', 'Impossible to access a key "a" on an object of class "stdClass" that does not implement ArrayAccess interface in "%s" at line 1.'), ); @@ -79,7 +79,7 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase $template = new Twig_TemplateTest($twig); try { - $template->getAttribute($object, $item, array(), 'any'); + twig_get_attribute($twig, $template->getSourceContext(), $object, $item, array(), 'any'); if (!$allowed) { $this->fail(); @@ -133,7 +133,8 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase public function testGetAttributeOnArrayWithConfusableKey() { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $template = new Twig_TemplateTest($twig); $array = array('Zero', 'One', -1 => 'MinusOne', '' => 'EmptyString', '1.5' => 'FloatButString', '01' => 'IntegerButStringWithLeadingZeros'); @@ -146,14 +147,14 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase $this->assertSame('IntegerButStringWithLeadingZeros', $array['01']); $this->assertSame('EmptyString', $array[null]); - $this->assertSame('Zero', $template->getAttribute($array, false), 'false is treated as 0 when accessing an array (equals PHP behavior)'); - $this->assertSame('One', $template->getAttribute($array, true), 'true is treated as 1 when accessing an array (equals PHP behavior)'); - $this->assertSame('One', $template->getAttribute($array, 1.5), 'float is casted to int when accessing an array (equals PHP behavior)'); - $this->assertSame('One', $template->getAttribute($array, '1'), '"1" is treated as integer 1 when accessing an array (equals PHP behavior)'); - $this->assertSame('MinusOne', $template->getAttribute($array, -1.5), 'negative float is casted to int when accessing an array (equals PHP behavior)'); - $this->assertSame('FloatButString', $template->getAttribute($array, '1.5'), '"1.5" is treated as-is when accessing an array (equals PHP behavior)'); - $this->assertSame('IntegerButStringWithLeadingZeros', $template->getAttribute($array, '01'), '"01" is treated as-is when accessing an array (equals PHP behavior)'); - $this->assertSame('EmptyString', $template->getAttribute($array, null), 'null is treated as "" when accessing an array (equals PHP behavior)'); + $this->assertSame('Zero', twig_get_attribute($twig, $template->getSourceContext(), $array, false), 'false is treated as 0 when accessing an array (equals PHP behavior)'); + $this->assertSame('One', twig_get_attribute($twig, $template->getSourceContext(), $array, true), 'true is treated as 1 when accessing an array (equals PHP behavior)'); + $this->assertSame('One', twig_get_attribute($twig, $template->getSourceContext(), $array, 1.5), 'float is casted to int when accessing an array (equals PHP behavior)'); + $this->assertSame('One', twig_get_attribute($twig, $template->getSourceContext(), $array, '1'), '"1" is treated as integer 1 when accessing an array (equals PHP behavior)'); + $this->assertSame('MinusOne', twig_get_attribute($twig, $template->getSourceContext(), $array, -1.5), 'negative float is casted to int when accessing an array (equals PHP behavior)'); + $this->assertSame('FloatButString', twig_get_attribute($twig, $template->getSourceContext(), $array, '1.5'), '"1.5" is treated as-is when accessing an array (equals PHP behavior)'); + $this->assertSame('IntegerButStringWithLeadingZeros', twig_get_attribute($twig, $template->getSourceContext(), $array, '01'), '"01" is treated as-is when accessing an array (equals PHP behavior)'); + $this->assertSame('EmptyString', twig_get_attribute($twig, $template->getSourceContext(), $array, null), 'null is treated as "" when accessing an array (equals PHP behavior)'); } /** @@ -161,9 +162,10 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase */ public function testGetAttribute($defined, $value, $object, $item, $arguments, $type) { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $template = new Twig_TemplateTest($twig); - $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type)); + $this->assertEquals($value, twig_get_attribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type)); } /** @@ -171,13 +173,14 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase */ public function testGetAttributeStrict($defined, $value, $object, $item, $arguments, $type, $exceptionMessage = null) { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true))); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true)); + $template = new Twig_TemplateTest($twig); if ($defined) { - $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type)); + $this->assertEquals($value, twig_get_attribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type)); } else { try { - $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type)); + $this->assertEquals($value, twig_get_attribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type)); throw new Exception('Expected Twig_Error_Runtime exception.'); } catch (Twig_Error_Runtime $e) { @@ -193,9 +196,10 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase */ public function testGetAttributeDefined($defined, $value, $object, $item, $arguments, $type) { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $template = new Twig_TemplateTest($twig); - $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true)); + $this->assertEquals($defined, twig_get_attribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type, true)); } /** @@ -203,18 +207,20 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase */ public function testGetAttributeDefinedStrict($defined, $value, $object, $item, $arguments, $type) { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true))); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true)); + $template = new Twig_TemplateTest($twig); - $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true)); + $this->assertEquals($defined, twig_get_attribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type, true)); } public function testGetAttributeCallExceptions() { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $template = new Twig_TemplateTest($twig); $object = new Twig_TemplateMagicMethodExceptionObject(); - $this->assertNull($template->getAttribute($object, 'foo')); + $this->assertNull(twig_get_attribute($twig, $template->getSourceContext(), $object, 'foo')); } public function getGetAttributeTests() @@ -227,6 +233,7 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase 'bar' => true, 'foo' => true, 'baz' => 'baz', + 'baf' => 'baf', '09' => '09', '+4' => '+4', ); @@ -257,6 +264,7 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase array(true, true, 'bar'), array(true, true, 'foo'), array(true, 'baz', 'baz'), + array(true, 'baf', 'baf'), array(true, '09', '09'), array(true, '+4', '+4'), ); @@ -395,15 +403,6 @@ class Twig_TemplateTest extends Twig_Template { } - public function getAttribute($object, $item, array $arguments = array(), $type = Twig_Template::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false) - { - if (function_exists('twig_template_get_attributes')) { - return twig_template_get_attributes($this, $object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck); - } else { - return parent::getAttribute($object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck); - } - } - public function block_name($context, array $blocks = array()) { } @@ -421,6 +420,7 @@ class Twig_TemplateArrayAccessObject implements ArrayAccess 'bar' => true, 'foo' => true, 'baz' => 'baz', + 'baf' => 'baf', '09' => '09', '+4' => '+4', ); @@ -455,6 +455,7 @@ class Twig_TemplateMagicPropertyObject 'bar' => true, 'foo' => true, 'baz' => 'baz', + 'baf' => 'baf', '09' => '09', '+4' => '+4', ); @@ -488,6 +489,7 @@ class Twig_TemplatePropertyObject public $bar = true; public $foo = true; public $baz = 'baz'; + public $baf = 'baf'; protected $protected = 'protected'; } @@ -569,9 +571,14 @@ class Twig_TemplateMethodObject return true; } + public function hasBaz() + { + return 'should never be returned (has)'; + } + public function isBaz() { - return 'should never be returned'; + return 'should never be returned (is)'; } public function getBaz() @@ -579,6 +586,16 @@ class Twig_TemplateMethodObject return 'baz'; } + public function hasBaf() + { + return 'should never be returned (has)'; + } + + public function isBaf() + { + return 'baf'; + } + protected function getProtected() { return 'protected';