11月26日,PHP 8将向全世界发布。PHP 8将成为PHP历史上最具突破性的PHP版本之一,它将为WordPress等传统PHP代码库带来前所未有的挑战,以解决兼容性问题。
今天,我们为您带来有关WordPress和PHP 8兼容性的全面报告。在分享这些内容时,我们希望对WordPress和PHP社区进行教育并帮助其了解WordPress和PHP 8的状态。毕竟,PHP是为WordPress提供支持的技术,而WordPress是迄今为止最大的PHP使用者。
该报告是由Juliette(在PHP和WordPress社区中广受尊敬的PHP工程师),Herre(Yoast的首席软件架构师)和我本人共同努力的结果。
介绍此报告中有什么内容?
在本报告的第一部分中,我们将概述PHP 8中的更改,这些更改可能会严重影响WordPress和其他旧版代码库。在本报告的第二部分中,我们将尝试提供有关WordPress和PHP兼容性的过去,现在和未来挑战的观点。在报告的末尾,我们包含了Yoast.com的案例研究,以说明PHP 8上的大型WordPress网站可能发生的问题。
PHP 8中为什么会有如此多的重大更改?
PHP 8是PHP的主要更新,通常的做法是从先前的次要版本范围中删除主要版本中的弃用版本。对于PHP 8,在先前的7. *版本中已弃用了许多重大更改。因此,对于经过多年努力修复不推荐使用的API的项目,根本就不难升级。
但是,与以前的PHP版本相比,PHP 7. *版本的弃用范围更大。从PHP 5.6到PHP 7是一个相对简单的迁移,从7.x到8的迁移可能会非常痛苦,尤其是对于非常老的代码库,例如WordPress和许多可用的插件。对于类型正确的代码库或最新的PHP版本保持最新的代码库,没有太大的问题。但是,现实情况是WordPress不是这样的代码库。
WordPress是否已经与PHP 8兼容?
嗯,是。有点。也许。我们高度怀疑。真的不可能讲。
WordPress旨在始终与新版本的PHP兼容。Sergey在解决可以使用可用策略检测到的大多数兼容性问题方面做得非常出色。我们绝对会更深入地探讨这些内容以及存在的问题。从技术上讲,当前每晚的WordPress与PHP 8的兼容性与我们在新版本PHP出现之前从WordPress版本获得的兼容性处于同一水平。我们认为测试的范围非常广泛,修复过程非常艰苦,而且修复的程度与WordPress核心内的任何一轮PHP兼容性修复一样高。另请参阅在WordPress.org上进行PHP 8测试的电话。
但是,不幸的是,这次我们一直做的事情不会减少。PHP 8中包含的大量重大更改和更改类型,以及跨版本工具中增加的一些复杂性,使得这种兼容性挑战与我们以前看到的事物截然不同。该报告旨在解释情况。
第1部分:PHP 8中最令人担忧的重大更改严格在PHP 8中进行内部输入
PHP 8中最重要的突破性变化之一与严格的输入有关。PHP中的用户定义函数已引发TypeError。但是,内部函数发出警告并返回null。PHP 8使这种一致,内部函数现在也抛出TypeError。这不仅会影响PHP 8之前已经发出警告的函数,还会影响魔术方法(以前没有进行类型检查)和引入了类型声明的函数。因此,不可能通过修复PHP 7.4环境中的类型警告来捕获此更改引起的所有问题。以下是相关重大更改的概述,这些更改共同定义了PHP 8中与严格类型相关的更改的范围。
一致的类型错误
从PHP 8开始,内部函数现在为所有类型的参数抛出TypeError。
算术运算符类型检查
算术和按位运算符+,-,*,/,**,%,<<,>>,&,|,^,〜,++,-现在将在一个操作数为数组,资源或非重载对象。唯一的例外是array + array union操作,该操作仍受支持。
在PHP 8之前,可以在数组,资源或对象上应用算术或按位运算符。这是不可能的,它将引发TypeError。
魔术方法类型检查
现在,Magic Methods将对其参数和返回类型进行检查(如果已声明)。签名应与以下列表匹配:
- __call(字符串$ name,数组$ arguments):混合
- __callStatic(字符串$ name,数组$ arguments):混合
- __clone():无效
- __debugInfo():?array
- __get(字符串$ name):混合
- __invoke(混合$ arguments):混合
- __isset(字符串$ name):布尔
- __serialize():数组
- __set(字符串$ name,混合$ value):空
- __set_state(数组$ properties):对象
- __sleep():数组
- __unserialize(数组$ data):无效
- __unset(字符串$ name):无效
- __wakeup():无效
数字字符串处理
数字字符串处理已更改为更加直观且不易出错。现在允许在数字字符串中使用尾随空格,以与如何处理前导空格保持一致。这主要影响:
- is_numeric()函数
- 字符串之间的比较
- 类型声明
- 递增和递减运算
“前导数字字符串”的概念已被删除。为了简化迁移而存在的情况。现在,发出E_NOTICE“遇到一个格式错误的数值”的字符串将发出E_WARNING“遇到一个非数值值”,并且发出E_WARNING“遇到一个非数值值”的所有字符串现在都将引发TypeError。这主要影响:
- 算术运算
- 按位运算
从E_WARNING到TypeError的更改也会影响E_WARNING对于非法字符串偏移的“非法字符串偏移’string’”。从字符串到int / float的显式强制转换的行为没有变化。
命名参数
还增加了对命名参数的支持。这有两个主要含义:
- 重命名参数成为一项重大更改。如果重命名参数,则使用命名参数调用该函数的任何地方都会中断。
- call_user_func_array()的行为发生了变化。以前,可以使用关联数组调用call_user_func_array()。现在,传递关联数组将被解释为使用命名参数,如果不存在任何命名参数,这将引发Exception。
API更改可能导致类型错误
下面我们列出了一些API更改示例的列表,这些示例将导致类型或参数错误,而以前的PHP版本中没有此类指示。
- mktime()和gmmktime()现在至少需要一个参数。time()可用于获取当前时间戳。
- spl_autoload_register()现在将始终在无效参数上引发TypeError,因此第二个参数$ do_throw将被忽略,如果将其设置为false,则会发出通知。
- assert()将不再求值字符串参数,而是将它们视为任何其他参数。应该使用assert($ a == $ b)来代替assert(’$ a == $ b’)。assert.quiet_eval ini指令和ASSERT_QUIET_EVAL常量也已删除,因为它们将不再起作用。
- vsprintf(),vfprintf()和vprintf()的$ args参数现在必须是一个数组。以前,任何类型都可以接受。
- 具有在运行时解析为null的默认值的参数将不再隐式将参数类型标记为可为空。请使用显式可为空的类型,或者使用显式为空的默认值。
警告转换为错误异常
在PHP 8中,有许多PHP警告已更改为错误异常。
与RFC无关的错误级别更改
以下警告已转换为可能与PHP 7.x版本中的弃用相关的错误:
- 尝试写入非对象的属性。以前,这会为null,false和空字符串隐式创建一个stdClass对象。
- 尝试将元素追加到已经使用PHP_INT_MAX键的数组中。
- 尝试使用无效的类型(数组或对象)作为数组键或字符串偏移量。
- 尝试写入标量值的数组索引。
- 尝试打开非数组/ Traversable的包装。
重新分类的发动机警告
以前仅触发警告或通知的许多错误已转换为错误。进行了以下更改:
- 未定义的变量:警告而不是通知
- 未定义的数组索引:警告而不是通知
- 除以零:DivisionByZeroError异常而不是警告
- 尝试增加/减少非对象的属性’%s’:错误异常而不是警告
- 尝试修改非对象的属性’%s’:错误异常而不是警告
- 尝试分配非对象的属性’%s’:错误异常而不是警告
- 从空值创建默认对象:错误异常而不是警告
- 试图获取非对象的属性’%s’:警告而不是通知
- 未定义的属性:%s :: $%s:警告而不是通知
- 无法将元素添加到数组,因为已经占用了下一个元素:错误异常而不是警告
- 无法取消设置非数组变量中的偏移量:错误异常而不是警告
- 无法将标量值用作数组:错误异常而不是警告
- 只能解压缩数组和Traversables:TypeError异常而不是警告
- 为foreach()提供了无效的参数:TypeError异常而不是警告
- 非法的偏移量类型:TypeError异常而不是警告
- isset中的偏移量类型非法或为空:TypeError异常而不是警告
- 未设置无效的偏移类型:TypeError异常而不是警告
- 数组到字符串的转换:警告而不是通知
- 资源ID#%d用作偏移量,转换为整数(%d):警告而不是通知
- 发生字符串偏移量强制转换:警告而不是通知
- 未初始化的字符串偏移量:%d:警告而不是通知
- 无法将空字符串分配给字符串偏移量:错误异常而不是警告
- 提供的资源不是有效的流资源:TypeError异常而不是警告
- #@运算符不再使致命错误失效。
可能此更改可能会揭示在PHP 8之前再次隐藏的错误。请确保在生产服务器上将display_errors = Off设置为Off!
不兼容的方法签名的致命错误
由于两个类之间的方法签名不兼容而导致的继承错误现在将引发致命错误,而不是警告。
默认错误报告级别
使用PHP 8,默认错误报告级别更改为E_ALL,而不是E_NOTICE和E_DEPRECATED之外的所有内容。这意味着许多错误将开始出现,这些错误以前是被忽略的。
7.x弃用
在PHP 7.x发行周期中,每个版本都引入了新的弃用方法,这些弃用方法现已作为PHP 8中的功能删除而完成。这也适用于PHP 5.x中已经存在但并未删除的某些弃用方法。在PHP 7.0版本中。
最值得注意的是,PHP 8中已删除了以下已过时的功能:
- $ php_errormsg
- create_function()
- mbstring.func_overload
- 没有第二个参数的parse_str()
- 每()
- 带有字符串参数的assert()
- 错误处理程序的$ errcontext参数
- 带整数针的字符串搜索功能
- 定义独立的assert()函数
- 实型(浮点数别名)
- 魔术报价遗留
- 具有对象的array_key_exists()
- 反射export()方法
- implode()参数顺序混合
- 从非静态闭包中解除$ this的绑定
- restore_include_path()函数
- allow_url_include ini指令
其他重大变化。
PHP 8中还有许多其他(重大)更改,包括对精选但经常使用的函数的返回类型进行了重大更改,将资源转换为Value Objects等。GD,OpenSSL,Sockets,XML,Zlib,substr()等的示例。
上面我们试图强调那些可能以重大方式直接影响WordPress(以及许多其他旧系统)的系统。我们基于对优秀导游“这个概述什么是在PHP 8个新的” stitcher.io和PHP 8升级指南。有关更多信息,请参考这些资源。
第2部分:兼容性挑战
为了使现有代码库与新版本的PHP兼容,可以部署两种不同的发现策略:
- 静态分析工具(如PHPCompatibility)可检测语法问题。
- 自动化测试以检测运行时问题。
- 手动测试以检测运行时问题。
根据测试套件的覆盖范围以及运行时和语法更改的比例,这些策略可作为固定代码库与新版本PHP兼容性的良好基础。但是,对于WordPress和PHP 8,还有很多其他挑战,这使得很难依靠这些策略来修复兼容性并声明WordPress与PHP 8兼容。下面我们将报告针对哪些策略进行了部署WordPress及其结果。
静态分析工具
由于PHP 8.0中某些更改的性质,使用静态分析可以发现的问题是有限的。在那些静态分析试图超越其传统功能并试图跟踪运行时类型以及变量和常量的值的情况下,此类扫描的结果将很容易出现误报。
除此之外,PHPCompatibility是唯一用于查找与PHP跨版本兼容性相关的问题的静态分析工具。
其他静态分析工具将报告更大范围的问题。遍历结果以查找与PHP跨版本兼容性相关且实际上正确的问题,这非常耗时,并且需要对工具进行深入了解才能将其配置为噪声最小。
同时,这些工具不断变化,试图跟上PHP的变化并更新可用的扫描,因此,与当前所发现的内容无关,这些工具很可能会发现更多的问题。在不远的将来。
使用PHP兼容性扫描WordPress
在WP 5.6开发周期的早期报告的问题,自基于PHPCompatibility扫描以来已修复:
- 在必需参数之前声明的可选参数
- 最终私人方法
- 删除对create_function的使用
- 仅有条件地调用libxmldisableentity_loader
PHPCompatibility检测到的另一个PHP 8问题是“ __destruct()将不再在__construct()中的die()之后被调用”。这是由扫描仪正确检测到的,但经进一步分析,确定在这种特定情况下没有问题。
PHPCompatibility还检测到插件/主题编辑器使用的代码中存在问题。对所涉及代码的分析已确定代码中存在基础监督。WordPress尝试对编辑器中的代码进行最少的分析,但未考虑PHP 5.3+(命名空间)代码。现在,在考虑到PHP 8.0的相关更改的同时,将使解决此监督变得更加复杂。
使用PHPCompatibility进行的扫描已在开发版本中运行。尚未发布包含PHP 8特定扫描的版本。
那里报告了扫描仪检测到的外部维护依赖关系中的问题。
使用Exakat扫描WordPress
截至10月16日基于WP干线的最新公共扫描,Exakat总共报告了149.567个问题。