Php UTF-8一路走来

Php UTF-8一路走来,php,mysql,linux,apache,utf-8,Php,Mysql,Linux,Apache,Utf 8,我正在设置一个新服务器,希望在我的web应用程序中完全支持UTF-8。我过去曾在现有服务器上尝试过这一点,但最终似乎不得不回到ISO-8859-1 我到底需要在哪里设置编码/字符集?我知道我需要配置Apache、MySQL和PHP来实现这一点——我是否可以遵循一些标准检查表,或者对出现不匹配的地方进行故障排除 这适用于运行MySQL 5、PHP、5和Apache 2的新Linux服务器。在PHP中,您需要使用或打开。这样,如果您的字符占用多个字节,则strlen之类的功能将起作用 您还需要确定响

我正在设置一个新服务器,希望在我的web应用程序中完全支持UTF-8。我过去曾在现有服务器上尝试过这一点,但最终似乎不得不回到ISO-8859-1

我到底需要在哪里设置编码/字符集?我知道我需要配置Apache、MySQL和PHP来实现这一点——我是否可以遵循一些标准检查表,或者对出现不匹配的地方进行故障排除


这适用于运行MySQL 5、PHP、5和Apache 2的新Linux服务器。

在PHP中,您需要使用或打开。这样,如果您的字符占用多个字节,则strlen之类的功能将起作用

您还需要确定响应的字符集。您可以如上所述使用AddDefaultCharset,也可以编写返回标头的PHP代码。或者,您可以向HTML文档添加元标记。

除了在php.ini中设置默认字符集外,您还可以在任何输出之前,使用代码中的标题发送正确的字符集:

header('Content-Type: text/html; charset=utf-8');
在PHP中使用Unicode是很容易的,只要您意识到大多数字符串函数不能使用Unicode,并且有些函数可能会完全破坏字符串。PHP认为字符长度为1字节。有时这是可以的,例如,explode只查找字节序列并将其用作分隔符,所以您查找的实际字符并不重要。但在其他情况下,当函数实际设计用于处理字符时,PHP不知道文本具有Unicode中的多字节字符

一个好的借书室是。这将重写所有不好的函数,以便您可以安全地处理UTF8字符串。也有像mbstring扩展这样的扩展尝试为您实现这一点,但我更喜欢使用库,因为它更便于移植,但我编写了大众市场产品,所以这对我来说很重要。但是phputf8可以在幕后使用mbstring来提高性能。

数据存储:

在数据库中的所有表和文本列上指定utf8mb4字符集。这使得MySQL物理上存储和检索以UTF-8本机编码的值。请注意,如果在没有任何显式字符集的情况下指定utf8mb4_*排序规则,MySQL将隐式使用utf8mb4编码

不幸的是,在MySQL<5.5.3的旧版本中,您将被迫只使用utf8,它只支持Unicode字符的子集。我希望我是在开玩笑

数据访问:

在应用程序代码(例如PHP)中,无论使用何种DB访问方法,都需要将连接字符集设置为utf8mb4。这样,当MySQL将数据交给您的应用程序时,它不会从本机UTF-8进行转换,反之亦然

一些驱动程序提供了自己的机制来配置连接字符集,它既更新自己的内部状态,又通知MySQL要在连接上使用的编码,这通常是首选方法。在PHP中:

如果将抽象层与PHP一起使用≥ 5.3.6,您可以在以下文件中指定字符集:

如果您正在使用,您可以拨打:

如果您一直使用plain,但碰巧正在运行PHP≥ 5.2.3,你可以打电话

如果驱动程序没有提供自己的机制来设置连接字符集,那么您可能必须发出一个查询,告诉MySQL您的应用程序希望如何对连接上的数据进行编码:

关于utf8mb4/utf8的相同考虑如上所述

输出:

如果您的应用程序将文本传输到其他系统,则还需要通知他们字符编码。对于web应用程序,必须通知浏览器通过HTTP响应头或响应头发送数据的编码

在PHP中,您可以使用PHP.ini选项,或者自己手动发出内容类型MIME头,这只是更多的工作,但具有相同的效果

使用json_encode编码输出时,添加json_UNESCAPED_UNICODE作为第二个参数

输入:

不幸的是,在您尝试存储或在任何地方使用它之前,您应该验证每个接收到的字符串是否是有效的UTF-8。PHP可以做到这一点,但你必须虔诚地使用它。真的没有办法解决这个问题,因为恶意客户端可以以他们想要的任何编码提交数据,我还没有找到让PHP可靠地为您完成这项工作的窍门

从我对当前的阅读来看,以下子项目符号对于现代HTML不再是必要的,甚至不再有效。我的理解是,浏览器将使用并提交文档指定字符集中的数据。但是,如果您针对的是较旧版本的HTML XHTML、HTML4等,以下几点可能仍然有用:

仅适用于HTML5之前的HTML:您希望浏览器发送给您的所有数据都是UTF-8格式。不幸的是,如果要可靠地执行此操作,唯一的方法是将accept charset属性添加到所有标记:。 仅适用于HTML5之前的HTML:请注意 3C HTML规范指出,客户端应该默认使用服务器提供的任何字符集将表单发送回服务器,但这显然只是一个建议,因此需要在每个标记上显式显示。 其他代码注意事项:

显然,您将提供的所有PHP、HTML、JavaScript等文件都应该使用有效的UTF-8编码

您需要确保每次处理UTF-8字符串时都是安全的。不幸的是,这是最困难的部分。您可能希望广泛使用PHP的扩展

PHP的内置字符串操作在默认情况下不是UTF-8安全的。使用普通的PHP字符串操作(如连接)可以安全地执行一些操作,但对于大多数操作,应该使用等效的mbstring函数

要知道你在做什么,请阅读:不要搞砸了,你真的需要知道UTF-8以及它如何在尽可能低的级别上工作。请查看中的任何链接,获取一些好的资源,以了解您需要了解的一切


PHP中的Unicode支持仍然非常混乱。虽然它能够将内部使用的ISO8859字符串转换为utf8,但它缺乏本机使用unicode字符串的能力,这意味着所有字符串处理函数都会损坏字符串。因此,您必须使用单独的库来获得适当的utf8支持,或者自己重写所有字符串处理函数


简单的部分就是在HTTP头和数据库中指定字符集,但是如果PHP代码没有输出有效的UTF8,这一切都不重要。这是最困难的部分,而PHP实际上对您没有任何帮助。我认为PHP6应该能解决最糟糕的问题,但那还需要一段时间

我想补充一点:

不要忘记META标记,要么像这样,要么:

这似乎微不足道,但IE7以前给我带来了问题

我做的每件事都是对的;数据库、数据库连接和内容类型HTTP头都设置为UTF-8,在所有其他浏览器中都可以正常工作,但Internet Explorer仍然坚持使用西欧编码

结果显示该页面缺少META标记。这解决了问题

编辑:

W3C实际上拥有相当大的影响力。他们有许多与此相关的文章–描述了HTTP、XHTML和CSS方面的内容:


他们建议在XHTML用作XML的情况下,同时使用HTTP头和HTML元标记或XML声明。

最好的答案是。以下是我在常规debian/php/mysql设置中所做的工作:

// storage
// debian. apparently already utf-8

// retrieval
// the mysql database was stored in utf-8, 
// but apparently php was requesting iso. this worked: 
// ***notice "utf8", without dash, this is a mysql encoding***
mysql_set_charset('utf8');

// delivery
// php.ini did not have a default charset, 
// (it was commented out, shared host) and
// no http encoding was specified in the apache headers.
// this made apache send out a utf-8 header
// (and perhaps made php actually send out utf-8)
// ***notice "utf-8", with dash, this is a php encoding***
ini_set('default_charset','utf-8');

// submission
// this worked in all major browsers once apache
// was sending out the utf-8 header. i didnt add
// the accept-charset attribute.

// processing
// changed a few commands in php, like substr,
// to mb_substr
就这些

在我的例子中,我使用的是mb_split,它使用正则表达式。因此,我还必须通过执行mb_regex_编码“utf-8”,手动确保regex编码是utf-8


作为补充说明,我还通过运行mb_internal_encoding发现内部编码不是utf-8,并通过运行mb_internal_encoding utf-8;进行了更改

我发现有人在使用PDO时出现问题,答案是将其用于PDO连接字符串:

$pdo = new PDO(
    'mysql:host=mysql.example.com;dbname=example_db',
    "username",
    "password",
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

我从中获取此信息的站点已关闭,但幸运的是,我能够使用Google缓存获取它。

我最近发现,使用strtolower可能会导致数据在特殊字符后被截断的问题

解决办法是使用

mb_strtolower($string, 'UTF-8');
mb_uu使用多字节。它支持更多的字符,但一般来说有点慢


首先,如果你的生命值小于5.3hp,那么就不行。你有很多问题要解决

我很惊讶没有人提到这个库,这个库很好地支持Unicode、graphemes、字符串操作、本地化等等,见下文

我将引用Elizabeth Smith在PHPBenelux'14上关于PHP中Unicode支持的一些信息

国际 好:

ICU图书馆的包装 标准化语言环境,为每个脚本设置语言环境 数字格式 货币格式 消息格式取代了gettext 日历、日期、时区和时间 音译词 欺骗者 资源束 转换器 IDN支持 字形 校勘 遍历器 坏的:

不支持zend_多字节 不支持HTTP输入输出转换 不支持函数重载 mb_字符串 启用zend_多字节支持 支持透明HTTP输入/输出编码 为strtoupper等功能提供一些包装 ICONV 主要用于字符集转换 输出缓冲区处理程序 mime编码功能 转变 一些字符串帮助程序len、substr、strpos、strrpos 流过滤器流过滤器附加$fp,'convert.iconv.ISO-2022-JP/EUC-JP' 数据库 MySQL:字符集和排序规则表,而不是连接上的排序规则。另外,不要使用mysql-mysqli或PDO postgresql:pg\u集\u客户端\u编码 sqlite3:确保它是使用Unicode和intl支持编译的 一些其他的哥特 除非使用第三部分扩展名,否则无法在PHP和windows中使用Unicode文件名。 如果您使用exec,proc\u open,请以ASCII格式发送所有内容 和其他命令行调用 纯文本不是纯文本,文件有编码 您可以使用iconv过滤器动态转换文件
我将更新此答案,以防更改添加的功能等等。

我唯一要添加到这些惊人答案中的是强调以utf8编码保存文件,我注意到浏览器接受此属性而不是将utf8设置为代码编码。任何像样的文本编辑器都会向您显示这一点,例如,记事本++有一个用于文件编码的菜单选项,它会向您显示当前编码并允许您更改它。对于我所有的php文件,我使用utf8而不使用BOM


不久前,有人要求我为别人设计的php/mysql应用程序添加utf8支持,我注意到所有文件都是用ANSI编码的,所以我不得不使用ICONV转换所有文件,更改数据库表以使用utf8字符集和utf8\u general\u ci collate,如果使用5.3.6或更早版本,请在连接后将“SET NAMES utf8”添加到数据库抽象层,否则必须在连接字符串中使用charset=utf8,并更改字符串函数以使用等效的php多字节字符串函数。

如果希望MySQL服务器决定字符集,而不是将php作为客户端的旧行为;我认为,最好在[mysqld]下的my.cnf中添加跳过字符集客户端握手,然后重新启动mysql


如果您使用的不是UTF8,那么这可能会造成问题。

我刚刚讨论了同样的问题,并在PHP手册中找到了一个很好的解决方案

我将所有文件编码更改为UTF8,然后是连接上的默认编码。这解决了所有的问题

if (!$mysqli->set_charset("utf8")) {
    printf("Error loading character set utf8: %s\n", $mysqli->error);
} else {
   printf("Current character set: %s\n", $mysqli->character_set_name());
}

如果您想要一个mysql解决方案,在服务器迁移之后,我的两个项目也有类似的问题。在搜索和尝试了很多解决方案后,我发现了这个解决方案,但在这个解决方案起作用之前,我什么都没有:

mysqli_set_charset($con,"utf8");
将这一行添加到我的配置文件后,一切正常

当我试图解决一个从html插入的查询时,我找到了这个解决方案

祝你好运

请注意:

您面临的问题是,您的非拉丁字符显示为,你问了一个问题,然后它就以这个经典的问题结束了,你尝试了一切,不管你做什么,你仍然会得到什么??????????来自MySQL

这主要是因为您正在测试旧数据,这些数据使用错误的字符集插入到数据库中,并被转换和存储为问号字符?。这意味着你永远失去了你的原始文本,无论你尝试什么,你都会得到

将从这个问题的答案中学到的知识重新应用到新的数据上可以解决您的问题。

in connection.php:mysqli\u set\u charset$con,“utf8”;
在sql collation utf=8中,我没有错:COLLATE意味着字符集。请参阅,例如,请注意MySQL与其他人所说的语言不同。当MySQL说utf8时,它真的意味着UTF-8的一些奇怪的延迟变体,它被限制在三个字节之内,天知道是什么可笑的原因。如果你真的想要UTF-8,你应该告诉MySQL你想要这个MySQL喜欢调用的奇怪东西。不要费心在WTF上存钱!s、 这里是一个关于所有可能出现的编码错误的概述:这里介绍了一般的编码,特别是PHP中的编码:最近的一些数据表明,2010年官方放弃的位置没有任何变化。。。关于PHP7和UTF-8还有更多的问题吗?这个问题很常见。但是没有捷径解决方案,您必须分别为每一个设置utf-8-MySQL 5,PHP 5或Apache 2。有关func_重载设置的好技巧-允许对现有代码进行最小修改。请小心-某些代码实际上可能依赖于标准字符串函数的每个字符一个字节的特性。值得注意的是,从PHP 7.2开始,mbstring.func_重载功能已被弃用,由于@JW在上述评论中指出的问题。因此,最好的建议是:是的,你一定要使用mbstring函数,但不要使用重载特性让标准函数作为多字节函数工作。是的,没错。Mysqli和PDO可以使用它们的本地驱动程序。此外,如果您将使用-with mysqli=mysqlnd-with pdo mysql=mysqlnd选项编译php,他们也可以使用mysqlnd驱动程序。进一步查找,这仅适用于5.3.6之前的php版本。另请参见:尽管它们使用单独的$dbh->execset names utf8;;我确实更喜欢这里介绍的方法。顺便说一句,PHP手册中也有类似的注释:。另请参见。你救了我的命。我花了一个小时试图在我正在处理的页面上找出一个编码问题,我通常很擅长找出问题所在。我经常查阅这一页,你的回答对我帮助很大。我投了一票。在我的例子中,set_charset'utf8mb4'不起作用,但是>set_charsetutf8起作用,而这实际上并没有显示在其他答案中。@Fun
kFortyNiner注意:set_charsetutf8可能会工作,但会有不同的行为。请参阅有关utf8和utf8mb4之间的差异以及mysql版本历史记录的说明。如果必须使用utf8,并且只有在您知道自己在做什么时才使用utf8!5星解决方案,我正在逐行阅读文本文件,并得到?对于每个字符,我使用utf8保存为,而不是ansi。谢谢
mb_strtolower($string, 'UTF-8');
if (!$mysqli->set_charset("utf8")) {
    printf("Error loading character set utf8: %s\n", $mysqli->error);
} else {
   printf("Current character set: %s\n", $mysqli->character_set_name());
}
mysqli_set_charset($con,"utf8");