In template v2, process escape filters before other filters

This commit is contained in:
Kijin Sung 2025-08-18 22:09:31 +09:00
parent 3b2fa4208d
commit f7543e4c9a
2 changed files with 23 additions and 5 deletions

View file

@ -872,7 +872,16 @@ class TemplateParser_v2
$str = $this->_escapeCurly($str);
$str = $this->_convertVariableScope($str);
// Apply filters.
// Process the escape option first.
foreach ($filters as $filter)
{
if (in_array($filter, ['autoescape', 'autolang', 'escape', 'noescape']))
{
$escape_option = $filter;
}
}
// Apply other filters.
foreach ($filters as $filter)
{
// Separate the filter option from the filter name.
@ -891,14 +900,13 @@ class TemplateParser_v2
}
}
// Apply each filter.
// Apply filters.
switch ($filter)
{
case 'autoescape':
case 'autolang':
case 'escape':
case 'noescape':
$escape_option = $filter;
break;
case 'escapejs':
case 'js':

View file

@ -446,9 +446,19 @@ class TemplateParserV2Test extends \Codeception\Test\Unit
$target = "<?php echo nl2br(htmlspecialchars(\$__Context->foo ?? '', \ENT_QUOTES, 'UTF-8', false)); ?>";
$this->assertEquals($target, $this->_parse($source));
// nl2br() with gratuitous escape
// nl2br() with extra escape
$source = '{{ $foo|nl2br|escape }}';
$target = "<?php echo htmlspecialchars(nl2br(htmlspecialchars(\$__Context->foo ?? '', \ENT_QUOTES, 'UTF-8', false)), \ENT_QUOTES, 'UTF-8', true); ?>";
$target = "<?php echo nl2br(htmlspecialchars(\$__Context->foo ?? '', \ENT_QUOTES, 'UTF-8', true)); ?>";
$this->assertEquals($target, $this->_parse($source));
// nl2br() with noescape
$source = '{{ $foo|nl2br|noescape }}';
$target = "<?php echo nl2br(\$__Context->foo ?? ''); ?>";
$this->assertEquals($target, $this->_parse($source));
// nl2br() with noescape and autoescape
$source = '{{ $foo|noescape|nl2br|autoescape }}';
$target = "<?php echo nl2br(htmlspecialchars(\$__Context->foo ?? '', \ENT_QUOTES, 'UTF-8', false)); ?>";
$this->assertEquals($target, $this->_parse($source));
// Array join (default joiner is comma)