【问题标题】:UTF-8 BOM signature in PHP filesPHP 文件中的 UTF-8 BOM 签名
【发布时间】:2010-04-01 04:44:58
【问题描述】:

我正在编写一些带注释的 PHP 类,但偶然发现了一个问题。我的名字(对于@author 标签)以 ș 结尾(这是一个 UTF-8 字符,......而且我知道一个奇怪的名字)。

尽管我将文件保存为 UTF-8,但一些朋友报告说他们看到这个字符完全搞砸了 (È™)。通过添加 BOM 签名可以解决此问题。但是那件事让我有点困扰,因为我对此知之甚少,除了我在 Wikipedia 上看到的以及 SO 上的其他一些类似问题。

我知道它在文件的开头添加了一些东西,据我了解,这并没有那么糟糕,但我很担心,因为我读到的唯一有问题的场景涉及 PHP 文件。而且由于我正在编写 PHP 类来共享它们,因此 100% 兼容比在 cmets 中有我的名字更重要。

但我正在尝试理解其中的含义,我是否应该放心使用它?或者是否有可能造成损害的情况?什么时候?

【问题讨论】:

  • 请注意,今天我遇到了一个问题,其中包含的 PHP 文件顶部的 <?php die('test') 在文件顶部显示 UTF-8 BOM(这些字符:) .很难弄清楚。就在那时,我发现其中一位开发人员将文件保存为 Unicode,而不是 ASCII,并且这个 PHP 无法读取 Unicoded PHP 文件。我开始了一个新的副本,复制了数据并以 ASCII 格式保存,问题就解决了。请注意,如果您遇到这种情况,某些 FTP 程序可以对您执行此操作。
  • 另请注意,当页面出现此 UTF-8 BOM 问题时,会话变量似乎无法跨页面正常工作。我不得不在 Ubuntu 上使用像 ghex 这样的十六进制编辑器加上iconv -f utf8 -t ascii old.php > new.php 反复检测所有的 Unicode 问题,删除它们,并最终以 ASCII 格式保存页面,而 iconv 命令没有错误。完成后,我注意到页面之间的会话变量保持状态。
  • 似乎当在文件中检测到 UTF-8 BOM 时,不会发送保持会话的标头,因此页面之间的会话变量将获得全新的会话,而不是保持相同的会话。跨度>
  • 并不是 PHP “检测” BOM 并且 - 如果存在 - “决定”丢弃会话变量 - 问题是 PHP(至少我见过这样做的版本)读取文件,读取一个ï,打印它,读取一个»,打印它,读取一个¿,打印它......现在的问题是 session_start() 导致一些标题通信,这只能在我们仍在标题中时发生- 沟通阶段 - 打印一些东西结束了这个阶段。如果您将“display_errors”ini 变量设置为“On”,您会收到一条消息,告诉您 session_start 由于这个原因而失败

标签: php utf-8 character-encoding byte-order-mark


【解决方案1】:

这是一篇旧帖子,已经得到答复,但我可以留下一些我在遇到此 BOM 问题时找到的其他资源。

http://people.w3.org/rishida/utils/bomtester/index.php 通过此页面,您可以检查特定文件是否包含 BOM。

还有一个方便的脚本可以输出当前目录中所有带有 BOM 的文件。

<?php 
function fopen_utf8 ($filename) { 
    $file = @fopen($filename, "r"); 
    $bom = fread($file, 3); 
    if ($bom != b"\xEF\xBB\xBF") 
    { 
        return false; 
    } 
    else 
    { 
        return true; 
    } 
} 

function file_array($path, $exclude = ".|..|design", $recursive = true) { 
    $path = rtrim($path, "/") . "/"; 
    $folder_handle = opendir($path); 
    $exclude_array = explode("|", $exclude); 
    $result = array(); 
    while(false !== ($filename = readdir($folder_handle))) { 
        if(!in_array(strtolower($filename), $exclude_array)) { 
            if(is_dir($path . $filename . "/")) { 
                                // Need to include full "path" or it's an infinite loop 
                if($recursive) $result[] = file_array($path . $filename . "/", $exclude, true); 
            } else { 
                if ( fopen_utf8($path . $filename) ) 
                { 
                    //$result[] = $filename; 
                    echo ($path . $filename . "<br>"); 
                } 
            } 
        } 
    } 
    return $result; 
} 

$files = file_array("."); 
?>

我在 php.net 找到了该代码

Dreamweaver 对此也有帮助,它为您提供了保存文件而不包含 BOM 内容的选项

这是一个迟到的答案,但我仍然希望它有所帮助。 再见

【讨论】:

  • fopen_utf8() 脚本帮助我将 BOM 文件与我使用的来自供应商的 SDK 隔离开​​来。很有帮助!
【解决方案2】:

您知道,php 中有一个选项zend.multibyte,它允许 php 读取带有 BOM 的文件,而不会出现 Headers already sent 错误。

来自 php.ini 文件:

; If enabled, scripts may be written in encodings that are incompatible with
; the scanner.  CP936, Big5, CP949 and Shift_JIS are the examples of such
; encodings.  To use this feature, mbstring extension must be enabled.
; Default: Off
;zend.multibyte = Off

【讨论】:

    【解决方案3】:

    BOM 实际上是识别 UTF-8 文件的最有效方式,现代浏览器和标准都支持并鼓励在 HTTP 响应正文中使用它。

    如果是 PHP 文件,它不是文件,而是作为响应发送的生成的输出,所以很明显,在开头保存所有带有 BOM 的 PHP 文件不是一个好主意,但这并不意味着你不应该在您的回复中使用 BOM。

    实际上,您可以在 doctype 声明之前安全地注入以下代码(如果您生成 HTML 作为响应):

    &lt;?="\u{FEFF}"?&gt;(或 PHP 7.0.0 之前:&lt;?="\xEF\xBB\xBF"?&gt;

    进一步阅读:https://www.w3.org/International/questions/qa-byte-order-mark#transcoding

    【讨论】:

      【解决方案4】:

      在 PHP 中,除了“标头已发送”错误之外,BOM 的存在还会以更微妙的方式破坏浏览器中的 HTML。

      请参阅Display problems caused by the UTF-8 BOM 了解问题的概要,重点关注 PHP(W3C 国际化)。

      当这种情况发生时,不仅渲染页面的顶部通常会有一个明显的空间,而且如果您在 Firefox 或 Chrome 中检查 HTML,您可能会注意到 head 部分是空的,并且它的元素似乎在身体。

      当然,查看源代码会显示它插入的所有内容,但浏览器会将其解释为正文内容(文本)并将其插入到文档对象模型 (DOM) 中。

      【讨论】:

        【解决方案5】:

        BOM 会导致Headers already sent 错误,因此,您不能在 PHP 文件中使用 BOM

        【讨论】:

          【解决方案6】:

          确实,BOM 是发送到浏览器的实际数据。浏览器会很高兴地忽略它,但您仍然无法发送标头。

          我相信问题确实出在您和您朋友的编辑器设置上。如果没有 BOM,您朋友的编辑器可能不会自动将文件识别为 UTF-8。他可以尝试设置他的编辑器,使得编辑器期望文件为 UTF-8(如果您使用真正的 IDE,例如 NetBeans,那么这甚至可以作为您的项目设置可以随代码一起传输)。

          另一种方法是尝试一些技巧:一些编辑器尝试根据输入的文本使用一些启发式方法来确定编码。您可以尝试使用

          开始每个文件
          <?php //Úτƒ-8 encoded
          

          也许启发式会得到它。可能有更好的东西可以放在那里,你可以谷歌搜索哪种编码检测启发式很常见,或者只是尝试一些:-)

          总而言之,我建议只修复编辑器设置。

          哦,等等,我误读了最后一部分:为了将代码传播到任何地方,我想你最安全的是让所有文件只包含低 7 位字符,即纯 ASCII,或者只是接受一些人古代编辑看到你的名字写得很有趣。没有万无一失的方法。由于标头已经发送,因此 BOM 肯定很糟糕。另一方面,只要你只在 cmets 等中放入 UTF-8 字符,一些编辑器误解编码的唯一影响就是奇怪的字符。我会正确拼写你的名字并添加针对启发式的评论,以便大多数编辑者都能理解,但总会有人看到假字符。

          【讨论】:

          • 感谢您的建议。我了解我的立场,我认为与其编码检测启发式方法(这是一种有点奇怪的折衷方案)相比,我会做一个不错的选择,只是用“s”而不是“ș”来拼写我的名字,大多数可能无论如何,编码员甚至在他们的语言中都没有那个字符。正确的? :)
          • 浏览器不会忽略 BOM。而且这些错误很难追踪。切勿使用 BOM 保存 PHP 文件。
          • 不,因为它不是错误。 BOM 是可憎的,不要使用它。
          • 这肯定是一个错误。 PHP 可以在标头阶段结束时轻松“重新流动”它。 BOM 有很多很好的理由,包括尽管有技术手段将内容编码与文件(包括 xattr/windows ADS)一起存储在带外,但实际上并没有这样做,所以......我们有点需要-band 方法,例如 BOM 和 的 TRUE 可憎。此外,它只是一个神奇的数字,就像它之前的许多编码/文件格式一样。
          【解决方案7】:

          或者您可以在 php.ini 中激活输出缓冲,这将解决“标头已发送”问题。如果您的网站负载很大,使用输出缓冲来提高性能也非常重要。

          【讨论】:

            【解决方案8】:

            添加到@omabena 答案使用此代码从您的文件中查找和删除 bom。请务必先备份文件以防万一。

            function fopen_utf8 ($filename) { 
                $file = @fopen($filename, "r"); 
                $bom = fread($file, 3); 
                if ($bom != b"\xEF\xBB\xBF") 
                { 
                    return false; 
                } 
                else 
                { 
                    return true; 
                } 
            } 
            
            function file_array($path, $exclude = ".|..|design", $recursive = true) { 
                $path = rtrim($path, "/") . "/"; 
                $folder_handle = opendir($path); 
                $exclude_array = explode("|", $exclude); 
                $result = array(); 
                while(false !== ($filename = readdir($folder_handle))) { 
                    if(!in_array(strtolower($filename), $exclude_array)) { 
                        if(is_dir($path . $filename . "/")) { 
                                            // Need to include full "path" or it's an infinite loop 
                            if($recursive) $result[] = file_array($path . $filename . "/", $exclude, true); 
                        } else { 
                            if ( fopen_utf8($path . $filename) ) 
                            { 
                                //$result[] = $filename; 
                                echo ($path . $filename . "<br>"); 
                                $pathname = $path . $filename; // change the pathname to your target file(s) which you want to remove the BOM.
                                $file_handler = fopen($pathname, "r");
                                $contents = fread($file_handler, filesize($pathname));
                                fclose($file_handler);
                                for ($i = 0; $i < 3; $i++){
                                    $bytes[$i] = ord(substr($contents, $i, 1));
                                }
                                if ($bytes[0] == 0xef && $bytes[1] == 0xbb && $bytes[2] == 0xbf){
                                    $file_handler = fopen($pathname, "w");
                                    fwrite($file_handler, substr($contents, 3));
                                    fclose($file_handler);
                                    printf("%s BOM removed.<br/>n", $pathname);
                                }
                            } 
                        } 
                    } 
                } 
                return $result; 
            } 
            
            $files = file_array("."); 
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-03-10
              • 2018-06-14
              • 2011-03-08
              • 1970-01-01
              • 2018-10-28
              • 2011-07-21
              • 1970-01-01
              相关资源
              最近更新 更多