PHP 8.4: 隐式 nullable 参数声明弃用
PHP 8.4 中的重要弃用
这是 PHP 8.4 中重要的弃用,由于 PHP 8.4 中的这一更新,旧版 PHP 应用可能会出现弃用通知。
PHP 支持为函数参数、返回值、类属性、类常量和枚举声明类型。PHP 是一种动态类型语言,有着几十年的历史,多年来得到了一些改进和特性。
随着标量类型(PHP 7.0)、 nullable 的类型(7.1)、类型化属性(7.4)、联合类型(8.0)、交集类型(8.1)、DNF 类型(8.2)和类型化类属性(8.3)等特性的引入,PHP 一直致力于使类型声明具有表达性。
自 PHP 5.1 以来,可以声明一个默认值为 null
的类型,即使声明了一个类型,它也使该类型隐式有效地允许 null
:
function test(string $test = null) {}
test('PHP'); // Allowed
test(null); // Allowed
PHP 7.1 添加了 nullable 类型支持,引入了 ?TYPE
语法将类型声明位 nullable:
- function test(string $test = null) {}
+ function test(?string $test = null) {}
test('PHP'); // Allowed
test(null); // Allowed
随着 PHP 8.0 中联合类型的引入,你可以将类型声明位某种类型(比如 string
)或另一种类型(不限于 null
, 也可以是 int
等)。
Nullable 类型也可以使用 null
将其声明位联合类型,比如:
function test_with_default(string|null $test = null) {}
尽管PHP得到了一些类型改进,例如在 PHP 8.0 中,启用了可选参数之后的必需参数,但考虑到现有 PHP 应用和项目中存在大量潜在的向后兼容性问题,在早期的 PHP 版本中,对隐式 nullable 参数类型的支持并没有被弃用。
Nullable 类型只在函数/方法类型中被允许。类型属性(PHP 7.4 中添加)不允许隐式 nullable 类型的声明。返回类型不支持默认值。枚举(PHP 8.1 中添加) 只支持 string
和 int
作为类型。此外,属性提升构造器参数不支持隐式 nullable 语法。
PHP 8.4 弃用了隐式 nullable 类型。建议 PHP 应用将类型显式声明为 nullable。所有默认值为 null
但在类型声明中未声明 null
的类型声明都会发出弃用通知:
function test(array $value = null) {}
Implicitly marking parameter $value as nullable is deprecated, the explicit nullable type must be used instead
当 PHP 遇到具有隐式 nullable 类型的声明时,会发出弃用。而不是调用此类函数时。
修改建议
将隐式 nullable 类型声明修改为显示声明:
- function test(string $test = null) {}
+ function test(?string $test = null) {}
test('PHP'); // Allowed
test(null); // Allowed
此外,运行在 PHP 8.0 及其之后版本上的应用可以使用联合类型使之更为明显:
- function test(string $test = null) {}
+ function test(string|null $test = null) {}
test('PHP'); // Allowed
test(null); // Allowed
两种类型声明方式都是等效的,即使在反射 API 中。第二个示例更详细,仅适用于 PHP 8.0 及更高版本。根据 PHP 版本要求、代码风格并考虑代码清晰度,选择其中一种方式。
在类方法上,将隐式 nullable 的参数更改为显式参数并不会破坏向后兼容性。父类和子类可以独立地用显式声明替换隐式 nullable 的参数声明。
参考如下代码片段:
class ParentClass {
public function tester(string $value = null) {}
}
class SubClass extends ParentClass {
public function tester(?string $value = null) {}
}
ParentClass::tester
方法触发了弃用通知。子类 SubClass::tester
显式声明了 nullable 类型,且不会触发弃用通知。更新父类而不出现方法兼容性问题是可能的:
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 5 到 PHP 8.4 的所有 PHP 版本上运行的应用和 PHP 库不能使用 nulllable 的类型,因为只有在 PHP 7.1 及更高版本上才支持 nullable 类型。
尽管十分不鼓励,但避免弃用通知的终极手段,同时保持代码与 PHP 7.0 及更高版本兼容是:删除参数中的类型声明,然后在函数内部检查类型。
安全地移除类型
如果参数未提供类型,则参数可以接受任何类型(即mixed
类型)。请确保在函数中进行类型检查,以应对类型安全性的不足。
比如,如果参数值在函数内进行了检查,那么此前有 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;
+ }
+
// Rest of the function here.
}
每种类型的类型检查将有所不同,如果可以将这些类型强制/篡改为另一种类型。在启用了 strict_types
的文件上,不需要进行这种类型切换检查
尽管删除参数类型可以使函数避免弃用通知,但这会严重顺还类型安全。这种变化也可以从反射 API 中观察到,这意味着任何依赖于参数类型的代码也会观察到这种变化。最值得注意的用例包括依赖注入容器和对象合成器,它们检查参数的类型以决定要 hydrated 的值的正确类型。
添加 PHPDoc 注释有助于缓解IDE支持的不足。
向后兼容性影响
这义弃用使类型声明的可见型和表达力得到提升。建议的替换为兼容 PHP 7.1 及更高版本。
自动修复
以下工具可以标准并自动修复隐式 nullable 类型声明。
- PHP-CS-Fixer 使用
nullable_type_declaration_for_default_null_value
规则 - PHP CodeSniffer 适用
SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue
- Rector