早在 2004 年,Tim Bray, Nick Bradbury 和 Mark Pilgrim 这几位大牛就有过一场著名的口水战,围绕着客户端是否应该接受不 valid 的 XML (XHTML) 而展开,这场口水战以 Mark Pilgrim 的一篇堪称经典的 Thought Experiment 而告结束。Mark 举的一个例子就是页面本来是完全 valid 的,但别人发来了一个 trackback,其中包含了非法的字符,导致整个页面都无法通过校验。
时至今日,一年半过去了,乱码的 trackback 仍然随处可见,我们的 blog 工具版本号一升再升,难道就是解决不了这个问题?
### 为什么要解决这个问题?
有人会问,为什么要解决这种问题呢?人人都用 UTF-8 不就好了么?没错,在一个理想的世界里,UTF-8 解决了一切问题。问题在于,在一个理想的世界里,M$ 不是坏蛋,IE 还支持 CSS 3 呢!所以我们不能依靠这种白日梦。踏实一点来看,我们现在生活的世界情况是这样的:Apache 占据了大部分的市场,PHP 和 Perl 是两种主要的 Web 脚本语言——尤其对 Blog 而言,MySQL 是大部分开源 Web 程序使用的数据库,可是主机服务上还往往不如人意,大部分的用户不能自如的指定自己 Blog 使用的字符编码,转换 MySQL 存储数据的编码也不是天天没事就能转来转去玩儿的。
这个环境,说明我们应该对他人的页面使用的编码宽容一些。
### 这个问题理应如何解决
Trackback 不是一个标准,更没有标准化组织进行维护,它只是 MovableType 中发明的一种格式,最权威的说明,就是这篇 TrackBack Technical Specification。而这个规范中对编码唯一的说明是:
The client SHOULD include the character encoding of the content being sent (title, excerpt, and weblog name) in the charset attribute of the Content-Type header.
例如:
POST http://www.example.com/trackback/5
Content-Type: application/x-www-form-urlencoded; charset=utf-8
但接收这个 trackback 的那一方如何获取这个 charset 的值呢?如果在 PHP 中,唯一的方法是用 `getallheaders()` 函数,但这是在“PHP 作为 Apache 模块安装时才可使用”——事实上许多主机提供商是以 CGI 方式安装 PHP 的。
所以此路不通,我们应该对 Trackback 的协议进行扩展,在 POST 的数据中添加关于字符编码的一项,WordPress 就是以这种方式来实现的,让我们先打开 wp-trackback.php,这是接收 trackback 用的:
$charset = $_POST[‘charset’];
if ($charset)
$charset = strtoupper( trim($charset) );
else
$charset = ‘ASCII, UTF-8, ISO-8859-1, JIS, EUC-JP, SJIS’;
if ( function_exists(‘mb_convert_encoding’) ) {
$title = mb_convert_encoding($title,
get_settings(‘blog_charset’), $charset);
…
上面的代码说明,WP 会从 POST 数据的 `charset` 一列中取得 trackback 发送者使用的字符编码,如果找不到就使用一套预设的编码 (从这个预设的编码我们可以肯定,90% 的可能是有个日本人给 WordPress 官方报告了 trackback 错误的问题,所以它们头痛医头,只加上了三个日文编码),然后使用 mb_convert_encoding 函数把这个编码转换为 blog 当前使用的编码。
让我们注意三点:
1. 每个 PHP 安装不一定都启用了 mbstring 模块,也就是说,不一定能进行这个转换,例如,我自己的电脑上安装的 PHP 5,默认就没有打开这个模块。
2. 当给出一列来源编码时,转换函数以一种猜测的方式来尝试转换 (我在 Planet 的 patch 中描述了这种转换的原理),但这种猜测未必是对的,例如,Big-5 的编码和 GB2312 的编码有部分是重合的,此时无论把 Big-5 列在前还是 GB2312 列在前,都无法同时正确解码这两种编码的文档。
3. 我们有理由相信这是 WP 私自作出的扩展,MT 不知道这一点 (我手头没有 MT 的代码,使用 MT 的朋友若有兴趣,请帮忙查查)。
### WP 糟糕在哪里和我们如何改进
WP 的问题是,它使用 POST 数据中的 charset 一栏来分辨编码,但它自己发送的 trackback 中,不包含这一栏 (`trackback()` 函数在 `wp-includes/functions.php` 中),我必须承认我搞不懂这是为什么。
有了上面的解释,我们很容易提出下列的改进:
1. 提出更改 Trackback 的规范,增加 charset 这一项。
2. 建议 WP 和 MT 都在发送 Trackback 时,包含这一项。
3. 建议 WP 和 MT 在发送 Trackback 时,先转换成 UTF-8 再发送。