Repository: twigphp/Twig (php)

Issue:
New named callable handling breaks for private methods
As reported in https://github.com/getsentry/sentry-symfony/issues/627 + https://github.com/getsentry/sentry-symfony/pull/628, the Sentry bundle breaks with Twig 3.4.0. This seems related with #3702 and to the fact that our bundle used a closure to invoke a private method: https://github.com/getsentry/sentry-symfony/blob/6177d71f2fd65f4f0f77036a1ca910aa9326767f/src/Twig/SentryExtension.php#L29-L39

Is this intended? Or does it require a fix?

Pull request description:
Fix optimizing non-public named closures
Fix #3703

Patch:
diff --git a/src/Node/Expression/CallExpression.php b/src/Node/Expression/CallExpression.php
index 6d105313..a3b7c7e0 100644
--- a/src/Node/Expression/CallExpression.php
+++ b/src/Node/Expression/CallExpression.php
@@ -289,6 +289,7 @@ abstract class CallExpression extends AbstractExpression
             return $this->reflector = [$r, $callable, $r->class.'::'.$r->name];
         }
 
+        $checkVisibility = $callable instanceof \Closure;
         $r = new \ReflectionFunction(\Closure::fromCallable($callable));
 
         if (false !== strpos($r->name, '{closure}')) {
@@ -305,6 +306,10 @@ abstract class CallExpression extends AbstractExpression
             $callable = $callableName = $r->name;
         }
 
+        if ($checkVisibility && \is_array($callable) && method_exists(...$callable) && !(new \ReflectionMethod(...$callable))->isPublic()) {
+            $callable = $r->getClosure();
+        }
+
         return $this->reflector = [$r, $callable, $callableName];
     }
 }


Test patch:
diff --git a/tests/Node/Expression/FilterTest.php b/tests/Node/Expression/FilterTest.php
index 21efa3c1..ef7c82cb 100644
--- a/tests/Node/Expression/FilterTest.php
+++ b/tests/Node/Expression/FilterTest.php
@@ -48,12 +48,17 @@ class FilterTest extends NodeTestCase
             {
                 return [
                     new TwigFilter('foo', \Closure::fromCallable([$this, 'foo'])),
+                    new TwigFilter('foobar', \Closure::fromCallable([$this, 'foobar'])),
                 ];
             }
 
             public function foo()
             {
             }
+
+            protected function foobar()
+            {
+            }
         };
         $environment->addExtension($extension);
 
@@ -127,6 +132,9 @@ class FilterTest extends NodeTestCase
         $node = $this->createFilter($string, 'foo');
         $tests[] = [$node, sprintf('$this->extensions[\'%s\']->foo("abc")', \get_class($extension)), $environment];
 
+        $node = $this->createFilter($string, 'foobar');
+        $tests[] = [$node, '$this->env->getFilter(\'foobar\')->getCallable()("abc")', $environment];
+
         return $tests;
     }