最近在做的一个Web项目时遇到了很恼人的问题,就是模板的结合点(特别是头部)会出现一个定宽的白条。我在用Opera的“元素审查”查看页面的时候,发现实际解析的页面已经完全崩坏了,标签及其内容直接丢失,只剩下一个,而原模板代码里的部分全部都被挪到了里。因为纠结于此现象的产生原因很长时间没有结果,而且莫名其妙的标签被吃掉,所以我称此现象为崩裂!!
在后来使用IE的开发者工具以及Chrome的“审查元素”后发现,那个白条的部分被解析为一个“文本”,而从工具上显示的情况来看,这个文本是实实在在的空文本,多次检查模板文件未果后,尝试去网络上搜索相关的情况,但也因没有找到合适的搜索关键词而毫无进展……后来排查PHP代码,由于我使用的是CI框架(CodeIgniter [1]),所以最后问题就锁定在了一个Model类上,只要加载了这个类,就会导致崩裂,可是反复检查代码N遍,的的确确没有错啊,甚至于都翻到了CI的Model核心文件,依然无果……万念俱灰……十分绝望,于是罢工N天 ’ 3 ` 。但是不能永远这样下去吗,于是最后死马当活马医!我再重新打一份这个文件!看看它还能再出啥问题!
结果……就真的没问题了……

尼玛坑爹那!
在此问题解决后我只是长舒了一口气,并没有刨根问题。直到昨天检索资料偶然间看到一个帖子华丽丽的提到了和我遇到的一模一样的问题!看着楼下高手的回复,我终于明白了近日纠缠我的恶魔究竟是什么。
BOM
……
……
……
这你妹的究竟是个神马东西!?
在百度百科[2]里找到了下面这种解释:

BOM
如果您在修改任何PHP文件後发生:
* 不能登入或者不能登出;
* 页顶出现一条空白;
* 页顶出现错误警告;
* 其它不正常的情况。
则多半是编辑器的问题。
本程序采用UTF-8编码。现在几乎所有的文本编辑软件都可以显示并编辑UTF-8编码的文件。但是很遗憾,其中很多软件的表现并不理想。
类似WINDOWS自带的记事本等软件,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即BOM)。它是一串隐藏的字符,用于让记事本等编辑器识别这个文件是否以UTF-8编码。对于一般的文件,这样并不会产生什么麻烦。但对于 PHP来说,BOM是个大麻烦。
PHP并不会忽略BOM,所以在读取、包含或者引用这些文件时,会把BOM作为该文件开头正文的一部分。根据嵌入式语言的特点,这串字符将被直接执行(显示)出来。由此造成即使页面的 top padding 设置为0,也无法让整个网页紧贴浏览器顶部,因为在html一开头有这3个字符呢!

的确,为了保持项目的整洁,我所有的文件都采用了UTF-8的编码方式编写,百科中最开始所描述的1、2、4条异常我都出现了。毫无疑问,崩裂的元凶就是BOM。
这里我们只能知道崩裂产生的原因就是因为保存UTF-8文件的时候会被在文件头附加三个字节的信息,但是这个信息究竟是有什么意义呢?
接下来感谢Wikipedia[3]提供的解说:

字节顺序标记(英语:byte-order mark,BOM)是位于码点U+FEFF的统一码字符的名称。当以UTF-16或UTF-32来将UCS/统一码字符所组成的字串编码时,这个字符被用来标示其字节序。它常被用来当做标示文件是以UTF-8、UTF-16或UTF-32编码的记号。

从这段定义性质的描述来看,这个BOM是为了确定UTF-16以及UTF-32等编码文件的字序的,那为啥我存成UTF-8也会中招呢?
词条中给出了如下解释:

UTF-8则没有字节顺序的议题。UTF-8编码过的字节顺序标记则被用来标示它是UTF-8的文件。它只用来标示一个UTF-8的档案,而不用来说明字节顺序。许多视窗程式(包含记事本)会添加字节顺序标记到UTF-8档案。

在PHP中,如果没有启用输出缓冲(output buffering),它会使得页面内容开始被送往浏览器(即:用户标头档已被送出),这使PHP脚本无法指定用户标头档(HTTP Header)。

也就是输UTF-8中的BOM并没有实际的功能上的意义,它仅仅作为UTF-8编码文件的一个说明性的标记!然而这种标记因为会引发各种各样的问题,所以在类Unix系统中不提倡添加。至此,真相大白。
最后我特意做了个实验,来看看这个BOM的真身。(我使用UE编辑器,在存储的时候可以选择UTF-8,和UTF-8无BOM的存储选项):
test1.php


test2.php

从图中可以看到,两个文件从编辑器上看,没有任何的区别,但是我们切换到HEX模式来看,玄机立现!
test1.php

test2.php

很明显的,第一个就是带BOM的UTF-8文件,那三个邪恶的字符也被揪了出来。
其实在一开始排查的时候我的确想到了是否文件内存在着什么不为人知的字符导致出现如此诡异的现象,也去看了下HEX模式的文件,但是由于我所有的文件都是按照带BOM方式存储的……所以并没有看出差别|||
就当是趁机又多科普了一些知识吧!

—————————————-
[1] CodeIgniter
[2] 百度百科 – BOM
[3] Wikipedia -字节顺序标记

说点什么

您将是第一位评论人!

  Subscribe  
提醒