做mysqli-> set_charset()的永久方法?

时间:2011-04-30 16:03:56

标签: php mysql character-encoding mysqli

设置了我可以找到utf-8的charset的所有配置文件和运行时选项之后,用php创建的新mysqli连接仍然将其字符集设置为latin1,这实际上意味着我必须每个都调用$mysqli->set_charset('utf8')我连接的时间。

$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中遇到了类似的问题。


在调用show variables like 'character_set%'之前,在mysql服务器上发出“$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中设置,

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

在my.cnf中设置。

我的表的默认字符集也是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

我正在使用mysql 5.1在debian wheezy上运行php 5.3。

任何建议?


使用评论信息进行更新:

我忘了提及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之外的任何字符集,这使得这种解决方法非常不切实际,因为我无法将此设置应用于我的网站运行的所有服务器。

所以我没有采用这种解决方法。进一步的评论和答案非常感谢。

3 个答案:

答案 0 :(得分: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而不是拥有多个数据库,每个数据库都有自己的字符集,如果客户端与另一个字符集连接,可能会静默地破坏数据。这是根本错误的,因为您已经有效地将应用程序配置(数据库字符集)的一个方面从app / client机器移动到它不属于它的数据库服务器。

如果考虑应用程序堆栈的层,

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

然后你会明白,像这样的特定于应用程序的配置的“正确”位置在应用程序本身,而不是堆栈中的其他位置。您可能不喜欢在PHP中指定数据库的字符集,但如果您考虑它,那就是它所属的位置,因为它也是您要指定要连接到的数据库本身的位置 - 它是一个连接参数,不是服务器配置问题。在其他地方硬编码字符集会使您的应用程序不可移植。

答案 1 :(得分:2)

根据MySQL的以下帖子

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。如果我发现它,我会通知你。

希望有所帮助。

此致

<强>更新

@Unisland BTW我发现这个帖子http://www.webmasterworld.com/php/3553642.htm讨论了类似的问题

尝试

  

所以你可以试着添加一个:
  的[mysqld]
  init-connect ='SET NAMES utf8'

  

[客户]
  default-character-set = utf8

     

的[mysqld]
  字符集服务器= UTF8
  默认字符集= UTF8
  默认自动分页= utf8_unicode_ci
  character-set-client = utf8

将此设置为所有连接的默认值,或者在特定脚本连接到数据库之后从这些查询开始,然后再发送其他查询: SET NAMES utf8; SET CHARACTER_SET utf8;

答案 2 :(得分:0)

我知道这是一个非常老的线程,但是在安装MariaDB之后,我刚在我的Fedora 30 KDE笔记本电脑上遇到了这个问题(并且花了一个多小时来寻找答案)。在我的Ubuntu 18.04服务器上,无需修改任何配置文件就可以正常工作,但是在运行Fedora 30 KDE的笔记本电脑上,我必须:

$ sudo vi /etc/my.cnf.d/client.cnf

,然后在default-character-set部分中添加[client-mariadb]

[client-mariadb]
default-character-set = utf8mb4

我必须对服务器配置执行相同操作:

$ sudo vi /etc/my.cnf.d/mariadb-server.cnf

,然后在[mysqld]部分中添加以下内容:

[mysqld]
character-set-server  = utf8mb4
collation-server      = utf8mb4_general_ci

然后重新启动MariaDB:

$ sudo systemctl restart mariadb.service

此后,无需在PHP脚本中显式设置字符集。