Twig icon indicating copy to clipboard operation
Twig copied to clipboard

Aliased/extended classes may not be reflected correctly when creating filter expressions

Open bennothommo opened this issue 3 years ago • 1 comments

The class reflection changes that were introduced in https://github.com/twigphp/Twig/commit/bb64a49441a00787ab2ea8f322b450af34f2778f may cause issues with classes that have __call or __callStatic magic method that belong to a parent class, like Laravel facades.

I've provided a test case as a patch below.

diff --git a/tests/Node/Expression/FilterTest.php b/tests/Node/Expression/FilterTest.php
index ef7c82cb..1a86e800 100644
--- a/tests/Node/Expression/FilterTest.php
+++ b/tests/Node/Expression/FilterTest.php
@@ -49,6 +49,7 @@ class FilterTest extends NodeTestCase
                 return [
                     new TwigFilter('foo', \Closure::fromCallable([$this, 'foo'])),
                     new TwigFilter('foobar', \Closure::fromCallable([$this, 'foobar'])),
+                    new TwigFilter('alias_bar', ['Twig_Test_Alias_Class', 'test']),
                 ];
             }
 
@@ -135,6 +136,10 @@ class FilterTest extends NodeTestCase
         $node = $this->createFilter($string, 'foobar');
         $tests[] = [$node, '$this->env->getFilter(\'foobar\')->getCallable()("abc")', $environment];
 
+        // aliased class
+        $node = $this->createFilter($string, 'alias_bar');
+        $tests[] = [$node, '$this->env->getFilter(\'alias_bar\')->getCallable()("abc")', $environment];
+
         return $tests;
     }
 
@@ -190,3 +195,31 @@ function twig_tests_filter_dummy()
 function twig_tests_filter_barbar($context, $string, $arg1 = null, $arg2 = null, array $args = [])
 {
 }
+
+// Sample class with a magic method
+class Twig_Test_Parent_Class
+{
+    public static function getAccessor()
+    {
+        throw new \Exception('Accessor not set');
+    }
+
+    public static function __callStatic($name, $arguments)
+    {
+        $accessor = static::getAccessor();
+
+        if ($name === 'test') {
+            return 'received ' . $arguments[0];
+        }
+    }
+}
+
+class Twig_Test_Child_Class extends Twig_Test_Parent_Class
+{
+    public static function getAccessor()
+    {
+        return 'child';
+    }
+}
+
+class_alias(Twig_Test_Child_Class::class, 'Twig_Test_Alias_Class');

It passes in the test case, in the sense that it creates the compiled getFilter call just fine - the issue would only manifest if one were to run the actual template, as it would throw the Accessor not set exception due to the $r->getClosureScopeClass() call here returning the class as Twig_Test_Parent_Class - ostensibly because this is the class that will actually fulfill the callable.

Refs:

  • https://github.com/wintercms/winter/issues/557#issuecomment-1160480419
  • https://github.com/twigphp/Twig/pull/3702#issuecomment-1160511737

bennothommo avatar Jun 21 '22 15:06 bennothommo

@stof @nicolas-grekas is this enough information to go on?

LukeTowers avatar Jun 26 '22 21:06 LukeTowers