坑大,或者不大,它就在那裏,等着你進。
先前修改成熟的一個基於thinkphp3.1.2的後臺框架,裏面有我的autoCode,本來在新項目上不想再用這麼落後的版本,但考慮到後臺項目不對外使用,重點是autoCode是我的進度保證,於是繼續使用了這個版本。
本地開發環境是windows + phpstudy(apache,php5.4),數據庫直接用線上的mariadb10.2,一切在本地就緒,可當我把這個後臺項目放進線上容器(centos7 + apache + php5.4)裏,滿心歡喜的打開在線地址觀看時,竟然發現網頁中的一部分中文變成了英文問號,經過短暫的分析即刻得出:數據庫裏讀出來的中文亂碼了,而模板輸出的中文是正常的。
這首先排除了頁面編碼的問題,而且代碼在本地運行時一切正常,與線上用的是同一個數據庫,因此數據庫也不存在問題。
需要強調的是:
1、我給所有人要求過代碼文件必須使用utf8無bom
2、每個html文件都有<meta http-equiv="content-type" content="text/html; charset=utf-8">這段
3、數據庫,表及字段全是utf8,且數據庫裏的中文內容是正常的
4、thinkphp的config.php裏面已經加入了'DB_CHARSET' => 'utf8'
深深的疑惑使我嘗試過許許多多的做法,我懷疑了很多不該懷疑的東西,除了人生。
我在php入口文件里加了header("Content-type: text/html; charset=utf-8");
我還把thinkphp3.1.2的/Lib/Driver/Db/DbMysql.class.php中的
mysql_query("SET NAMES '".C('DB_CHARSET')."'", $this->linkID[$linkNum]);
改爲了
mysql_query("SET NAMES 'utf8'", $this->linkID[$linkNum]);
然而這都是無用功。
其實非常明顯,這就是php連接mariadb時的編碼有問題,我一次又一次的把目光投向了apache、php和mariadb的配置文件,特別是mariadb的/etc/my.cnf.d/server.cnf,[mysqld]加入了:
character_set_server=utf8
init_connect = 'SET collation_connection = utf8_general_ci'
init_connect = 'SET NAMES utf8'
其他幾個配置文件該加default-character-set = utf8的也都加了。
亂碼依舊。
痛定思痛,我冷靜下來,經驗告訴我,也許問題就在某個被自己認爲沒有問題而忽視掉的地方。
於是我把以上每項又檢查了一遍。
是該做點別的嘗試了,於是我檢查了centos7的語言環境,並安裝了中文語言包。
我的期待並沒有得到滿足,亂碼還在。
對了我剛開始還讓另一個php工程師在相同環境下的另一個項目裏去讀同一個數據庫裏的中文,結果並沒有亂碼。
而且我直覺一定是哪個地方使得這個後臺項目中php使用了latin1或者別的編碼去連接mysql。
第N次的痛定思痛,我一直都很確定這就是php使用了非utf8編碼去連接mysql,但我在config.php裏配置了呀,甚至還在DbMysql.class.php裏寫死了。
於是我用php做了一個驗證:
dump(M()->query("SHOW VARIABLES LIKE '%char%'"));
dump(M()->query("SET NAMES 'utf8'"));
dump(M()->query("SHOW VARIABLES LIKE '%char%'"));
die;
這段代碼在我本地毫無意外的,與mysql連接的編碼都是utf8,而在線上的結果正如我所想,第一行打出來的結果裏,character_set_connection、character_set_client、character_set_results這三個都是latin1,而在執行第二行後,第三行打印的結果就變成utf8了。
這表明我最後的結論是正確的,可是在哪裏產生的這個問題呢?
於是我再一次打開thinkphp3.1.2框架的/Lib/Driver/Db/DbMysql.class.php文件,
把其中數據庫版本大於4.1的if段註釋掉,改成了判斷mysql_set_charset函數是否存在的if段,如下:
$dbVersion = mysql_get_server_info($this->linkID[$linkNum]);
/*
if ($dbVersion >= '4.1') {
//使用UTF8存取數據庫 需要mysql 4.1.0以上支持
mysql_query("SET NAMES '".C('DB_CHARSET')."'", $this->linkID[$linkNum]);
mysql_query("SET character_set_client = ".C('DB_CHARSET'), $this->linkID[$linkNum]);
mysql_query("SET character_set_results = ".C('DB_CHARSET'), $this->linkID[$linkNum]);
}*/
if (function_exists('mysql_set_charset') === false) {
mysql_query("SET NAMES '".C('DB_CHARSET')."'", $this->linkID[$linkNum]);
}else{
mysql_set_charset(C('DB_CHARSET'), $this->linkID[$linkNum]);
}
問題到此解決。
我先是震驚,問題居然這麼簡單,就是$dbVersion >= '4.1'這個判斷沒進去,於是我想起了我用的是mariadb10.2,確實按照字符串來對比版本的話,這盤算我輸。
線上把$dbVersion這個變量打出來看了下,確實是 string(19) "10.2.12-MariaDB-log"
我又疑惑了,那我本地也是用的這同一個庫啊,難道?
我在本地也打印了下$dbVersion,結果竟然是 string(25) "5.5.5-10.2.12-MariaDB-log"
這個疑問先留着,還有就是前面我明明在mariadb的server.cnf裏配置了默認的連接編碼,誰知道告訴我一聲,我還是先把這個坑記錄一下。