«
»
简介
“PHP 的特性” 简单介绍了 PHP V6 中的新特性。在本文中,了解这些让 PHP V6 更加容易使用、更加安全和更加适合国际化的新特性。本文探索改进的 Unicode 支持,删除了几个函数、改进的扩展、引擎添加内容、改进的面向对象函数和 PHP V6 中的一些扩展。
增强对 Unicode 的支持
PHP V6 中的主要特性是增强对 Unicode 的支持。目前,PHP 实际上是一个二进制处理器。PHP V5 没有提供原生的 Unicode 支持;它假定所有字符的长度都为 1 字节,这在处理非拉丁字符时会出现问题。您可以转换到 Unicode,但需要使用 mbstring 扩展,而默认的 PHP V5 或外部工具(比如 iconv)都不支持该扩展。
PHP V5 有时不能正确显示文本,这取决于字符编码。Unicode 是由 Unicode Consortium 开发的行业标准,它能表示所有语言、程序和平台上的每一个字符。各个行业和标准都广泛支持 Unicode,这使得实现国际化多语言应用程序成为可能。Unicode 可以使用多种字符编码表示,最常见的是 UTF-8、UTF-16 和 UTF-32。
PHP V6 原生地支持 Unicode (UTF-8),因此它的引擎、扩展和 API 都支持 Unicode。尽管已经添加将所有二进制字符串作为不同类型处理的支持,PHP V6 仍然能够将字符串作为 Unicode 处理。在 PHP V6 中,字符串的字面量为 Unicode,允许使用 Unicode 标识符,并且它的函数能够理解 Unicode 文本。PHP V6 能够在需要时支持 Unicode 字符串和其他编码之间的转换,并支持对 UTF-8 文件进行读写。下面给出一个从 UTF-8 文件读取内容的例子:
$input = fopen('inputfile.txt', 'rt');
$str = fread($input, 100); // returns 100 Unicode characters
向 UTF-8 文本文件写一个 Unicode 字符串 $uni:
$output = fopen('outputfile.txt', 'wt');
fwrite($fp, $uni); // writes out data in UTF-8 encoding
Unicode 模式
在 PHP V5 中,可以根据需求将 Unicode 模式设置为打开或关闭。这表明非 Unicode 和 Unicode 变体类、方法和函数名必须存储在符号表中,这导致更多的开销。PHP V6 在 php.ini 中提供一个服务器通用的配置设置,以启用或禁用 Unicode 模式。
Opcode 缓存用于缓存已编译的 PHP 代码。
服务器通用的配置使引擎的某个部分的实现更加容易,引起的 opcode 缓存问题更少,并且提高运行速度,因为不需要在运行时转换符号名。
PHP V6 保留了禁用 Unicode 模式的选项,因为一些字符串函数的 Unicode 实现变慢了 300%,这使整个应用程序的速度变慢 25%。PHP V6 在 php.ini 中提供一个运行时配置选项,用于启用或禁用 Unicode 语义。默认设置为 on。
unicode.semantics = on
将 unicode.semantics 设置为 off 并不意味着不使用 Unicode。当设置为 off 时,您仍然可用访问 Unicode 特性。当 Unicode 语义设置为 off,字符串的字面量是 8 位的字符串;1 个字符等于 1 个字节。
unicode.semantics = off
$str = "Hello, world!";// ASCII encoding
echo strlen($str);//result is 13
如果设置为 unicode.semantics=on,那么字符串字面量使用 Unicode 类型。当 Unicode 语义设置为 on 时,一个字符可能 > 1 个字节。
unicode.semantics = on
$str = "Hello, world!";// Unicode string
echo strlen($str);// result is 13
PHP V6 中的 unicode.runtime_encoding 配置选项指定在运行时执行 Unicode 和二进制字符串之间的转换时使用哪种编码。例如,将运行时编码设置为 iso-8859-1。
unicode.runtime_encoding = iso-8859-1
当与尚未支持 Unicode 的函数连接时,仍然需要使用运行时编码。PHP 脚本可以采用各种编码方式。PHP V6 提供 unicode.script_encoding 配置选项来指定脚本的编码。不管脚本的编码是什么,生成的字符串字面量都为 Unicode 类型。
unicode.script_encoding = iso-8859-1
$uni = ""; // Unicode string
unicode.script_encoding = utf-8
$uni = "Atildel"; // also Unicode string
您还可以将 declare() 语句作为 PHP 脚本的第一个语句,这样也可以设置脚本的编码。declare() 构造器覆盖 php.ini 设置。declare() 设置不会传播到文件。
unicode.script_encoding = utf-8
declare(encoding="iso-8859-1");
$uni = "Atildel";// read as ISO-8859-1 string
include "inputfile.php";// file is read as UTF-8
unicode.output_encoding 配置选项指定标准输出流所使用的编码,包括 echo、print 和 var_dump() 函数。输出流的编码被即时转换。unicode.output_encoding 配置选项不影响二进制字符串。
unicode.output_encoding = utf-8
unicode.script_encoding = iso-8859-1
$unicode = "Atildel"; // Unicode string (from ISO-8859-1)
echo $unicode; // converts $unicode to UTF-8 encoding
echo b"Atildel"; // no conversion, raw contents
如果启用了 Unicode 语义,HTTP 输入必须转换为 Unicode。GET 请求没有编码,并且也很少指定 POST 请求的编码。PHP V6 提供 unicode.http_input_encoding 配置选项来指定将 HTTP 输入转换为哪种 Unicode 编码。例如,将 HTTP 输入转换为 UTF-8:
unicode.http_input_encoding = utf-8
PHP 将尝试根据 unicode.http_input_encoding 设置进行解码。如果解码失败,PHP 将使用原始的二进制数据填充请求数组。unicode.filename_encoding 配置选项指定文件系统上文件和目录名的编码。
unicode.filename_encoding = utf-8
当输入和输出文件名时,与文件系统相关的函数执行所需的转换。PHP V6 提供 unicode.fallback_encoding 配置选项来指定 fallback 编码。
unicode.fallback_encoding = iso-8859-1
当没有给其他编码分配值时,将使用 fallback 编码。fallback 编码的默认值是 UTF-8。在 PHP V6 中,您可以使用不同的字符编码;当启用 unicode.semantics 选项时,PHP V6 将所有字符串字面量转换为 Unicode 字符串。您可以无缝地对输入和输出使用不同的字符。PHP V6 根据 php.ini 中指定的配置选项对 HTTP 输入和输出进行编码。数据库和用户代理程序能够收到所需的字符编码,而不需使用任何转换函数。
您不一定要使用 Unicode 开发脚本来处理和输出 Unicode,但我们推荐这么做。如前所述,通过 unicode.script_encoding 配置选项指定脚本编码。为了在数据中(比如 MySQL)存储 Unicode 数据,数据库不需要配置为 UTF-8 编码,但这样做比较好。MySQL 数据库被配置为运行不同的字符集,PHP V6 将以 Unicode 编码发送查询,而 MySQL 将尝试转换它。
两种字符串类型
PHP V5 的字符串类型实现有很多字符串类型(二进制、字符串和 Unicode),并且有很多内部引擎函数和帮助函数的实现。PHP V6 只有两种字符串类型:二进制和 Unicode。unicode.semantics 转换(switch)决定字符串字面量的默认类型。如果启用了 unicode.semantics,默认的字符串字面量就为 Unicode。如果禁用该语义转换,默认字符串字面量的类型就为二进制。您可以显式地在 Unicode 和二进制字符串类型之间转换,也可以隐式地进行转换。使用 unicode.runtime_encoding 配置选项进行隐式转换。表 1 显示了如何进行显式转换。
表 1. 转换字符串类型
类型转换 描述
(binary) 转换到二进制字符串类型
(unicode) 转换到 Unicode 字符串类型
(string) 如果 unicode.semantics 设置为 on,就转换到 Unicode 字符串类型,否则转换到二进制字符串类型
PHP V6 在内部使用 IS_STRING 表示二进制字符串数据,以及使用 IS_UNICODE 表示 Unicode 字符串数据。
在扩展中支持 Unicode
PHP V6 包含了 Unicode/API,扩展可以借助它实现 Unicode 支持。需要向扩展模块结构添加一个标志,以表明它是否支持 Unicode。如果扩展不支持 Unicode,并且在 PHP 中启用了 Unicode 语义,那么在启动时将不加载它。Unicode 支持被添加到 PHP 数据对象中 (PDO)。
ICU 库
International Components for Unicode (ICU) 是一组提供 Unicode 和全球化支持的库。PHP V6 需要使用 ICU,但 ICU 是一个大型的库,将增加 PHP 的下载体积。PHP 要求使用 ICU V3.4,并且要求发布版提供 ICU 库。
文件名编码
不同的文件名可能使用不同的字符集进行编码。例如,Windows 文件名使用 UTF-16 编码。filename_encoding 设置指定期望的文件名编码。当 read 函数(比如 readdir() 和 readfile())遇到其编码在 filename_encoding 中不存在的文件名时,将返回一个二进制字符串。
缓存校对器
校对器(Collator)用于比较字符串。打开和关闭校对器需要引入开销。PHP V6 缓存了校对器,但限制存储在缓存中的对象数量,以防止使用过多的内存。PHP V6 在线程/进程通用缓存中存储最近打开的 3 个校对器。
基于位置的函数
对于使用系统位置的 PHP 函数,由于位置名在不同的平台中不一致甚至不可用,因此这是一个问题。PHP V6 仅在合适的地方使用基于位置的函数。基于位置的函数包括 strtoupper/strtolower 和 stristr 等。strtoupper 函数将字母表中的字符都转换为大写的。“字母表的字符顺序” 由当前的位置决定。
字符集转换错误
在字符编码之间执行转换可能发生转换错误,因为并不是源字符串中的所有字符都有必要存储在目标字符串中。PHP V6 为字符集转换失败提供一个额外的错误模式,它会在失败时抛出异常。PHP V6 为处理转换错误引入了两个新的 php.ini 配置选项:unicode.from_error_mode 和 unicode.from_error_subst_char。
unicode.from_error_mode = U_INVALID_SUBSTITUTE
unicode.from_error_subst_char = 3f
unicode.from_error_subst_char 选项为无效的字符串指定了代替字符串的十六进制值;它的默认值是 3f。
unicode.from_error_mode 选项指定错误模式,它在遇到无效字符串时采取行动。表 2 显示了 unicode.from_error_mode 可能使用的值。
表 2. unicode.from_error_mode 值
常量 值 说明
U_INVALID_STOP 0 遇到第一个无效字符时停止
U_INVALID_SKIP 1 跳过无效字符
U_INVALID_SUBSTITUTE 2 替换无效字符;默认值
U_INVALID_ESCAPE 3 对无效字符进行转义
删除了几个函数
这个小节概述了在 PHP V6 中删除的几个函数。
删除了 register_globals
PHP V5 在 php.ini 中使用 register_globals 配置选项从环境和 HTTP 请求参数定义变量。不过,register_globals 生成不安全的代码。因此,在 PHP V6 中删除了 register_globals。
register_globals 生成不安全代码的方式之一是 authenticate.php,它使用 $auth 变量对用户进行身份验证。因为 $auth 没有被初始化,所以它的值可能通过使用 register_globals 注入变量来定义,比如 GETauthenticate.php?auth=1。
<?php
if (authenticated_user()) {
$auth = true;
}
if ($auth) {
include "/data.php";
}
?>
register_globals 注入的变量也可能在会话中生成不安全的代码。看看以下代码,当设置了 $username 变量时,将在用户会话中显示一条欢迎消息。但是,当启用 register_globals 时,可能会使用 HTTP 请求参数注入 $username 变量。
<?php
if (isset(