41、PHP 8.4 中弃用 - 已弃用隐式可为 null 的参数声明

作者: 温新

图书: 【PHP 8.4 新特性】

阅读: 137

时间: 2025-01-17 15:39:46

在 PHP 8.4 中,隐式可为 null 的参数声明(Implicit nullable parameters)被正式弃用。这一变化主要影响的是函数或方法参数类型声明中隐式地允许 null 作为合法值的情况。

PHP 支持为函数参数、返回值、类属性、类常量和枚举声明类型。作为一门动态类型的语言,并且拥有几十年历史,PHP 多年来经历了多个版本的改进和功能增强。

随着标量类型(PHP 7.0)、可为 null 的类型(PHP 7.1)、类型化属性(PHP 7.4)、联合类型(PHP 8.0)、交集类型(PHP 8.1)、DNF 类型(PHP 8.2)以及类型化类属性(PHP 8.3)等特性的引入,PHP 一直在朝着使类型声明更加富有表现力的方向发展。

类型声明的历史改进

从 PHP 5.1 开始,PHP 就可以声明带有默认值为 null 的类型,即使声明了类型,它也会隐式地允许 null 作为有效值:

function test(string $test = null) {}

test('PHP'); // 允许
test(null);  // 允许

PHP 7.1 引入了对可为 null 的类型的支持,使用 ?TYPE 语法来声明类型为可为 null

function test(?string $test = null) {}

test('PHP'); // 允许
test(null);  // 允许

联合类型的引入(PHP 8.0)

PHP 8.0 引入了联合类型(Union Types),使得可以声明一个类型为多种类型中的一种(例如,stringint),并且不仅限于 null,还可以是其他类型:

function test_with_default(string|null $test = null) {}

其他限制和规定

尽管 PHP 引入了多个类型系统的改进,例如在 PHP 8.0 中弃用了可选参数之后不能再跟必需参数,但由于现有 PHP 应用程序和项目中可能存在大量向后兼容性问题,PHP 在早期版本中并未弃用对隐式可为 null 的参数类型的支持。

xxxxxxxxxx pecl install oci8pecl install pdo_ociphp

  • 可为 null 的类型 仅允许在函数/方法的类型声明中使用。类型化属性(PHP 7.4 引入)不支持隐式的可为 null 类型声明。
  • 返回类型 不支持默认值。
  • 枚举类型(PHP 8.1 引入)仅支持 stringint 类型。
  • 属性提升的构造函数参数 不支持隐式可为 null 的语法。

PHP 8.4 弃用了隐式可为 null 的类型。建议 PHP 应用程序显式声明类型为可为 null。所有带有默认值为 null 但没有在类型声明中声明 null 的类型声明都会触发弃用通知:

function test(array $value = null) {}

隐式地将参数 $value 标记为可为 null 的做法已被弃用,必须显式使用可为 null 的类型。

弃用通知会在 PHP 遇到隐式可为 null 的类型声明时触发,而不是在调用这些函数时。

推荐的更改

将隐式可为 null 的类型声明改为显式的可为 null 的类型声明:

function test(string $test = null) {}
function test(?string $test = null) {}

test('PHP'); // 允许
test(null);  // 允许

或者,在 PHP 8.0 及更高版本中,可以使用联合类型(Union Type)使这一点更加明显:

function test(string $test = null) {}
function test(string|null $test = null) {}

test('PHP'); // 允许
test(null);  // 允许

这两种类型声明在功能上是等效的,即使在反射 API 中也是如此。第二种声明方式更为冗长,并且仅适用于 PHP 8.0 及更高版本。可以根据 PHP 版本要求、代码风格以及代码清晰度来选择适合的方式。

类方法的变化

对于类方法,将隐式可为 null 的参数改为显式声明并不会破坏向后兼容性。父类和子类可以独立地将隐式可为 null 的参数声明替换为显式声明。

考虑以下代码片段:

class ParentClass {
    public function tester(string $value = null) {}
}

class SubClass extends ParentClass {
    public function tester(?string $value = null) {}
}

在这个例子中,ParentClass::tester 方法会触发弃用通知。SubClass::tester 方法正确地显式声明了可为 null,并且不会触发弃用通知。父类可以在不破坏方法兼容性的情况下进行更新:

class ParentClass {
    public function tester(string $value = null) {}
    public function tester(?string $value = null) {}
}

class SubClass extends ParentClass {
    public function tester(?string $value = null) {}
}

移除参数类型

对于必须在 PHP 7.0 或更早版本到 PHP 8.4 之间所有版本上运行的应用程序和 PHP 库,由于可为 null 的类型仅支持 PHP 7.1 及更高版本,因此不能使用可为 null 的类型。

虽然强烈不推荐,但移除参数类型声明,然后在函数内部检查类型是避免触发弃用通知的最后手段,同时保持代码兼容 PHP 7.0 及更高版本。

移除类型安全性

当参数没有类型声明(即未提供类型)时,该参数可以接受任何类型(混合类型)。为了弥补缺少类型安全的情况,必须在函数内部检查类型。

例如,一个之前声明为 string $value = null 的函数参数,理论上可以移除类型声明,如果在函数内部对值进行了检查:

function test_discouraged(string $value = null) {
    /**
     * @param string|null $value
     */
    function test_discouraged($value = null) {
        if (is_array($value) || is_object($value)) {
            throw new TypeError(__FUNCTION__ . ': Argument #1 ($value) must be of type string, '. gettype($value) .' given');
        }
    
        if ($value !== null) {
            $value = (string) $value;
        }
    
        // 其余函数内容
    }
}

类型检查会因每种类型而异,如果类型可以被强制转换/变换为另一种类型。对于启用了 strict_types 的文件,这种类型转换检查是不必要的。

类型安全性的显著丧失

虽然移除参数类型可以让函数避免弃用通知,但这会显著丧失类型安全性。这一变化也可以通过反射 API 观察到,这意味着任何依赖于参数类型的代码也会观察到这种变化。最显著的用例包括依赖注入容器和对象填充器,这些组件检查参数的类型来决定填充值的正确类型。

添加 PHPDoc 注释

为了缓解缺少 IDE 支持的问题,可以通过添加 PHPDoc 注释来帮助补充类型信息。例如:

/**
 * @param string|null $value
 */
function test_discouraged($value = null) {
    // 具体实现
}

通过使用 PHPDoc 注释,IDE 和其他工具能够提供类型提示和代码补全,从而弥补缺失的类型声明带来的问题。

请登录后再评论