1,先讲下场景
用户在使用php回调url回调的时候,代码是正确的,返回给七牛的也是200状态码的json格式字符串,但是为什么七牛服务器返回这个报错信息呢
\"err_code\":200,\"error\":\"unexcepted response\"
2,问题研究
首先我们看下这个回调url返回来的到底是什么,为了看到有没有隐藏字符。
错误的返回
curl -s "http://xxx.weijuxing.cn/api.php" | xxd
0000000: efbb bf7b 2273 7563 6365 7373 223a 7472 ...{"success":tr
0000010: 7565 7d ue}
正确的响应
curl -s "http://xxx.weijuxing.cn/api.php" | xxd
0000000: 7b22 7375 6363 6573 7322 3a74 7275 657d {"success":true}
我们发现多了一个编码:efbb bf 也就是 …
3,问题剖析
归根到底是:PHP与BOM头(EF BB BF)的关系
PHP在设计之初,没有考虑到BOM头的问题,因此很容易因为BOM头引发诡异的问题,比如编码转换失败,样式错乱等等问题,而且此问题相当隐蔽,很难确定发生问题的文件(试想在没有工具的情况下从上万的工程文件中找到哪个文件带有BOM头)。
BOM头是隐藏字符,非编辑字符,就像普通空文件一样,当我们写?php的时候其实之前已经加了BOM头,如下
{BOM头}<?php
.....
当file.php被其他文件包含时,由于BOM头在php标签外,会当作输出内容输出到浏览器,然后引发问题(如果用gbk察看 会看到三个乱码的字符)
所以我们在开发过程中,得创建不包含BOM头的UTF8文件,具体见编辑器设置。所以再回调时候,用代码截取BOM头即可
解决方案:
<?php
/**
* 用法:复制以下代码至新建的php文件中,将该php文件放置项目目录,运行即可。代码来源于网络。
* chenwei 注。
*/
header('content-Type: text/html; charset=utf-8');
$auto=1;/* 设置为1标示检测BOM并去除,设置为0标示只进行BOM检测,不去除 */
$basedir='.';
$loop=true;
echo '当前查找的目录为:'.$basedir.'当前的设置是:';
echo '(1)',$loop?'检查当前目录以及当前目录的子目录':'只针对当前目录进行检测';
echo '(2)',$auto?'检测文件BOM同时去除检测到BOM文件的BOM<br />':'只检测文件BOM不执行去除BOM操作<br />';
checkdir($basedir,$loop);
function checkdir($basedir='',$loop=true){
$basedir=empty($basedir)?'.':$basedir;
if($dh=opendir($basedir)){
while (($file=readdir($dh))!==false){
if($file!='.'&&$file!='..'){
if(!is_dir($basedir.'/'.$file)){
echo '文件: '.$basedir.'/'.$file .checkBOM($basedir.'/'.$file).' <br>';
}else{
if(!$loop) continue;
$dirname=$basedir.'/'.$file;
checkdir($dirname);
}
}
}
closedir($dh);
}
}
function checkBOM($filename){
global $auto;
$contents=file_get_contents($filename);
$charset[1]=substr($contents,0,1);
$charset[2]=substr($contents,1,1);
$charset[3]=substr($contents,2,1);
if(ord($charset[1])==239&&ord($charset[2])==187&&ord($charset[3])==191){
if($auto==1){
$rest=substr($contents,3);
rewrite($filename,$rest);
return (' <font color=red>找到BOM并已自动去除</font>');
}else{
return (' <font color=red>找到BOM</font>');
}
}else{
return (' 没有找到BOM');
}
}
function rewrite($filename,$data){
$filenum=fopen($filename,'w');
flock($filenum,LOCK_EX);
fwrite($filenum,$data);
fclose($filenum);
}