PHP文件頭BOM頭問題

前幾天咱們公司服務器出現了一個離奇的問題,服務器與本地文件代碼徹底一致,本地運行正常,到了測試環境服務器以後,各類問題一個又一個浮現,先是後臺驗證碼不顯示,覺得是session寫入失敗,又是懷疑gd庫,又是以爲服務器gd路徑錯誤,又排查目錄權限,各類方法試之無效,百度必應各類搜索,整個公司一半以上PHP排查問題,咱們以前的代碼以下php

public function createImage() { $word = $this -> randomCode(); // 記錄字符串
        $_SESSION[$this -> _space]['code'] = base64_encode($this -> encryptsCode($word)); $this -> image = ImageCreate($this -> _width, $this -> _height); ImageColorAllocate($this -> image, 220, 220, 220); // 在圖片上添加擾亂元素
        $this -> disturbPixel(); // 在圖片上添加字符串
        $this -> drawCode($word); ob_end_clean();     ------------------------------------服務器文件上解決問題僅僅加了這麼一行函數 ob_clean();  //關鍵代碼,防止出現'圖像因其自己有錯沒法顯示'的問題。
        header("Content-type:text/html;charset=utf-8"); // 設置頁面的編碼風格
        header("Content-type: image/PNG"); ImagePng($this -> image); ImageDestroy($this -> image); }

言歸正傳,雖然這個問題解決了,可是購物車好好地失效了,緣由是咱們前端發現不少文件都帶了BOM頭上去,通過百度各類帖子,找到了檢查BOM文件的方法css

本地文件上傳到服務器上,某些文件頭部老是出現一條空白,不管怎麼修改文件都沒法去除空白,用firebug查看header部分一樣有一片空白,刪除後空白消失,可是在文件裏卻沒法找到那個空白的部分html

BOM頭

BOM: Byte Order Mark前端

UTF-8 BOM又叫UTF-8 簽名,其實UTF-8 的BOM對UFT-8沒有做用,是爲了支援UTF-16,UTF-32才加上的BOM,BOM簽名的意思就是告訴編輯器當前文件採用何種編碼,方便編輯器識別,可是BOM雖然在編輯器中不顯示,可是會產生輸出,就像多了一個空行windows

這些大部分是編輯器的問題,PHP文件採用UTF-8編碼,PHP開發大部分使用的文本編輯軟件如:Zend studio、editplus、eclipse等等均可以顯示並編輯UTF-8編碼的文件,可是也有一些軟件不能知足這個要求.瀏覽器

相似如windows的記事本,在保存一個以UTF-8編碼的文件時,會在文件開始的地方插入三個不可見的字符(0xEF 0xBB 0xBF,即BOM).它是一串隱藏的字符,用於讓記事本等編輯器識別這個文件是否以UTF-8編碼.對於通常的文件,這樣並不會產生什麼麻煩.但對於 PHP來講,BOM是個大麻煩.服務器

對於BOM,PHP並不會忽略,在讀取、包含或者引用這些文件時,PHP會把BOM做爲文件開頭正文的一部分,根據嵌入式語言的特色,這串字符將被直接執行(顯示)出來.這就致使了一些頁面的頭部老是有一條白條,儘管樣式padding、margin等各方面都設置好也沒法讓整個網頁緊貼瀏覽器頂部,這頭部白條就是這3個不可見的字符(0xEF 0xBB 0xBF,即BOM);session

另外還有的問題就是,受COOKIE送出機制的限制,在這些文件開頭已經有BOM的文件中,COOKIE沒法送出(由於在COOKIE送出前PHP已經送出了文件頭),因此登入和登出功能失效.一切依賴COOKIE、SESSION實現的功能所有無效.dom

因此,在編輯、修改任何文本文件的時候,請使用不會亂加BOM的編輯器.Linux下的編輯器應該都沒有這個問題.WINDOWS下,請勿使用記事本等編輯器.推薦使用Editplus,Zend studio、eclipse等編輯器.eclipse

其餘的對於已經添加了BOM的文件,要取消的話,能夠用不會亂加BOM的編輯器另存一次.固然也可使用如下方法去除該目錄下全部文件的頭部BOM:

檢查哪些文件存在BOM

<?php /*檢測並清除BOM*/

$basedir = ROOT_PATH; $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")." 
"; }else{ $dirname = $basedir."/".$file; checkdir($dirname); } } }//end while
        closedir($dh); }//end if($dh
}//end function
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 ""; }else{ return (""); } } else return ("BOM Not Found."); }//end function
function rewrite($filename, $data){ $filenum = fopen($filename, "w"); flock($filenum, LOCK_EX); fwrite($filenum, $data); fclose($filenum); }//end function

而後百度一些工具,有專門清除BOM頭的,其次可使用Notepad++來清除

補充:以上PHP代碼可能會有遺漏,在用以上方法測試完成能夠用一下一下方法

function printDir($d){ $dir=dir($d); while(false != $row = $dir->read()){ if($row=='.' || $row=='..') continue; if(is_dir($d.$row)){ printDir($d.$row.'/'); }else{ $f=fopen($d.$row,"r"); if($f){ $str=fgets($f,102); if (ord($str{0}) == 239 && ord($str{1}) == 187 && ord($str{2}) == 191) { echo $d.$row.'
'; } } fclose($f); } } }

想要花哨一點兒,也能夠這樣玩,不一樣的服務器要區分路徑的格式問題

<?php /** * copyright (c) crossphp.cn * author aray * created 2009-07-25 * **/
error_reporting(E_ALL & ~E_NOTICE); $exts = array( '.x',
    '.html',
    '.dwt',
    '.lbi',
    '.tpl',
    '.php',
    '.js',
    '.css',
    '.xml',
    '.txt', ); $start = false; if ($_POST && !empty($_POST['path']) ) { $start = true; $PATH = $_POST['path']; $PATH = addslashes($PATH); $EXTS = $_POST['exts']; } function ReadDirs($path, $ext) { global $BOM; echo $path; $dir = opendir($path ); echo '<ul>'; while ( ($file = readdir($dir ))) { if ($file == '.' || $file == '..') continue; $f = $path . '/' . $file; if (is_dir($f)) { echo '<li class="folder"><span class="symbol">1</span>' . $file; echo '<ul>' . ReadDirs($f, $ext) . '</ul></li>'; } else { $flag = false; if ( is_array($ext) ) { if (! in_array(getExt($file), $ext) ) { continue; } else { $flag = true; } } else { $flag = true; } if ($flag) { $cssClass = 'file'; if (checkBOM($f)) { $cssClass = 'bom'; $BOM[] = str_replace('//','/',str_replace('\\','/',$f)); } echo '<li class="'.$cssClass.'"><span class="symbol">2</span>' . $file . $isBom . "</li>"; } } } echo '</ul>'; } function getExt($filename ) { $ext = strrchr($filename,'.'); // 根本沒有擴展名
    if ( empty($ext) ) { return null; } return $ext; } function checkBOM($filename ) { $contents = file_get_contents($filename); $char[1] = substr($contents, 0, 1); // 1
    $char[2] = substr($contents, 1, 1); // 2
    $char[3] = substr($contents, 2, 1); // 3 // EF BB BF
    if ( ord($char[1]) == 239 && ord($char[2]) == 187 && ord($char[3]) == 191 ) { return true; } return false; } ?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>BOM檢測工具</title>
<style type="text/css"> body, td, th { font-size: 14px; } body { margin-left: 15%; margin-top: 2px; margin-right: 15%; margin-bottom: 2px; } form { margin: 0px; padding: 0px; } ul { margin: 0px 0px 0px auto; padding: 0px; } .symbol { font-family: Wingdings; font-size: 20px; padding-right: 10px; } .path { color: #0033CC;
} li { color: #333333;
    list-style: none; } .bom { color: #ff00ff;
} .folder { color: #0000ff;
} .file { color: #333333;
} </style>
</head>
<body>
<br/>
<br/>
<table width="100%" border="0" align="center" cellpadding="0" cellspacing="1" bgcolor="#8DAFDA">
  <tr>
    <td height="70" align="center" valign="middle" bgcolor="#CBDBEE"><form id="form1" name="form1" method="post" action="">
        <table border="0" cellspacing="0" cellpadding="0">
          <tr>
            <td height="30" align="left" valign="middle"> 文件夾:</td>
            <td align="left" valign="middle"><select name="path" >
                <?php $dir = opendir(dirname(dirname(__FILE__))); while ( ($f = readdir($dir)) ) { if ($f == '..' || is_file('./' . $f) ) continue;  ?>
                <option value="../<?=$f?>" <?php if($_POST['path'] == '.\\'.$f) echo 'selected';?> >
                <?=$f?>
                </option>
                <?php }?>
              </select></td>
            <td align="left" valign="middle"><input type="submit" name="button" id="button" value="提交" /></td>
          </tr>
        </table>
        <?php foreach($exts as $ext){ ?>
        <label>
          <input type="checkbox" name="exts[]" value="<?=$ext?>" 
          <?php if(is_array($_POST['exts']) && in_array($ext, $_POST['exts'])) echo 'checked'; ?> />
          <?=$ext?>
        </label>
        <?php }?>
      </form></td>
  </tr>
</table>
<div id="result"><br/>
  <br/>
  <?php if($start){?>
  <?php     echo '搜索路徑: <span class="path">' . str_replace('\\\\','\\',$PATH) . '</span> , 實際路徑: <span class="path">' . realpath($PATH) . '</span><br/>';    echo '文件列表: '; ReadDirs( $PATH, $EXTS);?>
  <br/>
  <br/>
  <?php if ($BOM) { ?> 發現BOM文件列表:<br/>
  <ul>
    <?php foreach( $BOM as $f){?>
    <li class="bom">
      <?=$f?>
    </li>
    <?php }?>
  </ul>
  <?php }?>
  <?php }?>
</div>
</body>
</html>
相關文章
相關標籤/搜索