PHP接收GET中文参数乱码深入研究 [PHP]

post by 朦朧中的罪惡 / 2010-6-18 10:29 Friday

相信很多PHPer都会遇到这样的问题:在utf-8的页面下面,如果直接访问带有中文参数的地址如test.php?s=测试 这样的地址输出参数的值会乱码,在搜索引擎上查询了下相关资料,都只给出了一些解决方案,但是却没有人研究导致这个问题的原因,今天特写此文来深入这个问题产生的原因:

首先我们演示这个问题,测试代码和运行结果如下。

代码:

点击查看原图

测试结果:

点击查看原图

代码中声明了响应内容的编码为utf-8,显示的内容确实乱码。

在这里请注意var_dump出变量的长度只有4 ,很显然,两个中文字的长度在utf-8编码下肯定不止4个字节

然后我们再看一下Firefox的访问这个页面url

点击查看原图

FireFox会自动将中文url编码,所以我们可以看到测试变成了%B2%E2%CA%D4,很明显,这里一个字是两个字节,是gb2313、gbk等中文编码格式,而不是utf-8编码。

如果我们把页面的编码切换为gbk,中文参数就会显示正常,参见下图

点击查看原图

这时一个有趣的问题就诞生了:像emlog的中文标签这样的参数怎么就没有乱码呢?

多方测试后,我发现了一个小小的区别:

emlog中文参数的链接是在页面上生成的,而上面我们测试则用手直接在地址栏输入的,

如果我们直接输入例如http://be-evil.org/?tag=原创 这样的链接,程序同样会提示找不到标签

测试代码如下:

点击查看原图

测试结果,正常显示:

点击查看原图

请注意上图中红框标出的url编码,这次测试两个字是由6个字节组成,而不是先前的2个字节,因此表明中文参数已经正确的成为utf-8编码。

那么,是什么导致这个问题的发生呢?

答案是浏览器默认编码 在作怪,我们都用的是中文系统,浏览器默认的编码自然也会设置为本地化,例如我自己电脑上的IE的FireFox的默认编码都是gb系列的,请参看下图:

IE的默认设置:

点击查看原图

Firefox的默认设置:

点击查看原图

正因为这个设置,让浏览器在请求用户输入的url 时会默认把url中的中文以默认的编码格式发送而不是以页面的编码格式发送,这就是为什么页面中带有中文的链接正常而我们手动输入的链接会乱码的原因。同理,如果我们把浏览器的默认编码调整为utf-8,那么输入url中的中文则会按照utf-8编码。

除了上面的之外,还有以下情况会出现这种情况:

如果gbk编码的页面生成的地址链接到utf-8的页面,gbk页面的中文是按照gbk的格式编码传送给下个页面,那么utf-8编码接收后肯定会出现乱码。

IIS的url重写模块,重写后的中文编码也是gbk,如果你的页面是utf-8编码,那么重写参数将会失效。

像这些情况,我们就需要使用php内置的转码函数来处理编码问题了:

方案1:

$str = iconv("gb2312","utf-8",$str);

方案2:

mb_convert_encoding($str, "utf-8", "gb2312");

希望本文对那些因为编码问题而抓破头的PHPer们有所帮助 :)

标签: php 原创 疑难杂症

PHP实现html标签补全 [PHP]

post by 朦朧中的罪惡 / 2010-6-13 20:28 Sunday

我们有时候需要截断html的内容来输出,但是由于截断的位置并不确定,导致阶段后的html内容中的很多标签都没有正常闭合而导致页面变形,如何解决这个问题呢?当然是使用强大的正则表达式来匹配那些没有闭合的标签了,代码如下:

图片可以点击放大

点击查看原图

用了这个函数就可以将截断的html补全了,下面是测试代码:

点击查看原图

程序源码参见附件压缩包:

参考资料:

Close HTML Tags

标签: php 算法

PHP匹配GBK全中文字正则表达式(不包括GBK标点符号) [PHP]

post by 朦朧中的罪惡 / 2010-6-12 10:03 Saturday

今天朋友让我帮他弄个正则表达式,要求如下:

编码为gbk,要求,a-z,0-9,只匹配中文字不包括中文的标点符号如# ¥ % &等

查了下资料,网上匹配GBK中文的表达式都是使用16进制码通杀中文的那种

例如 "/^[".chr(0xa1)."-".chr(0xff)."]+$/" ,依然会把 这样的的字符匹配进去

测试代码如下:

点击查看原图

测试结果:

点击查看原图

去维基百科查阅相关资料 ,GBK的编码范围如下:

点击查看原图

根据资料继续查阅GBK码表

前面提供的表达式是从A1就开始了,而A1-A9正好就是符号段,难怪会匹配到

排除符号段后就可以得出中文字符的码段为0xB0 - 0xF7,0x81 - 0xA0, 0xAA - 0xFE

按照编码标准,中文字由两个字节组成,那么我们按照标准建立正则表达式

(([\xB0-\xF7][\xA1-\xFE])|([\x81-\xA0][\x40-\xFE])|([\xAA-\xFE][\x40-\xA0])|(\w))+

测试代码如下:

点击查看原图

运行结果,成功匹配中文字符,问题解决

点击查看原图

标签: php 原创

PHP解析XML异常问题解决 [PHP]

post by 朦朧中的罪惡 / 2010-6-10 10:38 Thursday

今天在调试emlog工具箱导入rss文件时php报错

内容为

Char 0x0 out of allowed range in Entity

Google搜索相关资料无果。郁闷了半天,后来突然想到Char 0x0,觉得这个应该是rss文本中含有非法的字符而导致xml解析失败.

查询了w3.org的xml字符范围 之后

上面定义了有效的uncode字符范围:

Char       ::=       #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]

虽然0x0可以存在于utf-8文件中,但是0x0很明显不在上面规定范围之内

那么我只需要把文档里面不符合上面标准的字符过滤掉应该就能解决这个问题

php函数如下

点击查看原图

在解析xml之前使用该函数来过滤掉非法字符,就能解决xml解析异常的问题了

参考资料

Invalid XML Characters: when valid UTF8 does not mean valid XML

标签: php 原创 疑难杂症

PHP识别url重写请求 [PHP]

post by 朦朧中的罪惡 / 2010-6-9 10:28 Wednesday

现在很多PHP程序都使用了url重写技术来优化url,但是有时候我们要在PHP程序中区分默认url的请求和url重写后的请求,这该如何处理呢?

我的解决方案是在常量$_SERVER里面找答案,在不同的服务器中$_SERVER都会有一个索引用来记录重写请求访问重写之前的路径

根据我的测试,部分主流服务器的索引如下

IIS7 + Rewrite Module -> $_SERVER['HTTP_X_ORIGINAL_URL']

IIS6 + ISAPI Rewite -> $_SERVER['HTTP_X_REWRITE_URL’]

Apache2 -> $_SERVER['REQUEST_URI’] 或 $_SERVER['REDIRECT_URL']

nginx -> $_SERVER['REQUEST_URI’]

有了这个参数,我们就可以在PHP中来判断请求url来自于重写地址的还是默认格式的地址了

标签: php 原创

PHP上传带有单引号文件名的文件导致文件名被截断的bug [疑难杂症]

post by 朦朧中的罪惡 / 2010-6-7 10:00 Monday

新发现一个PHP的文件上传bug,bug描述如下:

触发条件:

PHP版本 < 5.3

PHP魔法引用开启

如果你上传一个叫做lalalala'gagaga.txt的文件,那么在服务器端接收到的时候,文件名就会从引号处被截断,名字变成gagaga.txt

$_FILES数组测试参数如下图:

点击查看原图

bug分析:

导致这个问题的原因是由于魔法引用功能转义单引号触发,

如果默认路径是c:\tmp\lalalala'gagaga.txt

那么经过魔法引用后就变成了c:\\tmp\\lalalala\'gagaga.txt

如果你使用basename函数打印这个字符串,就会得到'gagaga.txt

解决方案:

1.关闭你PHP的魔法引用功能

2.升级你的PHP到5.3版本,在5.3中这个问题已经修复

3.如果上面两个方案你都无法办到,那么下面是兼容方案

在你上传的表单里面增加一个隐藏域用于存储文件名称,选择文件后使用JavaScript取得文件名并将其存储在隐藏域中

例如:

<form name="imgform" method="post" action="upload.php" enctype="multipart/form-data">
   <input type="hidden" name="userfilename" id="userfilename">
   <input type="file" name="userfile" id="userfile" size="64" maxlength="256" onChange="javascript:top.document.imgform.userfilename.value= top.imgform.userfile.value">
</form>

然后在服务器端做相关处理

点击查看原图

这个问题在05年就有人发现了,不知道为啥PHP开发组一直没有修复这个bug

参考资料

标签: php 疑难杂症

mysql_fetch_object函数读取SELECT COUNT(*)数据的方法 [PHP]

post by 朦朧中的罪惡 / 2010-5-18 21:57 Tuesday

mysql_fetch_object 函数返回的对象形式的数据

点击查看原图

但是遇到在SQL语句中出现SELECT Count(*)和 SELECT  SUM等情况时,就不能使用上面的方法取得列的数据了,目前的解决方案是给Count(*)增加一个别名,请看下面的代码:

点击查看原图

起了别名之后,我们就可使用与别名同名的属性来访问列的数据了。

最后,这个解决方案除了mysql_fetch_object之外,还适用于mysql_fetch_assoc函数

标签: php 原创

ubuntu下PHP5.3安装xdebug后var_dump输出没有格式化的问题 [疑难杂症]

post by 朦朧中的罪惡 / 2010-5-6 10:49 Thursday

在ubuntu10.04下面配置Apache2.2 + PHP5.3的开发环境,顺便装了一个xdebug扩展方便调试代码。

但是环境配置好了之后却发现xdebug加载成功了但是var_dump输出的内容却没有使用html格式化

xdebug正常的var_dump输出应该是下面的样子

点击查看原图

但是我当前的情况却是没有格式化的代码

点击查看原图

这时想到估计是php.ini里面的某个输出的配置有问题,打印phpinfo()逐个查看配置,最后发现php配置项目

html_errors 为 Off

点击查看原图

将html_errors的值修改为On后,重启Apache,问题解决

标签: php ubuntu 疑难杂症

几个很酷的但是你不一定知道的PHP小技巧 [PHP]

post by 朦朧中的罪惡 / 2010-4-24 22:34 Saturday

今天在PHPBulider上看到了一篇文章《Six Cool PHP Tricks You May Not Know》

作者介绍了6个很有意思的小技巧,在这里翻译并记录一下

1.快速判断字符串的长度

一般我们都是使用strlen函数来判断,但是这里有一个更快的方法,下面是实现代码

点击查看原图

2.像使用一个函数那样使用echo

我一直认为,如果想连接字符串时,只能按照常规出牌。但是实际上你可以像是用函数那样使用echo,将打印的字符串使用逗号隔开(这么做速度也更快),请参见下面的代码

点击查看原图

3.尽可能的使用单引号

用单引号代替双引号,你就让PHP节省了从字符串中解析变量的时间,不仅如此,这么写还会对程序员更加友好一些,因为这样可以更加容易从你的代码中找到变量。

同样,使用字符串作为数组索引的时候要这么做,这样可以防止PHP花更多的时间研究你到底要表达什么意思.

4.变量的变量

很多情况下我们可能要访问一些动态的变量(变量名不确定),你可以很容易在PHP使用变量的变量来实现。

参看以下代码

点击查看原图

剩下的2条

5.在表单字段中使用数组

6.PHP输出缓存

估计做PHP的应该都知道,我就不在这里陈述了...原文页面在这里

标签: php

PHP计算相关两个时区的时间差 [PHP]

post by 朦朧中的罪惡 / 2010-4-19 9:03 Monday

我们在开发通用PHP程序的时候可能遇到这样的情况:

服务器配置的时区和用户的时区不一样,而不同的用户也可能子在不同时区,不同的服务器默认的时区也不会不一样,那么就会经常涉及一个不同时区时间转换的问题,而获得不同时区的时差就是一个很头疼的问题。

PHP在5.1之后提供了一个DateTimeZone来帮助我们转换时区的时间,我们可以使用这个类来处理时间转换的问题

下面看代码(点击放大):

点击查看原图

参考资料 http://cn.php.net/manual/en/function.timezone-offset-get.php

详细代码可以参见附件 timezone.php

附件下载:
timezone.zip 659字节

标签: php 原创

1 2 3 4