舍友(下一届辅导员)委托做一个新生在线登记、统计的东西,结合智能手机普及的情况,建议是用网页的形式,微信扫一扫嘛。我选了阿里云的服务器,租用一个月(因为只需要一个月),其环境只有 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"}}
我想了想,名字字段和其他东西有什么不同呢?
- 它是从数据库返回的。
- 含有中文(超出 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也可以用。