php几个问题的记录

  最近在一个项目中使用了php,主要用它来生成json,遇到了一些问题,特此记录下来。

1.

要想使用php的curl功能,需要进行一些配置工作,方法如下(我使用的是AppServ):

使用phpinfo()函数查看php.ini配置文件的存放路径。一般是在C盘的windows下。

找到extension=php_curl.dll,将注释去掉,开启这个扩展。

在apche的httpd.conf中加入如下配置(装载curl需要的2个dll链接库),路径替换为相应的路径:

LoadFile "D:/AppServ/php5/ssleay32.dll"
LoadFile "D:/AppServ/php5/libeay32.dll"

在控制面板->服务那里重启下apache即可使用curl了。

如下面一段简单的代码,可以拿去到百度的首页。

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'http://www.baidu.com');

curl_exec($ch);

curl_close($ch);



2.

补充加更正:重读了下阮一峰老师的文章,补充下。

Unicode编码和Utf8编码不是一回事,Unicode是字符集,只是规定字符的二进制代码,而没有规定这个二进制代码如何存储;Utf8是Unicode的一种实现,采用变长编码(使用1~4个字节来存储一个符号)。如:严的Unicode码是4E25,而Utf8编码是E4B8A5。

具体到保存成文件,如果我们以utf-8格式保存“严”,则实际存储的数据是:EF BB BF E4 B8 A5,前3个EF BB BF表明是utf-8文件(带BOM);以utf-8无BOM格式保存,则实际存储数据是:E4 B8 A5。

问题是,怎么utf-8保存的是E4 B8 A5,json_encode后传回来的,我们会看到\u4E25这种形式?我个人的理解是js内码是Unicode(或者可以说Utf16?)的,于是会就将E4 B8 A5“转换理解”成\u4E25;同样,带BOM的也一样,EF BB BF理解成\uFEFF。另外一种理解是E4 B8 A5只是计算机内部的表示方法,显示给人看时,还是使用Unicode码比较直观?

php的json_encode函数假定给定的数据都是utf-8编码的,或者换句话,它会把传给它的数据中的字符串全部用utf-8编码,而不做其他特殊处理。这样,生成的json字符串是utf-8编码的。这种处理方式,在json数据中含有中文时,可能会发生问题。

如果,我们是直接在php脚本里生成json的话,不会发生什么大问题,因为,php的字符串也是utf-8编码的,调用json_encode时,就会将相应的中文转为utf-8了。如下面简单的代码:

$data = array("title" => "博客园");
echo json_encode($data);

请求过来的数据会是:

通过查unicode码表(http://www.chi2ko.com/tool/CJK.htm),我们知道,\u535a\u5ba2\u56ed正是“博客园”三个汉字。

如果,我们是通过读取文件中的数据来生成json,而文件的编码又不是utf-8时,就会发生问题了。如下面代码:

$data = array();
$data["title"] = file_get_contents("data.txt");
echo json_encode($data);

其中,data.txt文件里面只有一行简单的数据“博客园”,并以记事本默认的ANSI格式保存(简体中文系统下,代表gb2312)。

此时,请求过来的数据却没有出现我们的中文:

这个真是奇怪的事,个人觉得可能是因为json_encode不知道如何用utf-8的编码方式编码gb2312的字符串(或者说,可能这个函数内部判断,字符串不是utf-8编码的话,一律返回空字符串),就出现这个问题了。
如果我们把data.txt的编码换成utf-8格式时,请求过来的数据就变成了:

这个时候,奇怪的事又来了,后面那3个\uxxxx的是“博客园”,那前面的那个\ufeff是什么东西?通过阅读阮一峰老师的这篇文章(http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html),了解了这个问题。

文中提到:Unicode码可以采用UCS-2格式直接存储。以汉字”博“为例,Unicode码是535a,需要用两个字节存储,一个字节是53,另一个字节是5a。存储的时候,53在前,5a在后,就是Big endian(大头)方式;5a在前,53在后,就是Little endian(小头)方式。但是,计算机如何知道采用的是哪种方式?于是,就约定说在文件的开头加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。

这里的\ufeff实际就是“零宽带非换行空格”,而且我们还知道,这个文件采用的是大头方式。

我们现在可以使用js把请求过来的数据在页面中显示了,利用jquery的话,代码大概是这个样子:

$(document).ready(function() {
var container = $('#container');

$.getJSON('data.php', function(resp) {
container.html(resp['title']);
}
});

显示的时候,\ufeff不是可见字符,所有没有问题。但是,如果,你的数据中包含数字,而且你希望对数字进行处理的时候(比如:parseInt,parseFloat后继续后面的处理),问题就来了。

我们把data.txt改成1,然后请求数据的php和处理的js分别改成:

$data = array();
$data["index"] = file_get_contents("data.txt");
echo json_encode($data);
$(document).ready(function() {
var container = $('#container'),
data = ['博客园', '好地方'];

$.getJSON('data.php', function(resp) {
var idx = parseInt(resp['index'], 10);
container.html(data[idx]);
});
});

但是,其实,我们什么都看不到,因为,在parseInt(resp['index'], 10)的时候,返回的其实是NaN。这是因为请求过来的数据其实是这样的:

对\ufeff1执行parseInt显然会是NaN。那这该如何破?我的解决方法是把文件存成无BOM的utf-8,想来,那个BOM应该就是“零宽度非换行空格”了。将data.txt存成无BOM的utf-8后,请求过来的数据将会是:

此时,再parseInt将会得到正确的结果了。

总结:使用php生成json时,请格外小心,如果是直接用php程序生成,可以大大放心;如果是从文件中读出内容再生成,就需要注意文件的编码(最好都统一转成无BOM的utf-8格式);如果是从数据库中来,数据库编码也最好设成utf-8。关于如何转换编码,还需要进一步研究。

3.

有时,我们需要传递的数据量会比较大,如果网络不很好的话,可能需要下载很久。这时,可以在服务端使用gzip来压缩内容,然后进行传送,浏览器端接收到gzip过的数据,然后再进行解压处理。在php里,可以这样使用:

ini_set("memory_limit", "64M"); //多点内存

Ob_Start("ob_gzhandler"); //开启gzip

$rst = array();
$rst["data"] = file_get_contents("data.txt"); //很大的数据量
echo json_encode($rst); //echo到一个缓冲区,然后将缓冲区里的内容gzip

Ob_End_Flush(); //将缓冲区内gzip过的内容传送

此时,response header了就会加多一个Content-Encoding:gzip的响应头,这个就是告诉浏览器,这段内容是gzip过的,你需要先进行解压。使用gzip压缩,是个很好的提速方法,试了下,3M的数据能压到差不多16K。

补充:看了请求响应头,还发现了一个Transfer-Encoding:chunked的response header。这个东西的作用是什么呢?一般数据量比较小的时候,服务器会先缓存好所有数据,然后添加Content-Length的response header,把数据全部发给客户端。如果需要一边产生数据一边传送给客户端,就需要使用Transfer-Encoding:chunked代替Content-Length了。那没有Content-Length,怎么知道数据的长度呢?约定是大概这样的:

http头
\r\n
\r\n      --连续的两个\r\n后是http body
数据长度
\r\n
实际数据(大小就是上面指定的长度)
\r\n    --每段数据结束后,以\r\n标识

第二段数据长度
\r\n
第二段实际数据
\r\n

………… (反复通过这样的方式表示每次传输的数据长度及数据)

0      --数据结束部分用0表示,然后是连续的两个\r\n
\r\n
\r\n

posted on 2011-12-16 16:45  KohPoll  阅读(1000)  评论(0编辑  收藏  举报