【问题标题】:Design decision: Matching cyrillic chars in JSON with PHP设计决策:将 JSON 中的西里尔字符与 PHP 匹配
【发布时间】:2015-04-18 20:50:57
【问题描述】:

我正在为 CMS 开发一个插件,但遇到了一个意想不到的问题:因为该插件支持多语言,所以输入可以是任何 unicode 字符集。该插件以 json 格式保存数据,并包含属性为valuelookup 的对象。对于value,一切都很好,但是PHP 使用lookup 属性来检索这些实体,并在某些点通过正则表达式(内容过滤器)。 问题是:

  1. 对于非拉丁字符(例如Экспорт),正则表达式中的\w(字字符)不匹配。 有没有办法将西里尔字符识别为单词字符?还有其他隐藏的问题吗?
  2. 数据格式为 JSON,非拉丁字符转换为 JS unicode,例如:\u042D\u043A\u0441\u043F\u043E\u0440\u0442这样做是否安全(服务器限制等)

我遇到的大“设计”问题源于前两个问题:

我是否应该允许使用非拉丁字母语言的用户将自己的字符用于 lookup 属性,或者我应该强制他们使用传统的“单词”字符,即 a、b、c 等 + 下划线(因此另一种语言的字母表)?我欢迎技术建议来指导这个决定(不是用户体验)。

【问题讨论】:

  • 这不是一个问题。我可以回答第一部分,但不能回答第二部分。你会接受谁的答案?
  • @stribizhev 任何找到的拼图部分都将获得奖励。你会建议我改写这个问题并将这个问题分成 2/3 个问题吗?

标签: php regex json character-encoding http-headers


【解决方案1】:

第一个问题

对于非拉丁字符(例如Экспорт),正则表达式中的\w(字字符)不匹配任何内容。有没有办法将西里尔字符识别为单词字符?还有其他隐藏的陷阱吗?

您只需要打开u 标志:

preg_match("#^\w+$#u", $str);

Demo.

PHP docs 在这里具有误导性:

你 (PCRE_UTF8)
此修饰符打开与 Perl 不兼容的 PCRE 的附加功能。模式和主题字符串被视为 UTF-8。此修饰符在 Unix 上的 PHP 4.1.0 或更高版本以及 win32 上的 PHP 4.2.3 中可用。自 PHP 4.3.5 起检查模式和主题的 UTF-8 有效性。无效的主题将导致 preg_* 函数不匹配;无效的模式将触发 E_WARNING 级别的错误。自 PHP 5.3.4 (resp. PCRE 7.3 2007-08-28) 起,五个和六个八位字节的 UTF-8 序列被视为无效;以前那些被认为是有效的 UTF-8。

我说这是误导,因为从上面的 ideone 测试中,它不仅启用了 PCRE_UTF8,而且还启用了 PCRE_UCP(Unicode 字符属性),这是您想要的行为。

以下是 PCRE 文档的说法:

PCRE_UTF8
此选项使 PCRE 将模式和主题都视为 UTF-8 字符串而不是单字节字符串。但是,仅当 PCRE 构建为包含 UTF 支持时才可用。如果不是,则使用此选项会引发错误。 pcreunicode 页面中提供了此选项如何更改 PCRE 行为的详细信息。

PCRE_UCP
此选项更改 PCRE 处理 \B\b\D\d\S\s\W\w 和一些 POSIX 字符类的方式。默认情况下,仅识别 ASCII 字符,但如果设置了PCRE_UCP,则使用 Unicode 属性代替对字符进行分类。更多详细信息在 pcrepattern 页面中有关通用字符类型的部分中给出。如果您设置PCRE_UCP,匹配它影响的项目之一需要更长的时间。仅当 PCRE 已使用 Unicode 属性支持编译时,该选项才可用。

如果你想让它一目了然PCRE_UCP 标志将被设置,你可以在开始时将它插入到模式本身中,就像这样:

preg_match("#(*UCP)^\w+$#u", $str);

另一个可能出现在模式开头的特殊序列是(*UCP)。这与设置PCRE_UCP 选项的效果相同:它使\d\w 等序列使用Unicode 属性来确定字符类型,而不是通过查找表仅识别代码小于128 的字符。

第二个问题

数据格式为 JSON,非拉丁字符转换为 JS unicode,例如:\u042D\u043A\u0441\u043F\u043E\u0440\u0442。不这样做安全吗? (服务器限制等)

只要您的 Content-Type 标头定义了正确的编码,就不要这样做是安全的。

所以你可能想使用类似的东西:

header('Content-Type: application/json; charset=utf-8');

并确保您确实以 UTF8 发送它。

但是,将这些字符编码为转义序列可以使整个 ASCII 兼容,因此您基本上可以通过这种方式完全消除问题。

设计题

我是否应该允许使用非拉丁字母语言的用户将自己的字符用于 lookup 属性,或者我应该强制他们使用传统的“单词”字符,即 a、b、c 等 + 下划线(因此另一种语言的字母表)?我欢迎技术 建议来指导这个决定(不是 UX 建议)。

从技术上讲,只要您的整个堆栈都支持 Unicode(浏览器、PHP、数据库等),我认为这种方法没有问题。只需确保对其进行良好测试并在您的数据库中使用支持 Unicode 的列类型即可。

请注意,PHP 是一种糟糕的字符串支持语言,因此您必须确保使用正确的函数(避免使用不支持 Unicode 的函数,如 strlen 等,除非您真的想要字节数)。

确保一切都按预期运行可能需要做更多的工作,但如果这是您想要支持的东西,那没有问题。

【讨论】:

  • 感谢vm的严谨回答! 只要你的整个堆栈都支持 Unicode => 这就是问题所在:这个插件的一些潜在用户可能在(旧的)共享/免费主机上,PCRE 没有使用 unicode 支持编译,这将添加插件工作的额外要求。我无法控制最终输出的 Content-type 标头,也无法控制对其进行严格测试的资源。
  • 所以根据您的指导,我将选择以下内容:1) 因为正则表达式是括号括起来的,所以我可以使用简单的. 而不是\w 来检测,2) 我会允许用户在lookup 属性中使用这些字符,但为了兼容性保持它们转换为ASCII。唯一的缺点是这些用户只能在将原始 JSON 文件通过 unicode 转换器后才能读取它,但我可以忍受。将来我可能会选择支持 PCRE 的第二个版本,但可能性不大。再次感谢!
猜你喜欢
  • 2010-12-15
  • 2011-12-17
  • 2010-09-29
  • 2017-10-05
  • 2016-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多