建立一个小网站时候的问题与(部分)解决

舍友(下一届辅导员)委托做一个新生在线登记、统计的东西,结合智能手机普及的情况,建议是用网页的形式,微信扫一扫嘛。我选了阿里云的服务器,租用一个月(因为只需要一个月),其环境只有 Linux(PHP+MySQL)。昨天完成了一部分功能,放到阿里云上。现在记录几个坑。

诡异的 FTP 连接

我之前申请过一个 Windows Server 版的服务器,FTP 传输的时候选择的是被动模式(passive mode),其他的默认了。所以这次也选用了被动模式,结果在 PhpStorm 里直接 deploy 大多数时间连接超时,即使连接上去,有幸执行了 LIST,也无法传输文件。后来改动设置,取消被动,设置/取消兼容(compatible mode,PhpStorm 的设置),问题依旧。于是找了 FileZilla,结果非常顺利地就连接、传输了。(只不过由于 phpMyAdmin 的文件较多,即使限制并发数为1阿里云那边也经常报错说该地址使用的连接数太多。)

服务器上的 PHP 版本不够高

本来一般的做法应该是 SSH 过去,远程命令行操作 MySQL 的。结果 SSH 连接不上,我之前也没配置过 Linux 版的阿里云服务器。干脆,FTP 放了个 phpMyAdmin 过去。我当然是选择最新版(4.4.13.1)啦。但是在配置好,打开的一刹那,显示的居然是:

PHP 5.3+ is required.

你是在逗我吗……只好找了一个支持 PHP 5.2 的4.0.10.10版本。

不统一的字符编码

有一个页面是 JavaScript 发起 AJAX 请求,一个 PHP 文件处理数据并返回 JSON。但是在测试的时候,“姓名”信息根本就显示不出来。设了几个 console.log(),看了一下返回值,该字段居然是 null。但是奇怪的是,其他的字段都正常;表头也是 PHP 返回的,正确地编码了。

{"newest":[["\u5b66\u53f7","\u59d3\u540d","\u73ed\u7ea7","\u767b\u8bb0\u65f6\u95f4"],[{"sno":"15071001","sname":null,"classno":"150712","submittime":"2015-08-17 21:26:13"}]],"regcount":{"new":"1","rec":"2"}}

我想了想,名字字段和其他东西有什么不同呢?

  1. 它是从数据库返回的。
  2. 含有中文(超出 ASCII 字符集的部分)。

从第2条大致就可以确定是一个编码的问题了。但是是什么不对呢?在我本机的测试上,是能正常工作的。我看了一下我建表的语句(注意用了 MySQL 的方言):

CREATE TABLE t (x INTEGER PRIMARY KEY) CHARACTER SET = 'GBK';

为了让中文统计符合通常的“1中文占用2字节”思想,我选用了 GBK 字符集。PHP 的字符串本质上是字节数组,所以可以确定从 MySQL 读取的时候编码是 GBK,就那么直接放过来了。

再看第1条。我的其他页面(直接写的 PHP 文件,字符串硬编码)显示正常。看看保存用的字符集,UTF-8 without BOM。由于表头也是 PHP 里写死的,然后直接去 json_encode() 了,这些能正常工作,所以大致就可以确定是 json_encode() 需要 UTF-8 而某些字符串不是的缘故了。

立即写了一个hack,iconv() 了一下,果然解决了问题。

{"newest":[["\u5b66\u53f7","\u59d3\u540d","\u73ed\u7ea7","\u767b\u8bb0\u65f6\u95f4"],[{"sno":"15071001","sname":"\u5f20\u4e09(\u540d\u5b57\u4fee\u6539\u540e)","classno":"150712","submittime":"2015-08-17 21:26:13"}]],"regcount":{"new":"1","rec":"2"}}

既然知道这么麻烦,每次都要转换编码,为什么不将表的编码改为 UTF-8 呢?

于是上了:

CREATE TABLE t (x INTEGER PRIMARY KEY) CHARACTER SET = 'UTF-8';

啊,报错了。略略嘲笑一下阿里云,居然不给我用 UTF-8 编码的表。


后来我查了一下,json_encode() 的文档中写明了,其函数定义如下:

string json_encode ( mixed $value [, int $options = 0 [, int $depth = 512 ]] )

其中 value 参数的说明有一条:

All string data must be UTF-8 encoded.


MySQL 建立 UTF-8 的表应该这么写:

CREATE TABLE t (x INTEGER PRIMARY KEY) CHARACTER SET = 'UTF8';

对,就是少一个连字符。


编码的事情还有后续。我更改了建表的语句,取消了那个hack,但是显示又不正常了。

在纳闷的时候,找到了这篇文章。于是我知道了,原来 MySQL 有多个部分的编码设置。好,我就看我这里的设置是什么样的。

运行 show variables;,结果的编码部分如下:

character_set_client      utf8
character_set_connection  utf8
character_set_database    gbk
character_set_filesystem  binary
character_set_results     utf8
character_set_server      gbk
character_set_system      utf8
character_sets_dir        /usr/local/mysql/share/mysql/charsets/
collation_connection      utf8_general_ci
collation_database        gbk_chinese_ci
collation_server          gbk_chinese_ci

看起来很正常,阿里云拒绝我用 SET character_set_database utf8; 这类语句修改变量。但是在关闭了那个hack的时候,显示又不对了,JSON 编码失败。

没办法,暂时先用着这个hack吧。将问题记录在这里。


后来发现,PHP 版本是可以从主机控制台调的。不过我没有去要那个密码(因为不怎么需要),5.2也可以用。

分享到 评论