From 79e5058611e2e249ccb8568e7624f6ed5fc50b60 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 10 May 2010 12:46:52 +0200 Subject: [PATCH] added support for __call() in expression resolution (closes #2) The new algorithm for object method resolution is now the following: * get all methods declared for `$object` using ReflectionObject::getMethods() * check if `$item` is in the list of method names * check if `get$item` is in the list of method names * check if `__call` is defined and pass `$item` as the method name --- CHANGELOG | 2 + lib/Twig/Resource.php | 39 ++++++++++++++++++++++------ test/fixtures/expressions/magic_call.test | 27 ++++++++++++++++++++ 3 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 test/fixtures/expressions/magic_call.test diff --git a/CHANGELOG b/CHANGELOG index c005080..34ddfa1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ * 0.9.6-DEV + * added support for __call() in expression resolution + * fixed node visiting for macros (macros are now visited by visitors as any other node) * fixed nested block definitions with a parent call (rarely useful but nonetheless supported now) * added the cycle filter * fixed the Lexer when mbstring.func_overload is used with an mbstring.internal_encoding different from ASCII diff --git a/lib/Twig/Resource.php b/lib/Twig/Resource.php index d33e4cd..37058ad 100644 --- a/lib/Twig/Resource.php +++ b/lib/Twig/Resource.php @@ -11,10 +11,12 @@ abstract class Twig_Resource { protected $env; + protected $cache; public function __construct(Twig_Environment $env) { $this->env = $env; + $this->cache = array(); } public function getEnvironment() @@ -36,12 +38,12 @@ abstract class Twig_Resource return $object[$item]; } - if ($arrayOnly) + if ($arrayOnly || !is_object($object)) { return null; } - if (is_object($object) && isset($object->$item)) + if (isset($object->$item)) { if ($this->env->hasExtension('sandbox')) { @@ -51,13 +53,32 @@ abstract class Twig_Resource return $object->$item; } - if ( - !is_object($object) || - ( - !method_exists($object, $method = $item) && - !method_exists($object, $method = 'get'.$item) - ) - ) + $class = get_class($object); + + if (!isset($this->cache[$class])) + { + $r = new ReflectionClass($class); + $this->cache[$class] = array(); + foreach ($r->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_FINAL) as $method) + { + $this->cache[$class][strtolower($method->getName())] = true; + } + } + + $item = strtolower($item); + if (isset($this->cache[$class][$item])) + { + $method = $item; + } + elseif (isset($this->cache[$class]['get'.$item])) + { + $method = 'get'.$item; + } + elseif (isset($this->cache[$class]['__call'])) + { + $method = $item; + } + else { return null; } diff --git a/test/fixtures/expressions/magic_call.test b/test/fixtures/expressions/magic_call.test new file mode 100644 index 0000000..159db96 --- /dev/null +++ b/test/fixtures/expressions/magic_call.test @@ -0,0 +1,27 @@ +--TEST-- +Twig supports __call() for attributes +--TEMPLATE-- +{{ foo.foo }} +{{ foo.bar }} +--DATA-- +class TestClassForMagicCallAttributes +{ + public function getBar() + { + return 'bar_from_getbar'; + } + + public function __call($method, $arguments) + { + if ('foo' === $method) + { + return 'foo_from_call'; + } + + return false; + } +} +return array('foo' => new TestClassForMagicCallAttributes()) +--EXPECT-- +foo_from_call +bar_from_getbar -- 1.7.2.5