最近在 PHP 8 环境下,发现WPJAM Basic的一个严重问题:就是后台文章和分类列表的一些操作无效了,点击保存按钮没有任何反应。经过深入调试,发现问题根源在于 PHP 8 对is_callable()函数的行为进行了重大调整。
技术细节
在 PHP 8 之前,is_callable()函数在检查类名与非静态方法的组合时会返回true,即使这种调用方式在实际执行时可能会导致问题。PHP 8 对此进行了更严格的检查,以提前发现潜在的错误调用方式,因此is_callable()在检查一个类名与非静态方法时将返回失败(应当检查一个类的实例)。
具体看下面这个例子就大概能够明白了:
class Test{
public function method1() { }
public static function method2() { }
}
// PHP 8 之前
var_dump(is_callable(['Test', 'method1'])); // bool(true)
var_dump(is_callable(['Test', 'method2'])); // bool(true)
// PHP 8 之后
var_dump(is_callable(['Test', 'method1'])); // bool(false)
var_dump(is_callable(['Test', 'method2'])); // bool(true)
var_dump(is_callable([new Test, 'method1'])); // bool(true)
变更原因
PHP 8 的这一变更是为了更严格地执行面向对象编程规范:
- 静态与非静态方法调用的区分
- :非静态方法需要类的实例才能调用,而静态方法可以直接通过类名调用。
- 提前错误检测
- :在 PHP 7 及以下版本,虽然is_callable()会返回true,但实际以静态方式调用非静态方法时会产生警告。PHP 8 通过is_callable()提前返回false来更早发现问题。
- 代码质量提升
- :强制开发者明确方法调用的上下文,避免模糊的调用方式。
解决方案
既然知道这个原因,那就是代码要根据上下文来写了。所以如果要检查某个对象的非静态方法是否可调用,应该使用一个对象实例而不是类名。
此外,其实很多时候我们只需要检查类的方法是否存在,而不关心其可调用性(例如访问控制),那么可以简单使用 method_exists()进行判断就好了:
if (method_exists('Test', 'method1')) {
// Do something
}