diff --git a/CHANGELOG b/CHANGELOG
index 5d869ca2b..98c031bd3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,6 @@
* 1.35.4 (2018-XX-XX)
- * n/a
+ * "js" filter now produces valid JSON
* 1.35.3 (2018-03-20)
diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php
index 6e5569a28..0e35e36ed 100644
--- a/lib/Twig/Extension/Core.php
+++ b/lib/Twig/Extension/Core.php
@@ -1040,7 +1040,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
case 'js':
// escape all non-alphanumeric characters
- // into their \xHH or \uHHHH representations
+ // into their \x or \uHHHH representations
if ('UTF-8' !== $charset) {
$string = twig_convert_encoding($string, 'UTF-8', $charset);
}
@@ -1152,9 +1152,23 @@ function _twig_escape_js_callback($matches)
{
$char = $matches[0];
- // \xHH
- if (!isset($char[1])) {
- return '\\x'.strtoupper(substr('00'.bin2hex($char), -2));
+ /*
+ * A few characters have short escape sequences in JSON and JavaScript.
+ * Escape sequences supported only by JavaScript, not JSON, are ommitted.
+ * \" is also supported but omitted, because the resulting string is not HTML safe.
+ */
+ static $shortMap = array(
+ '\\' => '\\\\',
+ '/' => '\\/',
+ "\x08" => '\b',
+ "\x0C" => '\f',
+ "\x0A" => '\n',
+ "\x0D" => '\r',
+ "\x09" => '\t',
+ );
+
+ if (isset($shortMap[$char])) {
+ return $shortMap[$char];
}
// \uHHHH
diff --git a/test/Twig/Tests/EnvironmentTest.php b/test/Twig/Tests/EnvironmentTest.php
index ca9f2cf8a..dd8dac9ad 100644
--- a/test/Twig/Tests/EnvironmentTest.php
+++ b/test/Twig/Tests/EnvironmentTest.php
@@ -61,7 +61,7 @@ class Twig_Tests_EnvironmentTest extends \PHPUnit\Framework\TestCase
));
$this->assertEquals('foo<br/ > foo<br/ >', $twig->render('html', array('foo' => 'foo
')));
- $this->assertEquals('foo\x3Cbr\x2F\x20\x3E foo\x3Cbr\x2F\x20\x3E', $twig->render('js', array('bar' => 'foo
')));
+ $this->assertEquals('foo\u003Cbr\/\u0020\u003E foo\u003Cbr\/\u0020\u003E', $twig->render('js', array('bar' => 'foo
')));
}
public function escapingStrategyCallback($name)
diff --git a/test/Twig/Tests/Fixtures/autoescape/name.test b/test/Twig/Tests/Fixtures/autoescape/name.test
index 04299bed3..98e89399a 100644
--- a/test/Twig/Tests/Fixtures/autoescape/name.test
+++ b/test/Twig/Tests/Fixtures/autoescape/name.test
@@ -17,6 +17,6 @@ return array('br' => '
')
return array('autoescape' => 'name')
--EXPECT--
<br />
-\x3Cbr\x20\x2F\x3E
+\u003Cbr\u0020\/\u003E
<br />
diff --git a/test/Twig/Tests/Fixtures/filters/escape_javascript.test b/test/Twig/Tests/Fixtures/filters/escape_javascript.test
index 647147a43..8e7278119 100644
--- a/test/Twig/Tests/Fixtures/filters/escape_javascript.test
+++ b/test/Twig/Tests/Fixtures/filters/escape_javascript.test
@@ -5,4 +5,4 @@
--DATA--
return array()
--EXPECT--
-\u00E9\x20\u265C\x20\uD834\uDF06
+\u00E9\u0020\u265C\u0020\uD834\uDF06
diff --git a/test/Twig/Tests/Fixtures/filters/force_escape.test b/test/Twig/Tests/Fixtures/filters/force_escape.test
index 85a9b7172..eb9cba7cf 100644
--- a/test/Twig/Tests/Fixtures/filters/force_escape.test
+++ b/test/Twig/Tests/Fixtures/filters/force_escape.test
@@ -14,5 +14,5 @@
return array()
--EXPECT--
foo<br />
-\x20\x20\x20\x20foo\x3Cbr\x20\x2F\x3E\x0A
+\u0020\u0020\u0020\u0020foo\u003Cbr\u0020\/\u003E\n
foo
diff --git a/test/Twig/Tests/Fixtures/tags/autoescape/functions.test b/test/Twig/Tests/Fixtures/tags/autoescape/functions.test
index ce7ea789e..653c41b8e 100644
--- a/test/Twig/Tests/Fixtures/tags/autoescape/functions.test
+++ b/test/Twig/Tests/Fixtures/tags/autoescape/functions.test
@@ -80,4 +80,4 @@ unsafe_br()|escape
autoescape js
safe_br
-\x3Cbr\x20\x2F\x3E
+\u003Cbr\u0020\/\u003E
diff --git a/test/Twig/Tests/Fixtures/tags/autoescape/strategy.legacy.test b/test/Twig/Tests/Fixtures/tags/autoescape/strategy.legacy.test
index bbf1356e7..c3f8eddfa 100644
--- a/test/Twig/Tests/Fixtures/tags/autoescape/strategy.legacy.test
+++ b/test/Twig/Tests/Fixtures/tags/autoescape/strategy.legacy.test
@@ -7,5 +7,5 @@
--DATA--
return array('var' => '
"')
--EXPECT--
-\x3Cbr\x20\x2F\x3E\x22
+\u003Cbr\u0020\/\u003E\u0022
<br />"
diff --git a/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test b/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test
index e496f6081..5b69449c2 100644
--- a/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test
+++ b/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test
@@ -7,5 +7,5 @@
--DATA--
return array('var' => '
"')
--EXPECT--
-\x3Cbr\x20\x2F\x3E\x22
+\u003Cbr\u0020\/\u003E\u0022
<br />"
diff --git a/test/Twig/Tests/Fixtures/tags/autoescape/type.test b/test/Twig/Tests/Fixtures/tags/autoescape/type.test
index 4f415201d..1250f0db1 100644
--- a/test/Twig/Tests/Fixtures/tags/autoescape/type.test
+++ b/test/Twig/Tests/Fixtures/tags/autoescape/type.test
@@ -44,15 +44,15 @@ return array('msg' => "<>\n'\"")
1. autoescape 'html' |escape('js')
-
+
2. autoescape 'html' |escape('js')
-
+
3. autoescape 'js' |escape('js')
-
+
4. no escape
@@ -61,9 +61,9 @@ return array('msg' => "<>\n'\"")
5. |escape('js')|escape('html')
-
+
6. autoescape 'html' |escape('js')|escape('html')
-
+
diff --git a/test/Twig/Tests/escapingTest.php b/test/Twig/Tests/escapingTest.php
index 9b98dddcf..9c2e12038 100644
--- a/test/Twig/Tests/escapingTest.php
+++ b/test/Twig/Tests/escapingTest.php
@@ -51,13 +51,15 @@ class Twig_Test_EscapingTest extends \PHPUnit\Framework\TestCase
protected $jsSpecialChars = array(
/* HTML special chars - escape without exception to hex */
- '<' => '\\x3C',
- '>' => '\\x3E',
- '\'' => '\\x27',
- '"' => '\\x22',
- '&' => '\\x26',
+ '<' => '\\u003C',
+ '>' => '\\u003E',
+ '\'' => '\\u0027',
+ '"' => '\\u0022',
+ '&' => '\\u0026',
+ '/' => '\\/',
/* Characters beyond ASCII value 255 to unicode escape */
'Ā' => '\\u0100',
+ '😀' => '\\uD83D\\uDE00',
/* Immune chars excluded */
',' => ',',
'.' => '.',
@@ -70,12 +72,14 @@ class Twig_Test_EscapingTest extends \PHPUnit\Framework\TestCase
'0' => '0',
'9' => '9',
/* Basic control characters and null */
- "\r" => '\\x0D',
- "\n" => '\\x0A',
- "\t" => '\\x09',
- "\0" => '\\x00',
+ "\r" => '\r',
+ "\n" => '\n',
+ "\x08" => '\b',
+ "\t" => '\t',
+ "\x0C" => '\f',
+ "\0" => '\\u0000',
/* Encode spaces for quoteless attribute protection */
- ' ' => '\\x20',
+ ' ' => '\\u0020',
);
protected $urlSpecialChars = array(