Am I correctly supporting UTF-8 in my PHP apps?
我想确保我所知道的关于UTF-8的一切都是正确的。我已经尝试使用UTF-8有一段时间了,但是我不断遇到越来越多的bug和其他奇怪的东西,这使得拥有一个100%的UTF-8站点几乎是不可能的。总有一个我似乎怀念的地方。也许这里的人可以更正我的列表,或者确定它,这样我就不会错过任何重要的事情。
数据库
每个站点都必须将数据存储在某个地方。无论您的PHP设置是什么,您还必须配置数据库。如果无法访问配置文件,请确保在连接后立即"设置名称"为"utf8"。另外,确保在所有表上使用utf8 uuunicode uci。这假设MySQL是一个数据库,您必须为其他数据库进行更改。
正则表达式
我做了很多regex,比一般的搜索替换更复杂。我必须记住使用"/u"修饰符,这样PCRE就不会损坏我的字符串。然而,即便如此,显然仍然存在问题。
字符串函数
所有默认的字符串函数(strlen()、strpos()等)都应替换为查看字符而不是字节的多字节字符串函数。
报头您应该确保您的服务器返回了正确的头部,以便浏览器知道您要使用什么字符集(就像您必须告诉MySQL一样)。
header('Content-Type: text/html;
charset=utf-8');
将正确的标记放在页头也是一个好主意。尽管实际头文件将覆盖这一点,但如果它们不同。
1 | <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> |
问题
当页面加载时,我是否需要将从用户代理(HTML表单的&uri)接收到的所有内容转换为UTF-8,或者如果我可以保留字符串/值的原样,并且仍然在这些函数中运行它们而不会出现问题?
如果我确实需要将所有内容转换为UTF-8,那么应该采取什么步骤?mb-detect-u编码似乎是为此而构建的,但我一直看到人们抱怨它并不总是有效。mb_check_编码似乎也有一个问题,告诉一个良好的utf-8字符串从一个畸形的。
根据使用的编码(如文件类型),PHP在内存中存储字符串的方式是否不同,或者它是否仍然像常规的sting一样存储,其中一些字符的解释方式不同(如&;amp;vs&;in html)。chazomaticus回答此问题:
In PHP (up to PHP5, anyway), strings
are just sequences of bytes. There is
no implied or explicit character set
associated with them; that's something
the programmer must keep track of.
如果给一个mb_*函数一个非utf-8字符串,它会不会导致问题?
如果utf字符串编码不正确,会出现问题(比如regex中的解析错误?)还是只将一个实体标记为坏(HTML)?是否有可能由于字符串不正确而导致函数返回false?
我听说您也应该将表单标记为utf-8(接受charset="utf-8"),但我不确定它的好处是什么……
用UTF-8写UTF-16是为了解决一个限制吗?就像utf-8没有足够的字符空间?(Y2(UTF)K?)
功能
下面是一些我已经找到的自定义PHP函数,但我没有任何方法来验证它们是否真正有效。也许有人有一个我可以用的例子。首先是converttoutf8(),然后看起来像是WordPress的utf8。
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | function seems_utf8($str) { $length = strlen($str); for ($i=0; $i < $length; $i++) { $c = ord($str[$i]); if ($c < 0x80) $n = 0; # 0bbbbbbb elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b else return false; # Does not match any model for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ? if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) return false; } } return true; } function is_utf8($str) { $c=0; $b=0; $bits=0; $len=strlen($str); for($i=0; $i<$len; $i++){ $c=ord($str[$i]); if($c > 128){ if(($c >= 254)) return false; elseif($c >= 252) $bits=6; elseif($c >= 248) $bits=5; elseif($c >= 240) $bits=4; elseif($c >= 224) $bits=3; elseif($c >= 192) $bits=2; else return false; if(($i+$bits) > $len) return false; while($bits > 1){ $i++; $b=ord($str[$i]); if($b < 128 || $b > 191) return false; $bits--; } } } return true; } |
如果有人感兴趣,我发现了一个很好的示例页面,可以在测试UTF-8时使用。
Do I need to convert everything that I receive from the user agent (HTML form's & URI) to UTF-8 when the page loads
号的用户代理的数据应该在submitting UTF-8格式;如果不是你失去效益(Unicode。
的方式确保一个用户代理submits在UTF-8格式的冰的含两个发球页的形式在它的submitting UTF-8编码。使用"内容类型标头(HTTP等价和元。如果你intend的形式保存了两个独立的和工作)。
I have heard that you should mark you forms as UTF-8 also (accept-charset="UTF-8")
不,不。这是一个很好的想法,在IE的HTML标准,但没有得到它的权利。这两个国家应该"容许字符集的列表,但它作为一个单位treats战略两个额外的字符集是一个尝试,每一场的基础上。所以,如果你有一个ISO 8859-1"页和接受字符="UTF-8"的形式,即试图将第一编码一场为ISO-8859-1,如果有一非8859-1字符在那儿,然后它会两个utf -度假村8。
但由于IE不告诉你它是否使用ISO 8859-1或UTF-8的,那绝对没有用的你。我想你会有两个,分别为每个字段的编码,这是在使用!不是有用的。《omit属性和服务你的页面为UTF-8;那真的可以做最好的时刻。
If a UTF string is improperly encoded will something go wrong
如果你很容易通过一个这样的序列的两个浏览器,你可能是在麻烦。的意思是"overlong序列的编码方式,低碳的概念,在一个代码点冰的字节序列(不是必要的。如果你在这均值滤波的"<"城市,寻找在一个ASCII字符(字节序列,你可能错过一A,和易脚本元素到你的思想是安全的文本。
overlong序列是banned回来的早一天的Unicode,但它把微软的一个很长的时间让他们两人在一起也会interpret的字节序列的xc0 XBC。"作为一个群集的IP服务包1直到IE6。歌剧也得到了它错误的IP(约两个版本,我认为)7。luckily这些年长的browsers是死了,但它还是值得的overlong滤波序列在这些案例browsers是(现在仍然是browsers化妆或新的白痴一样的错误在未来)。你可以这样做,对方的要求和固定的序列,和一个正则表达式,只允许适当的UTF - 8通,如这一个虔诚的.
如果你在使用_ MB的PHP函数,你可能会从这些问题是绝缘的。我不能说_酸性AA MB *是脆弱unusable写作当我是静止的PHP。
在任何情况下,这是一个很好的时间也除去控制人物,这是一个大的和普遍unappreciated来源的错误。我chars remove 9和13从submitted加两个字符串的正则表达式的W3的人需要时间;它也为平原和newlines砍掉。你知道不应该是多行文本框。
Was UTF-16 written to address a limit in UTF-8?
utf - 16号,是一个双字节编码,每个代码点的使用Unicode字符串,使密封更容易吗?在存储器(从天,当所有的Unicode将安装在双字节;Windows系统和Java类,仍然做它的方式)。unlike UTF-8格式,它以ASCII兼容的冰槽,小鸭子是个没有用的网站。但你偶尔需要它在档案救一救市,通常Windows用户谁已经misled市Windows的UTF - 16le描述为"Unicode"Save As菜单猫的时间。
seems_utf8
这是非常低效的两个相对的正则表达式。
Also, make sure to use utf8_unicode_ci on all of your tables.
你可以逃跑的实际上是没有这个处理MySQL作为一个大的什么,但他们只字节解释为UTF-8在你的脚本。大学的优势utf8 _使用Unicode _ CI的冰,这将collate(黑鸭子做案例将用于钝感)与知识(非ASCII字符,所以橡树。"?’吗?是相同的字符。如果你使用一个非utf8 collation应该坚持两个二进制(案例匹配敏感)。
你选择whichever,做consistently:使用相同的字符集为你的桌子为你做你的连接。你想避免的是一个有损之间的字符集转换你的脚本和数据库。
你现在所做的大部分都应该是正确的。
一些注意事项:MySQL中的任何
您可以告诉apache和php分别在php.ini的httpd.conf/.htaccess和
您可以告诉mbstring扩展来处理字符串函数。这对我很有用:
1 2 3 4 | mbstring.internal_encoding=utf-8 mbstring.http_output=UTF-8 mbstring.encoding_translation=On mbstring.func_overload=6 |
(这使
对于字符集转换,请查看https://sourceforge.net/projects/phputf8/。
PHP根本不关心变量中的内容,它只是盲目地存储和检索其内容。
如果声明一个
如果你担心有人故意发布错误编码的东西,我相信你应该考虑使用HTML净化器在处理之前过滤获取/发布数据。
UTF-16并不是UTF-8的大哥,它只是起到了不同的作用。
database/mysql:如果你使用的是
客户机通常不指示它发送的参数的编码。如果您希望使用UTF-8编码的数据并将其视为编码错误(在UTF-8中无效的字节序列)。因此,数据可能无法按预期显示,或者解析器可能会中止解析。但至少用户输入不能"转义"并造成更多危害,例如在内联SQL语句或HTML输出中。例如,使用脚本(另存为ISO-8859-1或UTF-8,无所谓)
1 2 3 4 5 6 | <?php $s = 'abcxyz'; var_dump(htmlspecialchars($s, ENT_QUOTES, 'utf-8')); // adding the byte sequence for ??ü in iso-8859-1 $s = 'abc'. chr(0xE4) . chr(0xF6) . chr(0xFC). 'xyz'; var_dump(htmlspecialchars($s, ENT_QUOTES, 'utf-8')); |
印刷品
1 2 | string(6)"abcxyz" string(0)"" |
e4f6fc不是有效的utf-8字节序列,因此htmlspecialchars返回空字符串。其他功能可能会返回?或者另一个"特殊"的角色。但至少他们不会"错误"地将一个字符作为恶意控制字符-只要他们都坚持"正确"的编码(在本例中是UTF-8)。
接受字符集并不能保证只接收使用该编码的数据。据您所知,客户端甚至可能没有"使用"/解析包含表单元素的HTML文档。这可能会有所帮助,而且没有理由不设置该属性。但它不"可靠"。
对于来自表单的用户输入,我将此属性添加到我的
UTF-8很好,而且没有UTF-16解决的任何限制。PHP不会改变在内存中存储字符串的方式(与Python不同)。如果整个数据流使用UTF-8(web表单接收UTF-8数据,表使用UTF 8编码,而您使用的是