如何在 PHP 中替换/转义 U+2028 或 U+2029 字符以阻止我的 JSONP API 中断

Posted

技术标签:

【中文标题】如何在 PHP 中替换/转义 U+2028 或 U+2029 字符以阻止我的 JSONP API 中断【英文标题】:How to replace/escape U+2028 or U+2029 characters in PHP to stop my JSONP API breaking 【发布时间】:2012-12-20 04:53:51 【问题描述】:

好的,我正在运行一个公共 JSONP API,数据由我的 php 服务器提供。我刚读了这篇文章:

JSON: The javascript subset that isn't (by Magnus Holm; May 2011)(请阅读说明)

基本上,如果我的 JSON 字符串包含 U+2028 字符(Unicode 行分隔符)或 U+2029 字符(Unicode 段落分隔符),那么这是完全有效的 JSON。但是,当使用 JSONP 时,JSON 将作为 JavaScript 执行,并且 JavaScript 中的任何字符串都不能包含文字 U+2028 或 U+2029,因为它会破坏 JavaScript。显然,只要您使用正确的 JSON 解析器,这通常不是问题,但在 JSONP 的情况下,浏览器 就是 JSON 解析器。

基本上,如果这些字符在我发送到客户端的 JSONP 数据的字符串中,这将在字符串中抛出一个行或段落中断,这会破坏 JavaScript 并停止它的执行。这是一种可能性,因为 API 正在发回一些客户端输入的数据。有人可能会在数据库中输入 U+2028 或 U+2029,因此当我将其作为 JSONP 发回时,它会破坏使用我的 API 的任何实现。

所以我的问题是,在 PHP 中如何清理/输出转义 JSON 数据以删除或转义 U+2028 和 U+2029 字符,然后再将其发送到客户端?

目前我的进程正在对一组数据执行json_encode 并将该数据发送到客户端。我应该通过遍历数组并对其进行过滤来转义数据,还是一次转义所有 JSON 编码的字符串?

另一件事是我不确定如何在 PHP 中转义 U+2028 和 U+2029 字符。我可以做一个 str_replace 吗?我不确定 str_replace 是否是多字节安全的并且有 no mb_str_replace 函数,除非我使用一些定制的函数。那么如何删除/转义那些 unicode 字符呢?

非常感谢。

【问题讨论】:

除非我没看错文章,你不能直接换成\u2028吗? \u2028u2029 是 unicode 换行符。您希望您的应用程序允许在字符串中使用这些字符吗?意思是,无论在您的服务器上接受此文本,都会主动将 CR/LF 转换为这些字符之一,因为几乎没有人使用新的换行符。我对此表示怀疑。我猜你只是想消除所有换行符,无论是 CR/LF,还是新的。在这种情况下,只需消除它们,而不是插入文字转义序列。 【参考方案1】:

您可以在 PHP 端或 JavaScript 端,或同时将 U+2028U+2029 替换为 "\u2028""\u2029",只要它至少发生一次就没有关系(它是幂等的)。

您可以只使用普通的字符串替换函数。它们不需要“多字节安全”,您可以使用任何 Unicode 编码(UTF-8、UTF-16、UTF-32 都同样适用)轻松地做到这一点。上次我检查时 PHP 没有 Unicode 转义序列,这只是 PHP 成为笑话的另一个原因,但您可以将 \x 转义与 UTF-8 一起使用...

(简而言之,没有多字节字符串替换函数的原因是因为它是多余的——它与非多字节字符串替换函数完全相同。)

// Javascript
data = data.replace("\u2028", "\\u2028").replace("\u2029", "\\u2029");

// PHP
$data = str_replace("\xe2\x80\xa8", '\\u2028', $data);
$data = str_replace("\xe2\x80\xa9", '\\u2029', $data);

或者你可以什么都不做,因为 PHP 在 json_encode() 中默认转义非 Unicode 字符:

// Safe
echo json_encode("\xe2\x80\xa9");
--> "\u2029"

// Correct JSON, but invalid Javascript...
// (Well, technically, JSON root must be array or object)
echo json_encode("\xe2\x80\xa9", JSON_UNESCAPED_UNICODE);
--> "
"

【讨论】:

很好的回复,谢谢!我也可以使用 json_encode() 离开它,因为这似乎为我完成了所有转义工作。写这个函数的人干得好! 但这假设是 UTF-8。如果您使用不同的字符编码,这不会中断吗? 但这假设是 UTF-8。如果您使用不同的字符编码,那不会中断。 @ColeJohnson:JSON 不允许使用 UTF-8 以外的 8 位编码。 @DietrichEpp。绝对允许 JSON 使用 UTF-8、UTF-16LE、UTF-16BE、UTF-32LE 或 UTF-32BE 中的任何一种。无需指定使用哪个,因为很容易区分,因为前两个字符必须是 ASCII(第一个字符将是 [,第二个字符将是 "'、空格或数字)。【参考方案2】:

值得指出的是,这不再是必要的。

默认情况下,json_encode() 编码 所有 个非 ASCII 字符(包括 U+2028 和 U+2029),并且还转义正斜杠,即使不需要转义JSON 规范。逃避它并没有什么害处,而且在某些情况下它可能更安全。因此,默认情况下,这些字符无论如何都会被转义。

JSON_UNESCAPED_UNICODE 常量输出未转义的 Unicode,可以节省字节。然而,正如斜线字符被转义是因为它在某些情况下可能是危险的,所以 U+2028 和 U+2029 也被转义,因为它们在某些情况下也是危险的。在您提出问题时并非如此:this feature has been added to PHP more recently。

(这些额外的转义可以分别用JSON_UNESCAPED_SLASHESJSON_UNESCAPED_LINE_TERMINATORS 关闭。)

【讨论】:

以上是关于如何在 PHP 中替换/转义 U+2028 或 U+2029 字符以阻止我的 JSONP API 中断的主要内容,如果未能解决你的问题,请参考以下文章

Android Studio:错误:非法字符:'\u2028'

如何在 Postgres 中搜索包含给定 unicode 字符的所有行

如何在 PHP 中使用正则表达式转换标记 (\u \l \U \L \E)?

如何在 mysql 或 php 中将 'u00e9' 转换为 utf8 字符?

如何将 unicode 字符串转换为其 unicode 转义?

关于json转义中文