diff --git a/composer.json b/composer.json index f33756238..bceeedd32 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,7 @@ "autoload": { "files": [ "src/Resources/debug.php", + "src/Resources/escaper.php", "src/Resources/string_loader.php" ], "psr-4" : { diff --git a/src/Extension/EscaperExtension.php b/src/Extension/EscaperExtension.php index ef8879dbd..88faa60a0 100644 --- a/src/Extension/EscaperExtension.php +++ b/src/Extension/EscaperExtension.php @@ -9,12 +9,19 @@ * file that was distributed with this source code. */ -namespace Twig\Extension { +namespace Twig\Extension; + +use Twig\Environment; +use Twig\Error\RuntimeError; use Twig\FileExtensionEscapingStrategy; +use Twig\Markup; +use Twig\Node\Expression\ConstantExpression; +use Twig\Node\Node; use Twig\NodeVisitor\EscaperNodeVisitor; use Twig\TokenParser\AutoEscapeTokenParser; use Twig\TwigFilter; + final class EscaperExtension extends AbstractExtension { private $defaultStrategy; @@ -49,9 +56,9 @@ final class EscaperExtension extends AbstractExtension public function getFilters(): array { return [ - new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']), - new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']), - new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]), + new TwigFilter('escape', [self::class, 'escape'], ['needs_environment' => true, 'is_safe_callback' => [self::class, 'escapeFilterIsSafe']]), + new TwigFilter('e', [self::class, 'escape'], ['needs_environment' => true, 'is_safe_callback' => [self::class, 'escapeFilterIsSafe']]), + new TwigFilter('raw', [self::class, 'raw'], ['is_safe' => ['all']]), ]; } @@ -132,285 +139,279 @@ final class EscaperExtension extends AbstractExtension $this->safeLookup[$strategy][$class] = true; } } -} -} -namespace { -use Twig\Environment; -use Twig\Error\RuntimeError; -use Twig\Extension\EscaperExtension; -use Twig\Markup; -use Twig\Node\Expression\ConstantExpression; -use Twig\Node\Node; - -/** - * Marks a variable as being safe. - * - * @param string $string A PHP variable - */ -function twig_raw_filter($string) -{ - return $string; -} - -/** - * Escapes a string. - * - * @param mixed $string The value to be escaped - * @param string $strategy The escaping strategy - * @param string $charset The charset - * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false) - * - * @return string - */ -function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) -{ - if ($autoescape && $string instanceof Markup) { + /** + * Marks a variable as being safe. + * + * @param string $string A PHP variable + * + * @internal + */ + public static function raw($string) + { return $string; } - if (!\is_string($string)) { - if (\is_object($string) && method_exists($string, '__toString')) { - if ($autoescape) { - $c = \get_class($string); - $ext = $env->getExtension(EscaperExtension::class); - if (!isset($ext->safeClasses[$c])) { - $ext->safeClasses[$c] = []; - foreach (class_parents($string) + class_implements($string) as $class) { - if (isset($ext->safeClasses[$class])) { - $ext->safeClasses[$c] = array_unique(array_merge($ext->safeClasses[$c], $ext->safeClasses[$class])); - foreach ($ext->safeClasses[$class] as $s) { - $ext->safeLookup[$s][$c] = true; + /** + * @internal + */ + public static function escapeFilterIsSafe(Node $filterArgs) + { + foreach ($filterArgs as $arg) { + if ($arg instanceof ConstantExpression) { + return [$arg->getAttribute('value')]; + } + + return []; + } + + return ['html']; + } + + /** + * Escapes a string. + * + * @param mixed $string The value to be escaped + * @param string $strategy The escaping strategy + * @param string $charset The charset + * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false) + * + * @return string + * + * @internal + */ + public static function escape(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) + { + if ($autoescape && $string instanceof Markup) { + return $string; + } + + if (!\is_string($string)) { + if (\is_object($string) && method_exists($string, '__toString')) { + if ($autoescape) { + $c = \get_class($string); + $ext = $env->getExtension(EscaperExtension::class); + if (!isset($ext->safeClasses[$c])) { + $ext->safeClasses[$c] = []; + foreach (class_parents($string) + class_implements($string) as $class) { + if (isset($ext->safeClasses[$class])) { + $ext->safeClasses[$c] = array_unique(array_merge($ext->safeClasses[$c], $ext->safeClasses[$class])); + foreach ($ext->safeClasses[$class] as $s) { + $ext->safeLookup[$s][$c] = true; + } } } } + if (isset($ext->safeLookup[$strategy][$c]) || isset($ext->safeLookup['all'][$c])) { + return (string) $string; + } } - if (isset($ext->safeLookup[$strategy][$c]) || isset($ext->safeLookup['all'][$c])) { - return (string) $string; - } - } - $string = (string) $string; - } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) { - return $string; + $string = (string) $string; + } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) { + return $string; + } } - } - if ('' === $string) { - return ''; - } + if ('' === $string) { + return ''; + } - if (null === $charset) { - $charset = $env->getCharset(); - } + if (null === $charset) { + $charset = $env->getCharset(); + } - switch ($strategy) { - case 'html': - // see https://www.php.net/htmlspecialchars + switch ($strategy) { + case 'html': + // see https://www.php.net/htmlspecialchars - // Using a static variable to avoid initializing the array - // each time the function is called. Moving the declaration on the - // top of the function slow downs other escaping strategies. - static $htmlspecialcharsCharsets = [ - 'ISO-8859-1' => true, 'ISO8859-1' => true, - 'ISO-8859-15' => true, 'ISO8859-15' => true, - 'utf-8' => true, 'UTF-8' => true, - 'CP866' => true, 'IBM866' => true, '866' => true, - 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true, - '1251' => true, - 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true, - 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true, - 'BIG5' => true, '950' => true, - 'GB2312' => true, '936' => true, - 'BIG5-HKSCS' => true, - 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true, - 'EUC-JP' => true, 'EUCJP' => true, - 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true, - ]; - - if (isset($htmlspecialcharsCharsets[$charset])) { - return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset); - } - - if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) { - // cache the lowercase variant for future iterations - $htmlspecialcharsCharsets[$charset] = true; - - return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset); - } - - $string = twig_convert_encoding($string, 'UTF-8', $charset); - $string = htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8'); - - return iconv('UTF-8', $charset, $string); - - case 'js': - // escape all non-alphanumeric characters - // into their \x or \uHHHH representations - if ('UTF-8' !== $charset) { - $string = twig_convert_encoding($string, 'UTF-8', $charset); - } - - if (!preg_match('//u', $string)) { - throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); - } - - $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) { - $char = $matches[0]; - - /* - * A few characters have short escape sequences in JSON and JavaScript. - * Escape sequences supported only by JavaScript, not JSON, are omitted. - * \" is also supported but omitted, because the resulting string is not HTML safe. - */ - static $shortMap = [ - '\\' => '\\\\', - '/' => '\\/', - "\x08" => '\b', - "\x0C" => '\f', - "\x0A" => '\n', - "\x0D" => '\r', - "\x09" => '\t', + // Using a static variable to avoid initializing the array + // each time the function is called. Moving the declaration on the + // top of the function slow downs other escaping strategies. + static $htmlspecialcharsCharsets = [ + 'ISO-8859-1' => true, 'ISO8859-1' => true, + 'ISO-8859-15' => true, 'ISO8859-15' => true, + 'utf-8' => true, 'UTF-8' => true, + 'CP866' => true, 'IBM866' => true, '866' => true, + 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true, + '1251' => true, + 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true, + 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true, + 'BIG5' => true, '950' => true, + 'GB2312' => true, '936' => true, + 'BIG5-HKSCS' => true, + 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true, + 'EUC-JP' => true, 'EUCJP' => true, + 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true, ]; - if (isset($shortMap[$char])) { - return $shortMap[$char]; + if (isset($htmlspecialcharsCharsets[$charset])) { + return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset); } - $codepoint = mb_ord($char, 'UTF-8'); - if (0x10000 > $codepoint) { - return sprintf('\u%04X', $codepoint); + if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) { + // cache the lowercase variant for future iterations + $htmlspecialcharsCharsets[$charset] = true; + + return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset); } - // Split characters outside the BMP into surrogate pairs - // https://tools.ietf.org/html/rfc2781.html#section-2.1 - $u = $codepoint - 0x10000; - $high = 0xD800 | ($u >> 10); - $low = 0xDC00 | ($u & 0x3FF); - - return sprintf('\u%04X\u%04X', $high, $low); - }, $string); - - if ('UTF-8' !== $charset) { - $string = iconv('UTF-8', $charset, $string); - } - - return $string; - - case 'css': - if ('UTF-8' !== $charset) { $string = twig_convert_encoding($string, 'UTF-8', $charset); - } + $string = htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8'); - if (!preg_match('//u', $string)) { - throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); - } + return iconv('UTF-8', $charset, $string); - $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) { - $char = $matches[0]; - - return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8')); - }, $string); - - if ('UTF-8' !== $charset) { - $string = iconv('UTF-8', $charset, $string); - } - - return $string; - - case 'html_attr': - if ('UTF-8' !== $charset) { - $string = twig_convert_encoding($string, 'UTF-8', $charset); - } - - if (!preg_match('//u', $string)) { - throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); - } - - $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) { - /** - * This function is adapted from code coming from Zend Framework. - * - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com) - * @license https://framework.zend.com/license/new-bsd New BSD License - */ - $chr = $matches[0]; - $ord = \ord($chr); - - /* - * The following replaces characters undefined in HTML with the - * hex entity for the Unicode replacement character. - */ - if (($ord <= 0x1F && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7F && $ord <= 0x9F)) { - return '�'; + case 'js': + // escape all non-alphanumeric characters + // into their \x or \uHHHH representations + if ('UTF-8' !== $charset) { + $string = twig_convert_encoding($string, 'UTF-8', $charset); } - /* - * Check if the current character to escape has a name entity we should - * replace it with while grabbing the hex value of the character. - */ - if (1 === \strlen($chr)) { + if (!preg_match('//u', $string)) { + throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); + } + + $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) { + $char = $matches[0]; + /* - * While HTML supports far more named entities, the lowest common denominator - * has become HTML5's XML Serialisation which is restricted to the those named - * entities that XML supports. Using HTML entities would result in this error: - * XML Parsing Error: undefined entity - */ - static $entityMap = [ - 34 => '"', /* quotation mark */ - 38 => '&', /* ampersand */ - 60 => '<', /* less-than sign */ - 62 => '>', /* greater-than sign */ + * A few characters have short escape sequences in JSON and JavaScript. + * Escape sequences supported only by JavaScript, not JSON, are omitted. + * \" is also supported but omitted, because the resulting string is not HTML safe. + */ + static $shortMap = [ + '\\' => '\\\\', + '/' => '\\/', + "\x08" => '\b', + "\x0C" => '\f', + "\x0A" => '\n', + "\x0D" => '\r', + "\x09" => '\t', ]; - if (isset($entityMap[$ord])) { - return $entityMap[$ord]; + if (isset($shortMap[$char])) { + return $shortMap[$char]; } - return sprintf('&#x%02X;', $ord); + $codepoint = mb_ord($char, 'UTF-8'); + if (0x10000 > $codepoint) { + return sprintf('\u%04X', $codepoint); + } + + // Split characters outside the BMP into surrogate pairs + // https://tools.ietf.org/html/rfc2781.html#section-2.1 + $u = $codepoint - 0x10000; + $high = 0xD800 | ($u >> 10); + $low = 0xDC00 | ($u & 0x3FF); + + return sprintf('\u%04X\u%04X', $high, $low); + }, $string); + + if ('UTF-8' !== $charset) { + $string = iconv('UTF-8', $charset, $string); } - /* - * Per OWASP recommendations, we'll use hex entities for any other - * characters where a named entity does not exist. - */ - return sprintf('&#x%04X;', mb_ord($chr, 'UTF-8')); - }, $string); + return $string; - if ('UTF-8' !== $charset) { - $string = iconv('UTF-8', $charset, $string); - } + case 'css': + if ('UTF-8' !== $charset) { + $string = twig_convert_encoding($string, 'UTF-8', $charset); + } - return $string; + if (!preg_match('//u', $string)) { + throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); + } - case 'url': - return rawurlencode($string); + $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) { + $char = $matches[0]; - default: - $escapers = $env->getExtension(EscaperExtension::class)->getEscapers(); - if (\array_key_exists($strategy, $escapers)) { - return $escapers[$strategy]($env, $string, $charset); - } + return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8')); + }, $string); - $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers))); + if ('UTF-8' !== $charset) { + $string = iconv('UTF-8', $charset, $string); + } - throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies)); - } -} + return $string; -/** - * @internal - */ -function twig_escape_filter_is_safe(Node $filterArgs) -{ - foreach ($filterArgs as $arg) { - if ($arg instanceof ConstantExpression) { - return [$arg->getAttribute('value')]; + case 'html_attr': + if ('UTF-8' !== $charset) { + $string = twig_convert_encoding($string, 'UTF-8', $charset); + } + + if (!preg_match('//u', $string)) { + throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); + } + + $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) { + /** + * This function is adapted from code coming from Zend Framework. + * + * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com) + * @license https://framework.zend.com/license/new-bsd New BSD License + */ + $chr = $matches[0]; + $ord = \ord($chr); + + /* + * The following replaces characters undefined in HTML with the + * hex entity for the Unicode replacement character. + */ + if (($ord <= 0x1F && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7F && $ord <= 0x9F)) { + return '�'; + } + + /* + * Check if the current character to escape has a name entity we should + * replace it with while grabbing the hex value of the character. + */ + if (1 === \strlen($chr)) { + /* + * While HTML supports far more named entities, the lowest common denominator + * has become HTML5's XML Serialisation which is restricted to the those named + * entities that XML supports. Using HTML entities would result in this error: + * XML Parsing Error: undefined entity + */ + static $entityMap = [ + 34 => '"', /* quotation mark */ + 38 => '&', /* ampersand */ + 60 => '<', /* less-than sign */ + 62 => '>', /* greater-than sign */ + ]; + + if (isset($entityMap[$ord])) { + return $entityMap[$ord]; + } + + return sprintf('&#x%02X;', $ord); + } + + /* + * Per OWASP recommendations, we'll use hex entities for any other + * characters where a named entity does not exist. + */ + return sprintf('&#x%04X;', mb_ord($chr, 'UTF-8')); + }, $string); + + if ('UTF-8' !== $charset) { + $string = iconv('UTF-8', $charset, $string); + } + + return $string; + + case 'url': + return rawurlencode($string); + + default: + $escapers = $env->getExtension(EscaperExtension::class)->getEscapers(); + if (\array_key_exists($strategy, $escapers)) { + return $escapers[$strategy]($env, $string, $charset); + } + + $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers))); + + throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies)); } - - return []; } - - return ['html']; -} } diff --git a/src/Resources/escaper.php b/src/Resources/escaper.php new file mode 100644 index 000000000..cf038645f --- /dev/null +++ b/src/Resources/escaper.php @@ -0,0 +1,35 @@ +createMock(LoaderInterface::class)); foreach ($this->htmlSpecialChars as $key => $value) { - $this->assertEquals($value, twig_escape_filter($twig, $key, 'html'), 'Failed to escape: '.$key); + $this->assertEquals($value, EscaperExtension::escape($twig, $key, 'html'), 'Failed to escape: '.$key); } } @@ -170,7 +170,7 @@ class Twig_Tests_Extension_EscaperTest extends TestCase { $twig = new Environment($this->createMock(LoaderInterface::class)); foreach ($this->htmlAttrSpecialChars as $key => $value) { - $this->assertEquals($value, twig_escape_filter($twig, $key, 'html_attr'), 'Failed to escape: '.$key); + $this->assertEquals($value, EscaperExtension::escape($twig, $key, 'html_attr'), 'Failed to escape: '.$key); } } @@ -178,7 +178,7 @@ class Twig_Tests_Extension_EscaperTest extends TestCase { $twig = new Environment($this->createMock(LoaderInterface::class)); foreach ($this->jsSpecialChars as $key => $value) { - $this->assertEquals($value, twig_escape_filter($twig, $key, 'js'), 'Failed to escape: '.$key); + $this->assertEquals($value, EscaperExtension::escape($twig, $key, 'js'), 'Failed to escape: '.$key); } } @@ -189,7 +189,7 @@ class Twig_Tests_Extension_EscaperTest extends TestCase try { mb_internal_encoding('ISO-8859-1'); foreach ($this->jsSpecialChars as $key => $value) { - $this->assertEquals($value, twig_escape_filter($twig, $key, 'js'), 'Failed to escape: '.$key); + $this->assertEquals($value, EscaperExtension::escape($twig, $key, 'js'), 'Failed to escape: '.$key); } } finally { if (false !== $previousInternalEncoding) { @@ -201,40 +201,40 @@ class Twig_Tests_Extension_EscaperTest extends TestCase public function testJavascriptEscapingReturnsStringIfZeroLength() { $twig = new Environment($this->createMock(LoaderInterface::class)); - $this->assertEquals('', twig_escape_filter($twig, '', 'js')); + $this->assertEquals('', EscaperExtension::escape($twig, '', 'js')); } public function testJavascriptEscapingReturnsStringIfContainsOnlyDigits() { $twig = new Environment($this->createMock(LoaderInterface::class)); - $this->assertEquals('123', twig_escape_filter($twig, '123', 'js')); + $this->assertEquals('123', EscaperExtension::escape($twig, '123', 'js')); } public function testCssEscapingConvertsSpecialChars() { $twig = new Environment($this->createMock(LoaderInterface::class)); foreach ($this->cssSpecialChars as $key => $value) { - $this->assertEquals($value, twig_escape_filter($twig, $key, 'css'), 'Failed to escape: '.$key); + $this->assertEquals($value, EscaperExtension::escape($twig, $key, 'css'), 'Failed to escape: '.$key); } } public function testCssEscapingReturnsStringIfZeroLength() { $twig = new Environment($this->createMock(LoaderInterface::class)); - $this->assertEquals('', twig_escape_filter($twig, '', 'css')); + $this->assertEquals('', EscaperExtension::escape($twig, '', 'css')); } public function testCssEscapingReturnsStringIfContainsOnlyDigits() { $twig = new Environment($this->createMock(LoaderInterface::class)); - $this->assertEquals('123', twig_escape_filter($twig, '123', 'css')); + $this->assertEquals('123', EscaperExtension::escape($twig, '123', 'css')); } public function testUrlEscapingConvertsSpecialChars() { $twig = new Environment($this->createMock(LoaderInterface::class)); foreach ($this->urlSpecialChars as $key => $value) { - $this->assertEquals($value, twig_escape_filter($twig, $key, 'url'), 'Failed to escape: '.$key); + $this->assertEquals($value, EscaperExtension::escape($twig, $key, 'url'), 'Failed to escape: '.$key); } } @@ -296,15 +296,15 @@ class Twig_Tests_Extension_EscaperTest extends TestCase || $chr >= 0x41 && $chr <= 0x5A || $chr >= 0x61 && $chr <= 0x7A) { $literal = $this->codepointToUtf8($chr); - $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'js')); + $this->assertEquals($literal, EscaperExtension::escape($twig, $literal, 'js')); } else { $literal = $this->codepointToUtf8($chr); if (\in_array($literal, $immune)) { - $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'js')); + $this->assertEquals($literal, EscaperExtension::escape($twig, $literal, 'js')); } else { $this->assertNotEquals( $literal, - twig_escape_filter($twig, $literal, 'js'), + EscaperExtension::escape($twig, $literal, 'js'), "$literal should be escaped!"); } } @@ -320,15 +320,15 @@ class Twig_Tests_Extension_EscaperTest extends TestCase || $chr >= 0x41 && $chr <= 0x5A || $chr >= 0x61 && $chr <= 0x7A) { $literal = $this->codepointToUtf8($chr); - $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'html_attr')); + $this->assertEquals($literal, EscaperExtension::escape($twig, $literal, 'html_attr')); } else { $literal = $this->codepointToUtf8($chr); if (\in_array($literal, $immune)) { - $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'html_attr')); + $this->assertEquals($literal, EscaperExtension::escape($twig, $literal, 'html_attr')); } else { $this->assertNotEquals( $literal, - twig_escape_filter($twig, $literal, 'html_attr'), + EscaperExtension::escape($twig, $literal, 'html_attr'), "$literal should be escaped!"); } } @@ -344,12 +344,12 @@ class Twig_Tests_Extension_EscaperTest extends TestCase || $chr >= 0x41 && $chr <= 0x5A || $chr >= 0x61 && $chr <= 0x7A) { $literal = $this->codepointToUtf8($chr); - $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'css')); + $this->assertEquals($literal, EscaperExtension::escape($twig, $literal, 'css')); } else { $literal = $this->codepointToUtf8($chr); $this->assertNotEquals( $literal, - twig_escape_filter($twig, $literal, 'css'), + EscaperExtension::escape($twig, $literal, 'css'), "$literal should be escaped!"); } } @@ -359,7 +359,7 @@ class Twig_Tests_Extension_EscaperTest extends TestCase { $this->expectException(RuntimeError::class); - twig_escape_filter(new Environment($this->createMock(LoaderInterface::class)), 'foo', 'bar'); + EscaperExtension::escape(new Environment($this->createMock(LoaderInterface::class)), 'foo', 'bar'); } /** @@ -370,7 +370,7 @@ class Twig_Tests_Extension_EscaperTest extends TestCase $twig = new Environment($this->createMock(LoaderInterface::class)); $twig->getExtension(EscaperExtension::class)->setEscaper('foo', 'Twig\Tests\foo_escaper_for_test'); - $this->assertSame($expected, twig_escape_filter($twig, $string, $strategy)); + $this->assertSame($expected, EscaperExtension::escape($twig, $string, $strategy)); } public function provideCustomEscaperCases() @@ -389,8 +389,8 @@ class Twig_Tests_Extension_EscaperTest extends TestCase $env2 = new Environment($this->createMock(LoaderInterface::class)); $env2->getExtension(EscaperExtension::class)->setEscaper('foo', 'Twig\Tests\foo_escaper_for_test1'); - $this->assertSame('fooUTF-8', twig_escape_filter($env1, 'foo', 'foo')); - $this->assertSame('fooUTF-81', twig_escape_filter($env2, 'foo', 'foo')); + $this->assertSame('fooUTF-8', EscaperExtension::escape($env1, 'foo', 'foo')); + $this->assertSame('fooUTF-81', EscaperExtension::escape($env2, 'foo', 'foo')); } /** @@ -401,8 +401,8 @@ class Twig_Tests_Extension_EscaperTest extends TestCase $obj = new Extension_TestClass(); $twig = new Environment($this->createMock(LoaderInterface::class)); $twig->getExtension('\Twig\Extension\EscaperExtension')->setSafeClasses($safeClasses); - $this->assertSame($escapedHtml, twig_escape_filter($twig, $obj, 'html', null, true)); - $this->assertSame($escapedJs, twig_escape_filter($twig, $obj, 'js', null, true)); + $this->assertSame($escapedHtml, EscaperExtension::escape($twig, $obj, 'html', null, true)); + $this->assertSame($escapedJs, EscaperExtension::escape($twig, $obj, 'js', null, true)); } public function provideObjectsForEscaping() diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index aa24d5fbe..8dd5e3bf7 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -13,6 +13,7 @@ namespace Twig\Tests; use Twig\Extension\AbstractExtension; use Twig\Extension\DebugExtension; +use Twig\Extension\EscaperExtension; use Twig\Extension\SandboxExtension; use Twig\Extension\StringLoaderExtension; use Twig\Node\Expression\ConstantExpression; @@ -215,7 +216,7 @@ class TwigTestExtension extends AbstractExtension */ public function escape_and_nl2br($env, $value, $sep = '
') { - return $this->nl2br(twig_escape_filter($env, $value, 'html'), $sep); + return $this->nl2br(EscaperExtension::escape($env, $value, 'html'), $sep); } /**