2011-04-30 34 views
16

在爲utf-8設置了字符集的所有配置文件和運行時選項之後,使用php創建的新mysqli連接仍然將其charset設置爲latin1,這意味着我必須每次連接時都調用$mysqli->set_charset('utf8')做一個永久的方式mysqli-> set_charset()?

$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); 
if ($mysqli->connect_error) 
    err_handle("mysql connect error({$mysqli->connect_errno})."); 
if (!$mysqli->set_charset("utf8")) 
    err_handle("db error({$mysqli->errno})."); 

我不知道是否有永久性的方式做到這一點?

this post遇到類似的問題。


MySQL的服務器上的「show variables like 'character_set%'」查詢調用$mysqli->set_charset('utf8')前顯示:
(這部分是在以前的轉速曖昧)

character_set_client latin1 
character_set_connection latin1 
character_set_database utf8 
character_set_filesystem binary 
character_set_results latin1 
character_set_server utf8 
character_set_system utf8 

客戶端,連接和結果的字符集只能改變在運行時與$mysqli->set_charset('utf8')一起utf8。之後,它顯示:

character_set_client utf8 
character_set_connection utf8 
character_set_database utf8 
character_set_filesystem binary 
character_set_results utf8 
character_set_server utf8 
character_set_system utf8 

我有

default_charset = "utf-8" 

在php.ini中設置,並在my.cnf

[client] 
default-character-set=utf8 
... 
[mysqld] 
## This option is deprecated in favor of --character-set-server. 
#default-character-set=utf8 

集。

我的表的默認字符集也是utf8。

好像「[client]」選項隻影響cmd「mysql」工具,並且與php無關。

$mysqli->character_set_name()的返回值總是latin1無論我做什麼,直到調用$mysqli->set_charset('utf8')

我猜「latin1」是一個mysql的東西,因爲我不能回想起我的系統中默認爲「latin1」的其他任何東西。

^更新:根據MySQL手冊9.1.49.1.55.1.3character_set_client應該由客戶端提供。我想PHP不提供連接和MySQL使用後退字符集latin1

我在Debian wheezy上運行php 5.3,使用mysql 5.1。

有什麼建議嗎?


更新了信息從評論:

我忘了提skip-character-set-client-handshake指令爲什麼我不願意使用它。

在乍一看我還以爲忽略握手可能會導致局勢的客戶端會談LATIN1而服務器會談UTF8。不知道當前正在使用的字符集,服務器如何將字符串從字符集character_set_client轉換爲character_set_server

糾正我,如果我錯了,PLZ。 今天晚些時候我會試用這個設置,看它是否有效。

workaroud更新時間:

確保一切都在UTF-8(或任何字符集最好)的作品。然後將skip-character-set-client-handshake行添加到my.cnf

這對我來說是有效的。我嘗試了一些雙倍寬度的utf-8字符。 insertselect都成功並在瀏覽器中正確顯示。

什麼跳過握手手段仍不清楚。現在mysql服務器變得無法使用除utf-8之外的任何字符集,因爲我不能將此設置應用於我的網站所運行的所有服務器,所以這種解決方法非常不切實際。

所以我沒有采用這種解決方法。進一步的評論和回答非常感謝。根據從MySQL下列職位

+0

您是否嘗試過使用'setlocale()'更改系統區域設置? – Narf 2011-04-30 16:22:34

+0

@Narf可用於我的系統上的php('locale -a'的輸出)的語言環境是C,POSIX和en_US.utf8。而且,'setlocale()'不能成爲一個永久的解決方案,因爲_「locale信息是每個進程維護的」_,根據php手冊。 – 2011-04-30 17:58:42

+0

如果這應該起作用,en_US.utf8將是正確的。除非另外配置,否則將每個程序的默認字符集設置爲當前系統區域設置是合乎邏輯的。如果將PHP作爲Apache模塊運行,則可以將區域設置設置爲apache指令,或者甚至可以將默認系統區域設置爲en_US.utf8。無論如何...這只是一個想法,以防萬一沒有其他作品。 – Narf 2011-04-30 22:41:47

回答

18

您已經正確診斷出基本問題:雖然您可以更改客戶端計算機的my.cnf.my.cnf中的默認MySQL客戶端字符集,但PHP不會使用這些文件。

如果您想了解PHP的MySQLi/MySQL擴展如何工作,這將有意義 - 它們與mysql客戶端程序無關,並且不會抓取配置文件的文件系統,因爲它們直接使用libmysql

要更改libmysql的實際默認字符集,您只需重新構建libmysql。這可能不是你喜歡的答案(因爲你正在使用預編譯的MySQL二進制文件),但它是實際的答案。默認值是在編譯時設置的,然後可以在運行時重寫。

如果你不想這樣做,並且調用set_charset()會引起你的困擾,我的建議是簡單地擴展MySQLi類並使用該類代替mysqli。即:

class MyDB extends mysqli { 
    // (You could set defaults for the params here if you want 
    // i.e. $host = 'myserver', $dbname = 'myappsdb' etc.) 
    public function __construct($host = NULL, $username = NULL, $dbname = NULL, $port = NULL, $socket = NULL) { 
    parent::__construct($host, $username, $dbname, $port, $socket); 
    $this->set_charset("utf8"); 
    } 
} 

通常在應用程序中你有某種數據庫抽象層的,所以你可以擁有這層使用MYDB,而不是庫MySQLi,或者你可以擁有這層 MYDB,並添加或者覆蓋你想要的任何方法(我已經用簡單的無ORM應用程序完成了這個)。

總是有一些數據庫抽象層是一個很好的習慣,即使它只是從class MyDB extends mysqli {}開始,因爲那樣你就永遠不必搜索/替換整個代碼庫來做一些小的修改。

RE:您的解決方法,正如您所解釋的,無論客戶端請求什麼,本質上都會將整個數據庫服務器硬編碼爲UTF-8。如果客戶端連接另一個字符集,服務器只能使用UTF-8,並且可能會默默地破壞數據,而不是擁有多個數據庫,而每個數據庫都有自己的字符集。這基本上是錯誤的,因爲您已經將應用程序的配置(數據庫字符集)的一個方面有效地從應用程序/客戶端機器移動到它並不真正屬於的數據庫服務器。

如果你對應用程序棧的層,

[server] <=> [network] <=> [client libmysql] <=> [PHP binary] <=> [app] 

那麼你就會明白,「正確」的地方,像這樣的應用程序特定的配置是在應用程序本身,在棧中沒有別處。你可能不喜歡用PHP來指定你的數據庫的字符集,但是如果你仔細想一想,那其實就是它的所在,因爲它也是你指定要連接的數據庫本身的地方 - 它是一個連接參數,不是服務器配置問題。在其他地方對字符集進行硬編碼會使您的應用程序不可移植。

+0

+1爲抽象層。無論如何,我沒有使用解決方法。 – 2011-06-07 06:05:32

2

http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html http://dev.mysql.com/doc/refman/5.0/en/charset-applications.html

你的設置不完全正確即使用代替

[mysqld] 
character-set-server=utf8 
collation-server=utf8_general_ci 

[mysqld] 
default-character-set=utf8 

客戶端我只找到

[mysql] 
default-character-set=utf8 

[client] 
default-character-set=utf8 

嘗試給我一些建議。

我記得我曾經讀過關於設置var的一段關於客戶端 更改字符設置的功能。但是我現在無法在mysql文檔中找到ref。如果我找到它,我會讓你知道。

希望有所幫助。

問候

UPDATE

@Unisland順便說一句,我發現這個線程http://www.webmasterworld.com/php/3553642.htm哪裏有類似的問題進行了討論

儘量要麼

所以,你可以嘗試添加:
[mysqld]
INIT-連接= 'SET NAMES UTF8'

[客戶]
默認字符集= UTF8

的[mysqld]
字符集服務器= utf8
default-character-set = utf8
default-collat​​ion = utf8_unicode_ci
character-set-client = utf8

設置此爲所有連接的默認,或與這些查詢開始您的特定腳本發送其他查詢之前連接到數據庫後: SET NAMES UTF8; SET CHARACTER_SET utf8;

+0

我實際上有'character-set-server'這一行,附帶的'show variables'查詢輸出證明它正在工作。 – 2011-05-01 01:51:07

+0

我認爲'[mysql]'指令隻影響cmd'mysql'工具。如我錯了請糾正我。我知道我可以用'skip-character-set-client-handshake'來忽略來自客戶端的字符集信息,但我有點不情願這樣做。這是不是意味着忽略漢克哈克的情況可能會變成客戶在服務器讀取_utf8_時會說_latin1_? – 2011-05-01 01:58:09

+0

@Unilsland試用一下。你有什麼要放棄的?問題是如果mysql在my.cnf中檢測到錯誤的行,那麼你不知道它如何處理配置的其餘部分。一旦我在一行中設置了錯誤的設置,並且導致了奇怪的行爲,就會發生在我身上 – 2011-05-01 07:47:11