处理其他扩展的问题
除了本章已经讨论过的内容外,PHP 8 还对一些 PHP 扩展引入了其他一些值得注意的变化。正如我们在本书中反复强调的那样,了解这些变化对你未来的 PHP 开发职业生涯极为重要。
首先让我们来看看数据库扩展的变化。
新数据库扩展操作系统库要求
任何使用 MySQL、MariaDB、PostgreSQL 或 PHP Data Objects (PDO) 的开发人员都需要了解支持操作系统库的新要求。下表总结了 PHP 8 要求的新的最低版本:
扩展 | 库 | 最小版本 |
---|---|---|
PgSQL |
libpq |
9.1 |
PDO_PGSQL |
libpq |
9.1 |
MySQLi |
libmysqlclient |
5.1 |
PDO_MySQL |
libmysqlclient |
5.1 |
libpq 既影响 PostgreSQL
扩展,也影响 PDO 扩展的驱动程序。libmysqlclient
是 MySQL Improved (MySQLi) 扩展使用的库,也是 PDO
扩展的 MySQL 驱动程序。还应注意的是,如果您使用的是流行的 MySQL 开源版本 MariaDB,新的最低 MySQL 库要求也适用于您。
在了解了数据库扩展的变化之后,我们接下来要关注的是 ZIP 扩展。
检查 ZIP 扩展名的更改
ZIP 扩展用于利用 libzip 操作系统库,以编程方式创建和管理压缩归档文件。目前还存在其他压缩扩展,如 Zlib、bzip2、LZF、PHP Archive Format (phar) 和 Roshal Archive Compressed (RAR);但是,没有一个其他扩展能提供 ZIP 扩展所提供的丰富功能。此外,其他扩展大多具有特殊用途,一般不适合用于一般的 ZIP 文件管理。
让我们先来看看该扩展最显著的变化。
处理 ZIP 扩展 OOP 迁移
ZIP 扩展的最大变化是可能会导致大量向后兼容的代码中断。从 PHP 8 开始,过程 API(所有过程函数)已被弃用!虽然这目前不会影响任何代码,但所有 ZIP 扩展函数最终都将从语言中删除。
最佳做法是使用 ZipArchive
类将任何 ZIP 扩展程序代码迁移到 OOP API。以下代码示例说明了如何从过程代码迁移到对象代码,打开 test.zip
文件并生成条目列表:
// /repo/ch07/php7_zip_functions.php
$fn = __DIR__ . '/includes/test.zip';
$zip = zip_open($fn);
$cnt = 0;
if (!is_resource($zip)) exit('Unable to open zip file');
while ($entry = zip_read($zip)) {
echo zip_entry_name($entry) . "\n";
$cnt++;
}
echo "Total Entries: $cnt\n";
这是在 PHP 7 中运行的输出:
root@php8_tips_php7 [ /repo/ch07 ]#
php php7_zip_functions.php
ch07/includes/
ch07/includes/test.zip
ch07/includes/tree.xml
ch07/includes/test.png
ch07/includes/kitten.jpg
ch07/includes/reflection.html
ch07/php7_ext_is_resource.php
ch07/php7_gd_is_resource.php
... not all entries shown ...
ch07/php8_simple_xml.php
ch07/php8_xml_writer.php
ch07/php8_zip_oop.php
Total Entries: 27
从前面的输出中可以看到,总共找到了 27 个条目。(如果我们在 PHP 8 中尝试同样的代码示例,就会得到截然不同的结果:
root@php8_tips_php8 [ /repo/ch07 ]#
php php7_zip_functions.php
PHP Deprecated: Function zip_open() is deprecated in /repo/
ch07/php7_zip_functions.php on line 5
PHP Deprecated: Function zip_read() is deprecated in /repo/
ch07/php7_zip_functions.php on line 8
PHP Deprecated: Function zip_entry_name() is deprecated in /
repo/ch07/php7_zip_functions.php on line 9
ch07/includes/
Deprecated: Function zip_entry_name() is deprecated in /repo/
ch07/php7_zip_functions.php on line 9
... not all entries shown ...
ch07/php8_zip_oop.php
PHP Deprecated: Function zip_read() is deprecated in /repo/
ch07/php7_zip_functions.php on line 8
Total Entries: 27
从前面的 PHP 8 输出中可以看到,代码示例可以运行,但发出了一系列 "报废通知"(deprecation Notice)。
下面是在 PHP 8 中编写相同代码示例的方法:
// /repo/ch07/php8_zip_oop.php
$fn = __DIR__ . '/includes/test.zip';
$obj = new ZipArchive();
$res = $obj->open($fn);
if ($res !== TRUE) exit('Unable to open zip file');
for ($i = 0; $entry = $obj->statIndex($i); $i++) {
echo $entry['name'] . "\n";
}
echo "Total Entries: $i\n";
输出结果(未显示)与上一个示例完全相同。有趣的是,重写的示例在 PHP 7 中也能运行!值得注意的是,在 PHP 8 中,您可以使用 ZipArchive::count()
获取条目的总数(每个目录)。您可能还注意到,要检查 ZIP 压缩包是否已正确打开,在 PHP 8 中不能再使用 is_resource()
。
新的 ZipArchive 类方法
除了资源到对象的迁移,ZipArchive
类还做了许多改进。其中一项改进是添加了以下新方法:
-
setMtimeName()
-
setMtimeIndex()
-
registerProgressCallback()
-
registerCancelCallback()
-
replaceFile()
-
isCompressionMethodSupported()
-
isEncryptionMethodSupported()
方法名称不言自明。Mtime
指 修改时间。
addGlob() 和 addPattern() 的新选项
ZipArchive::addGlob()
和 ZipArchive::addPattern()
方法新增了一组选项。这两个方法很相似,都用于向压缩包中添加文件。不同之处在于,addGlob()
使用与核心 PHP glob()
命令相同的文件模式,而 addPattern()
则使用 regex 过滤文件。这里总结了新的选项集:
-
flags:让你使用位运算符组合适当的类常量
-
comp_method:使用任意
ZipArchive::CM_*
常量作为参数指定压缩方法 -
comp_flags:使用所需的
ZipArchive::FL_*
常量指定压缩标志 -
enc_method:让您指定所需的字符编码(使用任何
ZipArchive::FL_ENC_*
标志) -
enc_password:如果已为该 ZIP 压缩包设置了加密密码,可用于指定该密码
值得一提的是,在 PHP 8 之前,remove_path
选项必须是一个有效的目录路径。从 PHP 8 开始,该选项是一个简单的字符串,表示要删除的字符。这样就可以删除文件名前缀和不需要的目录路径。
虽然我们仍在研究选项,但值得注意的是,我们添加了两个新的编码方法类常量:ZipArchive::EM_UNKNOWN
和 ZipArchive::EM_TRAD_PKWARE
。此外,还添加了一个新的 lastId
属性,以便确定最后一个 ZIP 压缩包条目的索引值。
其他 ZipArchive 方法更改
除了前面提到的变化外,PHP 8 中还有一些其他 ZipArchive
方法发生了变化。 在本节中,我们将总结其他 ZipArchive
方法的变化如下:
-
ZipArchive::extractTo() 以前使用当前日期和时间作为修改时间。从 PHP 8 起,该方法恢复了原始文件修改时间。
-
即使在调用 ZipArchive::close() 后,ZipArchive::getStatusString() 仍会返回结果。
-
ZipArchive::addEmptyDir()、ZipArchive::addFile() 和 ZipArchive::addFromString() 方法都有一个新的 flags 参数。你可以使用任何适当的
ZipArchive::FL_*
类常量,并使用位运算符进行组合。 -
ZipArchive::open() 现在可以打开空(零字节)文件。
现在,您对 ZIP 扩展引入的更改和改进有了一定的了解,让我们来看看正则表达式方面的更改。
检查 PCRE 扩展更改
PCRE 扩展包含大量旨在使用正则表达式执行模式匹配的函数。正则表达式通常简称为 regex。regex 是描述另一个字符串的字符串。下面是 PCRE 扩展中值得注意的一些变化。
模式中的无效转义序列不再被解释为字面意义。过去可以使用 X 修饰符,但现在 PHP 8 中忽略了该修饰符。 值得高兴的是,为了帮助处理 PCRE 模式分析的内部错误,新增了 preg_last_error_msg()
函数,当遇到 PCRE 错误时,该函数会返回一条人可读的信息。
通过 preg_last_error()
函数,可以确定在模式分析过程中是否发生了 PCRE 内部错误。但该函数只返回一个整数。在 PHP 8 之前,开发人员需要查找代码并找出实际错误。
preg_last_error() 返回的错误代码列表可以在这里找到: https://www.php.net/manual/en/function.preg-lasterror.php#refsect1-function.preg-last-errorreturnvalues |
-
下面的代码示例简要说明了上述问题。以下是实现这一目标的步骤:
$pregTest = function ($pattern, $string) { $result = preg_match($pattern, $string); $lastErr = preg_last_error(); if ($lastErr == PREG_NO_ERROR) { $msg = 'RESULT: '; $msg .= ($result) ? 'MATCH' : 'NO MATCH'; } else { $msg = 'ERROR : '; if (function_exists('preg_last_error_msg')) $msg .= preg_last_error_msg(); else $msg .= $lastErr; } return "$msg\n"; };
-
然后,我们创建一个故意包含 \8+ 无效转义序列的模式,如下所示:
$pattern = '/\8+/'; $string = 'test 8'; echo $pregTest($pattern, $string);
-
接下来,我们定义了一种故意使 PCRE 超过其回溯限制的模式,如下所示:
$pattern = '/(?:\D+|<\d+>)*[!?]/'; $string = 'test '; echo $pregTest($pattern, $string);
下面是 PHP 7.1 的输出结果:
root@php8_tips_php7 [ /repo/ch07 ]# php php7_pcre.php
RESULT: MATCH
ERROR : 2
从前面的输出中可以看出,无效模式被视为字面值 8。因为字符串中存在 8,所以认为找到了匹配。至于第二个模式,则超出了回溯限制;然而,PHP 7.1 无法报告这个问题,迫使您去查找。
在 PHP 8 中的输出则完全不同,如下所示:
root@php8_tips_php8 [ /repo/ch07 ]# php php7_pcre.php
PHP Warning: preg_match(): Compilation failed: reference to
non-existent subpattern at offset 1 in /repo/ch07/php7_pcre.php
on line 5
ERROR : Internal error
ERROR : Backtrack limit exhausted
从前面的输出中可以看出,PHP 8 产生了一条警告信息。还可以看到 preg_last_error_msg()
产生了一条有用的信息。现在我们来看看国际化(Intl)扩展。
处理 Intl 扩展变更
Intl
扩展由多个类组成,用于处理可能会因地域不同而发生变化的多个应用程序方面的问题。这些类处理的任务包括国际化数字和货币格式化、文本解析、日历生成、时间和日期格式化以及字符集转换等。
在 PHP 8 中,Intl
扩展引入的主要变化是以下新的日期格式:
-
IntlDateFormatter::RELATIVE_FULL
-
IntlDateFormatter::RELATIVE_LONG
-
IntlDateFormatter::RELATIVE_MEDIUM
-
IntlDateFormatter::RELATIVE_SHORT
下面的代码示例展示了新格式。下面是实现这一点的步骤:
-
首先,我们定义一个
DateTime
实例和一个包含新格式代码的数组,如下所示:$dt = new DateTime('tomorrow'); $pt = [IntlDateFormatter::RELATIVE_FULL, IntlDateFormatter::RELATIVE_LONG, IntlDateFormatter::RELATIVE_MEDIUM, IntlDateFormatter::RELATIVE_SHORT ];
-
然后,我们对格式进行循环,并回显输出结果,如下所示:
foreach ($pt as $fmt) echo IntlDateFormatter::formatObject($dt, $fmt)."\n";
此示例在 PHP 7 中不起作用。下面是 PHP 8 的输出结果:
root@php8_tips_php8 [ /repo/ch07 ]#
php php8_intl_date_fmt.php
tomorrow at 12:00:00 AM Coordinated Universal Time
tomorrow at 12:00:00 AM UTC
tomorrow, 12:00:00 AM
tomorrow, 12:00 AM
正如您所看到的,新的相对日期格式工作得很好!现在我们简单地回到 cURL
扩展。
了解 cURL 扩展更改
cURL 扩展利用 libcurl ( http://curl.haxx.se/ ) 提供强大而高效的 HTTP 客户端功能。在 PHP 8 中,服务器操作系统必须安装 libcurl 7.29(或更高版本)。
PHP 8 中的另一个不同之处是,该扩展现在使用对象而不是资源。本章前面的表 7.1(PHP 8 资源到对象的迁移)介绍了这一变化。在涉及 is_resource()
的潜在代码断点一节中举了一个例子。这一变化的一个副作用是,任何 curl*close()
函数都是多余的,因为当对象被取消设置或退出作用域时,连接就会被关闭。
现在让我们看看 COM
扩展的变化。
检查 COM 扩展更改
组件对象模型(COM)是一种仅限于 Windows 的扩展,它使用一种语言编写的程序代码可以调用任何其他支持 COM 的程序语言编写的代码并与之互操作。对于计划开发在 Windows 服务器上运行 PHP 应用程序的 PHP 开发人员来说,这一信息非常重要。
COM 扩展最重要的变化是现在自动执行大小写敏感性。因此,不能再从类型库中导入任何不区分大小写的常量。此外,您也不能再将 com_load_typelib()
函数的第二个参数 $case_insensitive
指定为 FALSE
。
此外,还修改了 COM 扩展 php.ini
中有关大小写敏感性的设置。其中包括以下设置:
-
com.autoregister_casesensitive:在 PHP 8 中永久启用。
-
com.typelib_file:名称以
#cis
或#case_insensitive
结尾的类型库不再导致常量被视为不区分大小写。
其中一个变化是新增了一个 php.ini
设置 com.dotnet_version
。该设置可为 dotnet 对象设置所使用的 .NET 版本。现在我们来看看其他值得注意的扩展变更。
检查其他扩展更改
还有一些对其他 PHP 扩展的修改值得一提。下表 7.5 总结了这些更改:
扩展 | 函数 | 描述 |
---|---|---|
Date |
mktime() gmmktime() |
第一个参数($hour)现在是强制性的 |
Enchant |
enchant_broker_list_dicts() enchant_broker_describe() enchant_dict_suggest() |
如果没有结果,这三个函数现在返回一个空数组而不是 NULL |
Tidy |
tidy_repair_string() |
不再支持 $use_include_path 参数。 |
现在您已经了解了 PHP 8 中扩展的变化。本章到此结束。现在,是时候总结一下了!
总结
本章所学到的最重要的概念之一,就是摆脱资源、转向对象的总体趋势。在本章涉及的各种 PHP 扩展中,您将了解到这一趋势的明显之处,以及如何开发变通方法来避免依赖资源的代码出现问题。您还学习了如何检测和开发代码以应对 XML 扩展中的变化,尤其是 SimpleXML
和 XMLWriter
扩展中的变化。
本章涉及的另一个有重大变化的重要扩展是 mbstring
扩展。您将学习如何检测依赖于已更改的 mbstring
功能的代码。正如你所学到的,mbstring
扩展的大部分变化都反映了 PHP 核心字符串函数的变化。
您还了解了 GD、Reflection 和 ZIP 扩展的主要变化。在本章中,你还了解了一些数据库扩展的变化,以及其他一些需要注意的扩展变化。总之,通过阅读本章和学习示例,你可以更好地防止应用程序在 PHP 8 升级后出现故障。
在下一章中,您将了解到 PHP 8 中已废弃或移除的功能。