这几天在调式一个ECshop的项目中遇到PHP的BOM问题,问题如下:在IE中打开网站,页面可以正常显示,但当点击其他页面后再回到首页则首页不能正常加载,需要刷新一次后才能正常加载。在谷歌和360浏览器下能正常加载,但页面顶部出现20px的空行,而在Firefox浏览器中都正常。几经查证,最后才发现原来是二次开发中编码出现了问题。PHP文件用记事本编辑保存后产生了BOM。
BOM的说明:在UCS 编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF。而FEFF在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符”ZERO WIDTH NO-BREAK SPACE”。这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little- Endian的。因此字符”ZERO WIDTH NO-BREAK SPACE”又被称作BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符”ZERO WIDTH NO-BREAK SPACE”的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。Windows就是使用BOM来标记文本文件的编码方式的。
UTF-8编码的文件中,BOM占三个字节。如果用记事本把一个文本文件另存为UTF-8编码方式的话,用UE打开这个文件,切换到十六进制编辑状态就可以看到开头的FFFE了。
很多软件都是通过BOM来识别这个文件是否是UTF-8编码的,可是,还是有很多软件不能识别BOM。如:在Firefox早期的版本里,扩展是不能有BOM的,不过Firefox 1.5以后的版本已经开始支持BOM了,PHP在设计时就没有考虑BOM的问题,也就是说他不会忽略UTF-8编码的文件开头BOM的那三个字符。
可以把代码保存成一个php文件,放在网站根目录下,需要时执行一下即可。
很多软件都是通过BOM来识别这个文件是否是UTF-8编码的,可是,还是有很多软件不能识别BOM。如:在Firefox早期的版本里,扩展是不能有BOM的,不过Firefox 1.5以后的版本已经开始支持BOM了,PHP在设计时就没有考虑BOM的问题,也就是说他不会忽略UTF-8编码的文件开头BOM的那三个字符。
写好的程序上传到服务器中在本机没问题,结果到了服务器上到处是空行,于是我们需要散掉这些Bom。当然手工删是不现实的,别告诉我您做的页面就几个。所以放出一段代码专门用于删BOM。
<?php
if (isset($_GET['dir'])){ //可以传参数指定删哪些目录
$basedir=$_GET['dir'];
}else{
$basedir = '.';
}
$auto = 1;
checkdir($basedir);
function checkdir($basedir){ //检查目录
if ($dh = opendir($basedir)) {
while (($file = readdir($dh)) !== false) {
if ($file != '.' && $file != '..'){
if (!is_dir($basedir."/".$file)) {
echo "filename: $basedir/$file ".checkBOM("$basedir/$file")." <br>";
}else{
$dirname = $basedir."/".$file;
checkdir($dirname);
}
}
}
closedir($dh);
}
}
function checkBOM ($filename) { //检查当前文件中是否存在Bom标记
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 Not Found.");
}
function rewrite ($filename, $data) {//覆盖原文件
$filenum = fopen($filename, "w");
flock($filenum, LOCK_EX);
fwrite($filenum, $data);
fclose($filenum);
}
?>
if (isset($_GET['dir'])){ //可以传参数指定删哪些目录
$basedir=$_GET['dir'];
}else{
$basedir = '.';
}
$auto = 1;
checkdir($basedir);
function checkdir($basedir){ //检查目录
if ($dh = opendir($basedir)) {
while (($file = readdir($dh)) !== false) {
if ($file != '.' && $file != '..'){
if (!is_dir($basedir."/".$file)) {
echo "filename: $basedir/$file ".checkBOM("$basedir/$file")." <br>";
}else{
$dirname = $basedir."/".$file;
checkdir($dirname);
}
}
}
closedir($dh);
}
}
function checkBOM ($filename) { //检查当前文件中是否存在Bom标记
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 Not Found.");
}
function rewrite ($filename, $data) {//覆盖原文件
$filenum = fopen($filename, "w");
flock($filenum, LOCK_EX);
fwrite($filenum, $data);
fclose($filenum);
}