环境
Windows 10
PHP 7.4
前言
在互联网领域,UTF-8编码已经几乎成为了标配,无论是网页、数据库还是代码文件等等,都广泛使用UTF-8作为编码方式。我平时写PHP代码时,文件的编码也都是使用UTF-8,今天突然想尝试一下,如果使用其它编码,会有什么后果呢?
实验
1. CLI环境
<?php
$str = '你';
var_dump($str);
编写以上代码,并且将文件的编码设置为GBK,然后在CMD命令行中运行此代码,最后显示结果是中文乱码:
经详细测试,只有在windows 10系统上,使用PHP 7.1(含)以上的PHP版本在CMD命令行中运行时,才会乱码,为什么会这样呢,目前我也不清楚…
接下来我们修改下代码,这次我们不将内容var_dump
出来,而是将内容写入到一个text.txt
文件里:
$str = '你';
file_put_contents(__DIR__ . '/test.txt', $str);
再执行代码,然后用type
命令查看text.txt
文件的内容,发现居然没乱码:
为什么
var_dump
出来会乱码,而写入到文件后再查看就不会乱码呢?因能力有限,这个问题目前还没弄清楚原因,后续有结果再更新:(
2. web环境
在CLI环境中会乱码,那在web页面中呢?答案是也会乱码:
但是我们可以通过设置一个响应头解决这个问题:
$str = '你';
header('Content-type: text/html; charset=gb2312');
var_dump($str);
再查看网页内容,已经不乱码了:
3. zend.multibyte
web环境可以通过设置响应头解决问题,那CLI环境呢?经过一番研究,发现可以通过修改php.ini文件来解决:
将zend.multibyte
参数设置为On
,通过上方的英文描述,我们可以得知,这个参数开启后,就可以使用PHP解释器不兼容的编码来编写代码,例如CP936、Big5等等(注:CP936就是常说的GBK编码)。并且使用这个参数需要mbstring
扩展。
然后在我们的代码文件中,添加一行declare(encoding="cp936")
的代码:
<?php
declare(encoding="cp936");
$str = '你';
var_dump($str);
最后再运行此脚本,已经不乱吗了:
如果你的PHP脚本很多,不想每个文件都添加declare(encoding="...")
的话,可以通过设置php.ini
的zend.script_encoding
配置项来解决,当PHP代码中没有设置declare(encoding="...")
时,会默认使用zend.script_encoding
的编码。
4. 其它
- 如果PHP文件编码是UTF-16或UTF-32,则必须开启
zend.multibyte
,否则代码会无法执行,但无需像GBK那样在代码中添加declare(encoding="...")
这是因为默认情况下,PHP解析器是以ASCII编码来解析代码的,因此文件编码必须兼容ASCII,这里的兼容是指字符编码值一致,且存储空间也一致,例如GBK、UTF-8就符合这个要求。而在UTF-16中,所有字符是使用2个字节存储的,UTF-32是4字节,ASCII是1个字节,存储空间不一致导致不兼容。因此如果用UTF-16/UTF-32作为文件编码的话,必须开启zend.multibyte,这样PHP解析器才能正确解析源代码并且执行。
- 如果PHP文件编码是UTF-8,则什么都不用做,也不会出现乱码
总结
经过上面简单的测试,已经出现了这么多的问题,因此平时做开发真的不要用非UTF-8的编码来写PHP代码!
后续需继续研究的问题:
- 使用UTF-16、UTF-32编码,为什么开启zend.multibyte后,无需像GBK编码那样添加
declare(encoding="...")
? - 使用GBK编码,为什么中文var_dump出来会乱码,而写入文件后再查看却不会乱码?
- UTF-8跟GBK一样,都属于多字节的编码,为什么UTF-8在没有开启zend.multibyte的情况下,输出中文不会乱码,而GBK会乱码?而且还是在windows命令行环境默认是GBK编码的情况下,就更加诡异