我有一些带有 utf8mb4 字段的MySQL表,以及带有 utf8 的其他表。
在所有表的PDO连接字符串中使用utf8mb4是安全的吗?或者我是否必须将所有内容转换为utf8mb4,或启动两个不同的PDO连接?
编辑:问题不在于"我可以将4字节字符存储到utf8列中吗?" 我们已经知道我们不能这样做,而不是' t取决于连接,因此如果列是utf8,则意味着它不会收到4个字节的字符,例如国家或货币代码,电子邮件地址,用户名...应用程序验证输入的位置。
答案 0 :(得分:5)
使用以下脚本可以很容易地对其进行测试:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'test', '');
$pdo->exec("
drop table if exists utf8_test;
create table utf8_test(
conn varchar(50) collate ascii_bin,
column_latin1 varchar(50) collate latin1_general_ci,
column_utf8 varchar(50) collate utf8_unicode_ci,
column_utf8mb4 varchar(50) collate utf8mb4_unicode_ci
);
");
$latin = 'abc äŒé';
$utf8 = '♔♕';
$mb4 = ' ';
$pdo->exec("set names utf8");
$pdo->exec("
insert into utf8_test(conn, column_latin1, column_utf8, column_utf8mb4)
values ('utf8', '$latin', '$latin $utf8', '$latin $utf8 $mb4')
");
$pdo->exec("set names utf8mb4");
$pdo->exec("
insert into utf8_test(conn, column_latin1, column_utf8, column_utf8mb4)
values ('utf8mb4', '$latin', '$latin $utf8', '$latin $utf8 $mb4')
");
$result = $pdo->query('select * from utf8_test')->fetchAll(PDO::FETCH_ASSOC);
var_export($result);
这是结果:
array (
0 =>
array (
'conn' => 'utf8',
'column_latin1' => 'abc äŒé',
'column_utf8' => 'abc äŒé ♔♕',
'column_utf8mb4' => 'abc äŒé ♔♕ ???? ????',
),
1 =>
array (
'conn' => 'utf8mb4',
'column_latin1' => 'abc äŒé',
'column_utf8' => 'abc äŒé ♔♕',
'column_utf8mb4' => 'abc äŒé ♔♕ ',
),
)
如您所见,当我们使用utf8
列(请参阅utf8mb4
)时,我们不能将????
用作连接字符集。但是在处理utf8mb4
列时,我们可以使用utf8
进行连接。同样,在latin
或ascii
列中读写也没有问题。
原因是您可以在utf8
中对任何latin
,ascii
或utf8mb4
字符进行编码,但不能相反。因此,在这种情况下,使用utf8mb4
作为连接的字符集是安全的。
答案 1 :(得分:3)
简短答案: 是的,如果您仅使用3字节(或更短的)UTF-8字符。
或者... 否,如果您打算使用4字节的UTF-8字符(如。
)详细答案:
(我将说明为什么“不” 可以是正确的答案。)
连接建立客户端使用的编码。
一列上的CHARACTER SET
(默认情况下来自表)确定可以在该列中添加哪种编码。
CHARACTER SET utf8
是utf8mb4
的子集。也就是说,utf8
可以接受utf8mb4
可接受的所有字符(通过连接或列)。换句话说,MySQL的utf8mb4
(与外界的UTF-8
一样)具有完整的4字节utf-8编码,比MySQL最多3位数包含更多的表情符号,更多的中文等。字节utf8
(又称“ BMP”)
(从技术上讲,utf8mb4
仅可处理4个字节,而UTF-8
可处理更长的字符。但是,我怀疑5字节字符是否会在我的一生中发生。)
因此,这是客户端中任何3字节(或更短的)UTF-8字符发生的情况,假设Connection为utf8mb4,并且表中的列仅为utf8:每个字符进出服务器没有任何转换,也没有错误。注意:问题发生在INSERT
上,而不发生在SELECT
上。但是,除非您执行SELECT
,否则您可能不会注意到问题。
但是,如果客户端中有表情符号怎么办?现在,您将得到一个错误。 (或截断的字符串)(或问号),这是因为无法将4字节的表情符号(例如)压缩为3字节的“ utf8”(或“ 1字节的latin1”或...)
如果运行的是5.5或5.6,则可能会遇到767(或191)问题。我在here中提供了几种解决方法。没有一个是完美的。
关于反转(utf8连接,但为utf8mb4列):如果您设法在表中放入4个字节的字符,SELECT
可能会遇到麻烦。
“官方消息”-祝您好运。我花了十年的时间试图弄清字符处理的来龙去脉,然后将其简化为可操作的句子。那个时候的大部分时间都在想我拥有所有答案,只是遇到了另一个失败的测试用例。常见情况在Trouble with UTF-8 characters; what I see is not what I stored中列出。但是,这不能直接解决您的问题!
来自评论
mysql> SHOW CREATE TABLE emoji\G
*************************** 1. row ***************************
Table: emoji
Create Table: CREATE TABLE `emoji` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`text` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)
mysql> insert into emoji (text) values ("abc");
Query OK, 1 row affected (0.01 sec)
mysql> show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8mb4 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)
上面说“连接”(认为“客户端”)使用的是utf8,而不是utf8mb4。
mysql> insert into emoji (text) values (""); -- 4-byte Emoji
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+----------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------------------------------------------+
| Warning | 1366 | Incorrect string value: '\xF0\x9F\x98\x85\xF0\x9F...' for column 'text' at row 1 |
+---------+------+----------------------------------------------------------------------------------+
1 row in set (0.00 sec)
现在,将“连接”更改为utf8mb4
:
mysql> SET NAMES utf8mb4;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into emoji (text) values ("");
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM emoji;
+----+--------------+
| id | text |
+----+--------------+
| 1 | ? ? ? ? |
| 2 | abc |
| 3 | ???????????? | -- from when "utf8" was in use
| 4 | | -- Success with utf8mb4 in use
+----+--------------+
4 rows in set (0.01 sec)
答案 2 :(得分:2)
简答:否,这是不安全的。
如果您的数据有utf8mb4
个字符并且您正在使用MySQL utf8
字符集连接,那么您将遇到问题,因为MySQL utf8
字符集仅支持BMP字符(最多3个字节字符) )。
我的建议是将所有表格转换为utf8mb4
以获得完整的UTF-8支持。此外,utf8mb4
向后兼容与utf8
。