From 04b1ad3cedfb81f48b6a7ec8d77a2116c604c7d6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 13 May 2024 18:01:26 +0200 Subject: [PATCH] Fix old escaper signature + add docs --- CHANGELOG | 2 +- doc/filters/escape.rst | 29 +++++++++++++++++++++++----- doc/internals.rst | 7 +++++-- src/Extension/EscaperExtension.php | 8 ++++---- tests/Extension/EscaperTest.php | 22 ++++++++++----------- tests/Runtime/EscaperRuntimeTest.php | 12 ++++++------ 6 files changed, 51 insertions(+), 29 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index eed466a2655..80234c86d33 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,6 @@ # 3.10.2 (2024-XX-XX) - * n/a + * Fix support for the deprecated escaper signature # 3.10.1 (2024-05-12) diff --git a/doc/filters/escape.rst b/doc/filters/escape.rst index e8b735db46b..70d18eb5722 100644 --- a/doc/filters/escape.rst +++ b/doc/filters/escape.rst @@ -93,15 +93,34 @@ to learn more about this topic. Custom Escapers --------------- +.. versionadded:: 3.10 + + The ``EscaperRuntime`` class has been added in 3.10. On previous versions, + you can define custom escapers by calling the ``setEscaper()`` method on + the escaper extension instance. The first argument is the escaper strategy + (to be used in the ``escape`` call) and the second one must be a valid PHP + callable:: + + use Twig\Extension\EscaperExtension; + + $twig = new \Twig\Environment($loader); + $twig->getExtension(EscaperExtension::class)->setEscaper('csv', 'csv_escaper'); + + When called by Twig, the callable receives the Twig environment instance, + the string to escape, and the charset. + You can define custom escapers by calling the ``setEscaper()`` method on the -escaper extension instance. The first argument is the escaper name (to be -used in the ``escape`` call) and the second one must be a valid PHP callable:: +escaper runtime instance. It accepts two arguments: the strategy name and a PHP +callable that accepts a string to escape and the charset:: + + use Twig\Runtime\EscaperRuntime; $twig = new \Twig\Environment($loader); - $twig->getExtension(\Twig\Extension\EscaperExtension::class)->setEscaper('csv', 'csv_escaper'); + $escaper = fn ($string, $charset) => $string; + $twig->getRuntime(EscaperRuntime::class)->setEscaper('identity', $escaper); -When called by Twig, the callable receives the Twig environment instance, the -string to escape, and the charset. + # Usage in a template: + # {{ 'foo'|escape('identity') }} .. note:: diff --git a/doc/internals.rst b/doc/internals.rst index ccbb202f372..2aeb12f3fc4 100644 --- a/doc/internals.rst +++ b/doc/internals.rst @@ -124,9 +124,12 @@ using):: { protected function doDisplay(array $context, array $blocks = []) { + $macros = $this->macros; // line 1 - echo "Hello "; - echo twig_escape_filter($this->env, (isset($context["name"]) ? $context["name"] : null), "html", null, true); + yield "Hello "; + // line 2 + yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape((isset($context["name"]) || array_key_exists("name", $context) ? $context["name"] : (function () { throw new RuntimeError('Variable "name" does not exist.', 2, $this->source); })()), "html", null, true); + return; yield ''; } // some more code diff --git a/src/Extension/EscaperExtension.php b/src/Extension/EscaperExtension.php index cf821c6d5d4..3360ef6710f 100644 --- a/src/Extension/EscaperExtension.php +++ b/src/Extension/EscaperExtension.php @@ -117,8 +117,8 @@ public function getDefaultStrategy(string $name) /** * Defines a new escaper to be used via the escape filter. * - * @param string $strategy The strategy name that should be used as a strategy in the escape call - * @param callable(Environment, string) $callable A valid PHP callable + * @param string $strategy The strategy name that should be used as a strategy in the escape call + * @param callable(Environment, string, string) $callable A valid PHP callable * * @deprecated since Twig 3.10 */ @@ -132,7 +132,7 @@ public function setEscaper($strategy, callable $callable) $this->escapers[$strategy] = $callable; $callable = function ($string, $charset) use ($callable) { - return $callable($this->environment, $string); + return $callable($this->environment, $string, $charset); }; $this->escaper->setEscaper($strategy, $callable); @@ -141,7 +141,7 @@ public function setEscaper($strategy, callable $callable) /** * Gets all defined escapers. * - * @return array An array of escapers + * @return array An array of escapers * * @deprecated since Twig 3.10 */ diff --git a/tests/Extension/EscaperTest.php b/tests/Extension/EscaperTest.php index 950bd1b47b6..e211677bfa8 100644 --- a/tests/Extension/EscaperTest.php +++ b/tests/Extension/EscaperTest.php @@ -29,15 +29,15 @@ public function testCustomEscaper($expected, $string, $strategy) $twig = new Environment($this->createMock(LoaderInterface::class)); $escaperExt = $twig->getExtension(EscaperExtension::class); $escaperExt->setEscaper('foo', 'Twig\Tests\legacy_escaper'); - $this->assertSame($expected, $twig->getRuntime(EscaperRuntime::class)->escape($string, $strategy)); + $this->assertSame($expected, $twig->getRuntime(EscaperRuntime::class)->escape($string, $strategy, 'ISO-8859-1')); } public function provideCustomEscaperCases() { return [ - ['fooUTF-8', 'foo', 'foo'], - ['UTF-8', null, 'foo'], - ['42UTF-8', 42, 'foo'], + ['foo**ISO-8859-1**UTF-8', 'foo', 'foo'], + ['**ISO-8859-1**UTF-8', null, 'foo'], + ['42**ISO-8859-1**UTF-8', 42, 'foo'], ]; } @@ -51,7 +51,7 @@ public function testCustomEscaperWithoutCallingSetEscaperRuntime($expected, $str $twig = new Environment($this->createMock(LoaderInterface::class)); $escaperExt = $twig->getExtension(EscaperExtension::class); $escaperExt->setEscaper('foo', 'Twig\Tests\legacy_escaper'); - $this->assertSame($expected, $twig->getRuntime(EscaperRuntime::class)->escape($string, $strategy)); + $this->assertSame($expected, $twig->getRuntime(EscaperRuntime::class)->escape($string, $strategy, 'ISO-8859-1')); } /** @@ -67,17 +67,17 @@ public function testCustomEscapersOnMultipleEnvs() $escaperExt2 = $env2->getExtension(EscaperExtension::class); $escaperExt2->setEscaper('foo', 'Twig\Tests\legacy_escaper_again'); - $this->assertSame('fooUTF-8', $env1->getRuntime(EscaperRuntime::class)->escape('foo', 'foo')); - $this->assertSame('fooUTF-81', $env2->getRuntime(EscaperRuntime::class)->escape('foo', 'foo')); + $this->assertSame('foo**ISO-8859-1**UTF-8', $env1->getRuntime(EscaperRuntime::class)->escape('foo', 'foo', 'ISO-8859-1')); + $this->assertSame('foo**ISO-8859-1**UTF-8**again', $env2->getRuntime(EscaperRuntime::class)->escape('foo', 'foo', 'ISO-8859-1')); } } -function legacy_escaper(Environment $twig, $string) +function legacy_escaper(Environment $twig, $string, $charset) { - return $string.$twig->getCharset(); + return $string.'**'.$charset.'**'.$twig->getCharset(); } -function legacy_escaper_again(Environment $twig, $string) +function legacy_escaper_again(Environment $twig, $string, $charset) { - return $string.$twig->getCharset().'1'; + return $string.'**'.$charset.'**'.$twig->getCharset().'**again'; } diff --git a/tests/Runtime/EscaperRuntimeTest.php b/tests/Runtime/EscaperRuntimeTest.php index 042473408d3..11764f4384f 100644 --- a/tests/Runtime/EscaperRuntimeTest.php +++ b/tests/Runtime/EscaperRuntimeTest.php @@ -350,19 +350,19 @@ public function testUnknownCustomEscaper() /** * @dataProvider provideCustomEscaperCases */ - public function testCustomEscaper($expected, $string, $strategy) + public function testCustomEscaper($expected, $string, $strategy, $charset) { $escaper = new EscaperRuntime(); $escaper->setEscaper('foo', 'Twig\Tests\escaper'); - $this->assertSame($expected, $escaper->escape($string, $strategy)); + $this->assertSame($expected, $escaper->escape($string, $strategy, $charset)); } public function provideCustomEscaperCases() { return [ - ['fooUTF-8', 'foo', 'foo'], - ['UTF-8', null, 'foo'], - ['42UTF-8', 42, 'foo'], + ['foo**ISO-8859-1', 'foo', 'foo', 'ISO-8859-1'], + ['**ISO-8859-1', null, 'foo', 'ISO-8859-1'], + ['42**UTF-8', 42, 'foo', null], ]; } @@ -391,7 +391,7 @@ public function provideObjectsForEscaping() function escaper($string, $charset) { - return $string.$charset; + return $string.'**'.$charset; } interface Extension_SafeHtmlInterface