关于PHP的:UTF – 8all the way through

UTF-8 all the way through

我正在设置一个新服务器,希望在我的Web应用程序中完全支持UTF-8。我以前在现有的服务器上尝试过这种方法,结果总是不得不回到ISO-8859-1。

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

这是一个新的Linux服务器,运行mysql 5、php、5和apache 2。


数据存储:好的。

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

  • 在旧版本的mysql(<5.5.3)中,不幸的是,您将不得不简单地使用utf8,它只支持Unicode字符的子集。我真希望我开玩笑。好的。

数据访问:好的。

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

  • 一些驱动程序提供了自己的连接字符集配置机制,这两种机制都会更新自己的内部状态,并通知MySQL要在连接上使用的编码&mdash;这通常是首选的方法。在PHP中:好的。

    • 如果将PDO抽象层与php&ge;5.3.6一起使用,则可以在DSN中指定charset:好的。

      1
      $dbh = new PDO('mysql:charset=utf8mb4');
    • 如果你使用mysqli,你可以打电话给set_charset():好的。

      1
      2
      $mysqli->set_charset('utf8mb4');       // object oriented style
      mysqli_set_charset($link, 'utf8mb4');  // procedural style
    • 如果你坚持使用普通的mysql,但恰好运行php&ge;5.2.3,你可以调用mysql_set_charset。好的。

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

  • 关于utf8mb4/utf8的相同考虑同样适用于上述情况。好的。

输出:好的。

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

  • 在php中,您可以使用default_charsetphp.ini选项,也可以自己手动发出Content-Typemime头文件,这只不过是更多的工作,但效果相同。好的。

输入:好的。

  • 不幸的是,在尝试存储或在任何地方使用之前,您应该验证每个接收到的字符串都是有效的UTF-8。PHP的mb_check_encoding()完成了这个技巧,但您必须认真使用它。确实没有办法解决这个问题,因为恶意客户机可以以他们想要的任何编码提交数据,而且我还没有找到让PHP可靠地为您完成这一任务的诀窍。好的。

  • 根据我对当前HTML规范的理解,以下子项目符号对于现代HTML来说不再是必需的,甚至不再有效。我的理解是浏览器将使用并提交文档指定字符集中的数据。但是,如果您的目标是旧版本的HTML(XHTML、HTML4等),这些要点可能仍然有用:好的。

    • 对于HTML5之前的HTML:您希望浏览器发送给您的所有数据都是UTF-8格式。不幸的是,如果您选择可靠的方法,那么将accept-charset属性添加到您的所有
      标记中:

    • 对于HTML5之前的HTML:请注意,W3C HTML规范规定,客户机"应该"默认以服务器提供的任何字符集将表单发送回服务器,但这显然只是一个建议,因此需要在每个
      标记上显式显示。

其他代码注意事项:好的。

  • 显然,您将要服务的所有文件(PHP、HTML、javascript等)都应该使用有效的UTF-8编码。好的。

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

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

  • 为了知道你在做什么(阅读:不要搞砸了),你真的需要知道UTF-8以及它在尽可能低的级别上是如何工作的。查看utf8.com的任何链接,获取一些好的资源,了解您需要了解的所有信息。好的。

好啊。


我想在查佐马提库斯出色的回答中加上一点:

也不要忘记meta标签(像这样,或者它的HTML4或XHTML版本):

1
<meta charset="utf-8">

这似乎微不足道,但IE7以前给了我一些问题。

我做得很好;数据库、数据库连接和内容类型HTTP头都设置为UTF-8,在所有其他浏览器中都工作正常,但Internet Explorer仍然坚持使用"西欧"编码。

结果发现页面缺少元标记。加上这些解决了问题。

编辑:

W3C实际上有一个相当大的部分专门用于i18n。它们有许多与此问题相关的文章&ndash;描述http,(x)html和css方面的内容:

  • 常见问题解答:将(x)HTML页面编码更改为UTF-8
  • 在HTML中声明字符编码
  • 教程:XHTML、HTML和CSS中的字符集和编码
  • 设置HTTP字符集参数

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


除了在php.ini中设置default_charset之外,您还可以在任何输出之前从代码中使用header()发送正确的字符集:

1
header('Content-Type: text/html; charset=utf-8');

在PHP中使用unicode很容易,只要您意识到大多数字符串函数不使用unicode,并且某些字符串可能会完全损坏。PHP认为"字符"的长度为1字节。有时这是可以的(例如,explode()只查找一个字节序列,并将其用作分隔符——所以您查找的实际字符并不重要)。但其他时候,当函数实际上设计为处理字符时,PHP不知道您的文本中有使用Unicode的多字节字符。

一个好的图书馆是phputf8。这会重写所有"坏"函数,这样您就可以安全地使用utf8字符串。还有一些扩展,比如mbstring扩展,尝试为您做这些,但是我更喜欢使用库,因为它更便携(但我写的是大众市场产品,所以这对我很重要)。但是phputf8可以在幕后使用mbstring来提高性能。


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

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

我从中得到这个的网站已经关闭了,幸运的是能够使用谷歌缓存获得它。


在我的例子中,我使用的是mb_split,它使用regex。因此,我还必须通过执行mb_regex_encoding('UTF-8');手动确保regex编码是utf-8。

作为补充说明,我还发现运行mb_internal_encoding()时,内部编码不是utf-8,并通过运行mb_internal_encoding("UTF-8");更改了这一点。


首先,如果你的马力小于5.3马力,那么就没有了。你有很多问题要解决。

我很惊讶没有人提到intl库,它对unicode、字形、字符串操作、本地化等都有很好的支持,见下文。

我将在phpbenelux'14上引用伊丽莎白·史密斯幻灯片中有关PHP中Unicode支持的一些信息。

国际电信联盟

好:

  • 包裹ICU图书馆
  • 标准化区域设置,每个脚本设置区域设置
  • 数字格式
  • 货币格式
  • 邮件格式(替换GetText)
  • 日历、日期、时区和时间
  • 音译者
  • 欺骗检查器
  • 资源捆绑
  • 变换器
  • IDN支持
  • 字形
  • 校勘
  • 遍历器

坏的:

  • 不支持Zend_多咬合
  • 不支持HTTP输入输出转换
  • 不支持函数重载

MbS-字符串

  • 启用Zend_多字节支持
  • 支持透明HTTP入/出编码
  • 提供一些功能性包装,如strtoupper

伊科夫

  • 字符集转换的主要功能
  • 输出缓冲处理程序
  • MIME编码功能
  • 转换
  • 一些字符串帮助程序(len、substr、strpos、strrpos)
  • 流式过滤器stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

数据库

  • mysql:charset和collation,用于表和连接(而不是collation)。也不要使用mysql-msqli或pdo
  • PostgreSQL:pg_set_client_编码
  • sqlite(3):确保它是用unicode和intl支持编译的

其他一些哥特人

  • 除非使用第三部分扩展名,否则不能在PHP和Windows中使用Unicode文件名。
  • 如果使用exec、proc_open和其他命令行调用,则以ASCII格式发送所有内容
  • 纯文本不是纯文本,文件有编码
  • 您可以使用iconv过滤器动态转换文件

我将更新这个答案,以防添加的功能发生变化等。


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

解决方案是使用

1
mb_strtolower($string, 'UTF-8');

mb_ uses MultiByte. It supports more characters but in general is a little slower.


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

前一段时间,有人让我为别人设计的php/mysql应用程序添加utf8支持,我注意到所有的文件都是用ansi编码的,所以我不得不使用iconv来转换所有的文件,更改数据库表以使用utf8字符集和utf8常规u ci collate,在连接后将"set name utf8"添加到数据库抽象层。action(如果使用5.3.6或更早版本,否则必须在连接字符串中使用charset=utf8)并更改字符串函数以使用等效的php多字节字符串函数。


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

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

1
2
3
4
5
6
7
if (!$mysqli->set_charset("utf8")) {
    printf("Error loading character set utf8: %s
"
, $mysqli->error);
} else {
   printf("Current character set: %s
"
, $mysqli->character_set_name());
}

视图源


在PHP中,您需要使用多字节函数,或者打开mbstring.func_重载。这样,如果您的字符超过一个字节,strlen之类的东西就会工作。

您还需要确定响应的字符集。您可以像上面一样使用adddefaultcharset,或者编写返回头的PHP代码。(或者您可以向HTML文档添加元标记。)


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

简单的部分就是在HTTP头文件和数据库等文件中指定字符集,但是如果您的PHP代码没有输出有效的UTF8,这些都不重要。这是最困难的部分,PHP在这方面几乎没有帮助。(我认为PHP6应该能解决最坏的问题,但那还需要一段时间)


如果您希望mysql服务器决定字符集,而不是将php作为客户机(我认为最好是旧行为),可以尝试在[mysqld]下将skip-character-set-client-handshake添加到您的my.cnf中,然后重新启动mysql

如果您使用的不是UTF8,这可能会导致问题。


最好的答案是非常好的。下面是我在常规debian/php/mysql设置中需要做的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 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

就这样!


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

1
mysqli_set_charset($con,"utf8");

把这一行添加到我的配置文件之后,一切都正常!

我在解决从HTML查询插入的问题时找到了这个解决方案https://www.w3schools.com/php/func_mysqli_set_charset.asp

祝你好运!