UTF-8文件中的PHP源代码; 如何正确解读?

Ira*_*ter 4 php code-analysis utf-8 iso-8859-1

我构建了分析源代码的工具.这些工具必须正确读取源代码文件,特别是在字符编码方面.例如,"字符串文字中的精确字节串是什么?" (PHP文字和HTML文本).

我可能错误的理解是PHP源文件只是8位字符(也就是说,PHP引擎以这种方式读取它们,因为它们只应该包含8位字符).但是,八位字符在哪个编码?(我认为打算匹配ISO-8859-1(-x?)[有人可以引用章节和经文吗?].也就是说,变音符号是一个变音符号,对吗?接下来,可以用HTML编写PHP脚本和大多数欧洲国家的字符串/字符集直接.

但很明显,Unicode存在问题.据我所知,大多数PHP应用程序主要通过包含UTF-8字节序列的字符串来处理Unicode,这些字符串可以插入8位PHP字符串中.在此之后,如果您告诉服务器您正在生成UTF-8文本,则可以生成其HTML包含Unicode UTF-8序列的脚本.

对于上述情况,可以将PHP文件读取为8位字符文本,这似乎与我的语言相匹配.

令我困惑的是编码为UTF-8的PHP源文件(Joomla软件包有大约1800个源文件,其中大约10个是UTF-8,其余的不是).在UTF-8渲染中正确显示的任何(非ASCII)欧洲字符实际上都被编码为多字节序列.我认为这些用作UTF-8的页面将正确呈现HTML.但是,在文本编辑器中显然正确呈现的欧洲字符或其他Unicode字符的任何字符串比较都是行不通的.字符串文字不包含它们似乎包含的内容.程序员是否使用UTF-8文件,因为这是编辑提供的?他们是故意这样做的吗?或者只是一场对大多数工作无关紧要的事故?

那么,如何读取PHP源文件呢?(特别是,在什么字符编码?)一个可能的答案是,始终作为ISO-8859-1 8位代码,无论实际内容或BOM(我看到很多UTF-8 BOM标记的PHP文件).另一个答案是UTF-8,如果这样标记的话.

[我们的工具读写任意编码."普通"工具是读取文件一个字符的编码,在另一个编码中写入相同的代码点.以这种方式读取UTF-8 PHP文件会让我们在编写ISO8859-1等效文件时遇到麻烦,因为许多UTF-8代码点(例如,欧元符号)无法在ISO8859-x中编码.

编辑8月30日:我们现在检查PHP文件以查看是否具有UTF-8 BOM,或者看起来是否具有合法的UTF-8序列.在这两种情况中,我们将文件读为UTF-8; 否则我们默认将其读作ISO8859-1.我们现在保留文件编码,如果我们修改它.(正确的做法是相当多的工作).这似乎是一种安全策略,但这可能与PHP程序员所期望的不同.

boe*_*bot 8

TL; DR

ASCII


在PHP 5.4之前,PHP解释器根本不关心PHP文件的字符集,这可以从zend.script_encoding ini指令仅出现在该版本中得到证明.它总是将它视为ASCII.

当PHP需要识别,例如,一个函数名称,恰好包含超出ASCII-7bit的字符(好吧,任何带有任何标签的标签实体,但你得到我的观点...),它只是寻找一个函数具有相同字节序列的符号表 - 以一种方式编写的变音符号(或其他......)将被视为与以另一种方式编写的变音符号不同.试试吧.为了向后兼容,如果未设置zend.script_encoding,则这仍然是默认行为.另请注意正则表达式显示什么是有效标识符,您可以看到charset为中性(嗯...除了拉丁字母,它们在ASCII-7bit范围内),但显示字节.

这也引导我们使用declare(encoding)构造.如果你在一个文件中看到那个,那就是那个特定文件的权威字符集(仅限).使用别的东西,直到你遇到一个,如果你看到多个 - 在声明声明后尊重第二个.

如果没有......

在静态上下文中(即,当您不知道有效的ini设置时),当字符集很重要时,您需要回退到其他东西(理想情况下是用户定义的东西),否则只需处理ASCII-7bit以外的字符作为纯二进制,并以一些统一的代码点方式显示它们.

在动态上下文中(例如,如果您可以重新命名文件片刻,请在该位置创建一个临时文件,使用该名称;让它回显zend.script_encoding的值;恢复正常文件),您应该使用zend.script_encoding值如果可用,则回退到其他东西(就像在静态上下文中一样).

相同的处理适用于字符串,HTML片段和PHP文件的任何其他内容 - 它只是作为二进制字符串读取,除了某些对PHP词法分析器很重要的ASCII字符(即字节),例如序列"<?php "(注意所有都是ASCII字符......); 单引号中的撇号; 等等 - 解释器本身并不关心字符串的字符集,如果必须在屏幕上显示字符串的内容,则应使用上述方法找出最佳方法.


边缘情况(在评论中要求):

  1. 对允许的编码有限制吗?

    似乎没有任何允许的编码列表,或者至少我找不到一个.鉴于这是--enable-zend-multibyte编译设置的后续版本,所有版本的UTF编码肯定都在该列表中.即使其他(ANSI)编码对PHP本身没有影响,也不应该阻止您将该值用作提示.

  2. 如果源文件是UTF-16(声明的8位ascii字符之间的空8位字节),"声明(编码)"如何工作?

    使用zend.script_encoding直到遇到声明(编码).如果未设置,则假定为ASCII.即使在UTF-16文件中这也不应该是一个问题......对吗?(我不使用UTF-16)

  3. 如果.ini或文件设置是UTF-8或其他,那么标识符可能只取自x41-xFF范围内的代码点,而不是代码点x100 up?

    我没有尝试提供无效的UTF-8字节来告诉你那个问题的答案,手册也没有提出任何问题.我会假设PHP执行失败,并在其上发生解析错误.或者至少它应该.就你的工具而言,它应该报告无效的UTF-8序列,因为即使PHP允许它,这仍然是QA问题.

  4. 对于UTF编码,字符串中的字符是否表示为其UTF代码点(这没有意义,因为PHP字符串似乎只有8位字符)?

    否.字符串和非PHP内容中的字符仍被视为一个字节序列,您可以通过查看strlen()的输出来确认它,并查看它与mb_strlen()的区别,后者是尊重的编码(嗯......它确实尊重mbstring.internal_encoding设置,但仍然).

  5. 如果没有,将编码设置为UTF是什么意思?

    AFAIK,它会影响符号表中的查找.使用UTF设置,变音符号以不同的方式编写,或以不同的UTF风格编写,最终使用相同的UTF代码点...它们都会聚合在相同的符号上,而不是没有声明(编码),其中逐字节字节比较完成了.我在这里说"AFAIK",因为坦率地说,我自己从未使用过这样的实验......我是"做得好"的一切 - 有效 - UTF-8'-er".

  • "即使在UTF-16文件中,这也不应该成为问题......对吧?" 错误.您不能读取UTF-16,就像它是单字节一样,它是一个双字节(和四字节)编码 (3认同)

Lor*_*yer 7

由于已经多次重复,PHP文件对x7f以上的字节没有任何编码.所有你能说的是字节x00到x7f是ascii.

开头带有BOM标记的文件不是有效的PHP.所以在iso-8859-1或utf-8中没有像PHP文件那样的东西.它很简单,只有8位.

PHP文件不是iso-8859-x,因为这些编码不包含所有可能的字节值.如您所知,x7f到x9f在iso-8859-1中无效,但任何PHP文件都可能包含它们.

PHP文件也不是utf-8,因为它可能包含无效的utf-8序列,而不是无效.

大局

Charset按惯例写作

PHP文件可以按照惯例进行编码,但这取决于程序员的判断.他会告诉他的编辑,这样的项目是在utf-8或iso-8859-1或其他什么.

但同样,这只是程序员的惯例.他的编辑器正在威胁PHP文件,好像它就是这样的编码.编码仅用于在编辑器中显示文件的目的,并允许程序员编辑它.

编译期间没有字符集

如上所述,编译器不需要知道程序员假设的编码.唯一重要的是文件中的字节序列是什么.

消费定义的隐式或显式字符集

PHP生成一些通过Internet发送到浏览器的数据.在浏览器显示数据时,编码是明确定义的,但如何?

  • 可以在HTTP头中定义编码,如下所示 Content-Type: text/html; charset=utf-8
  • 它可以在HTML输出本身中定义: <meta charset="utf-8">
  • 或者,如果未明确定义字符集,则浏览器根据文档中存在的字节序列进行有根据的猜测(例如,有效的utf-8序列或BOM).

当然,优秀的做法是PHP应用程序永远不会让浏览器选择,但不要求在任何地方定义编码.

更多细节

通常,程序员选择的编码与将在浏览器链的末尾使用的编码相同,并且PHP文件中的所有字符串都将使用相同的编码.

但事实并非如此.有正当理由,为什么不是这种情况.我们来看看例子:

不同的语言,不同的编码

我使用Joomla,因为它的版本是1.0.在此版本中,语言文件各自拥有自己的编码.法语是iso-8859-1,而阿拉伯文件是windows-1256和俄语文件koi8-r.对于那些重要的编码,但不是所有其他文件,可以同等对待为utf-8或iso-5598-1.(与此同时,Joomla改用utf-8.)

异构数据库

我们的一个Web应用程序连接到两个不同的数据库,一个恰好在utf-8中,另一个在windows-1252中.这意味着,此项目中的所有字符串都不是相同的编码.我尽可能使用utf-8,但我需要使用mb_*PHP中的一组函数来回填充编码.

PHP的转换功能

仅仅的编码转换功能的存在mb_convert_encoding,iconv,utf8_encode等表明,在不同的编码的同一个项目的字符串可以存在.

好的做法

定义你的编码并坚持下去!最好的选择是使用utf-8.如果需要其他编码的其他字符串,你总是可以编写类似的东西$s=mb_convert_encoding('???????','ucs-2','utf8');

这里再次说明:您不能在PHP中使用BOM标记.原因很简单:BOM标记是两个字节,位于开始标记之前<?php.因此它们被发送到浏览器.如果header()之后尝试发送,则会生成错误,并且不会发送标头.

结论

  • 通常,不需要确定PHP文件的编码.只有最终呈现的HTML文件的编码才是重要的.
  • 最好使用用于显示最终结果的相同编码编辑所有文件.但它真的只对语言文件有用(如果你使用任何i18n系统).
  • 虽然在实践中,一个文件中的所有字符串都采用相同的编码,但是没有什么可以让一个心不在焉的程序员在同一个文件中以不同的编码编写字符串,并且仍然可以得到一个工作程序.

最后,在PHP中进行编码只是在写入时使用的约定,以及在浏览器中用于呈现页面的字符集.在这两者之间,PHP文件没有特定的编码,它只是简单的8位.