PHP 8 类型系统实战:严格类型、联合类型与空安全
PHP 8 的类型能力已经能够覆盖大多数业务边界。明确的参数、返回值和属性类型能把许多运行时错误提前到开发阶段,同时也让静态分析与重构工具更准确地理解代码。
核心原则
- 在新代码入口启用 strict_types,并为公开方法补齐参数与返回类型。
- 联合类型只表达真实存在的有限输入形态,避免用 mixed 逃避建模。
- 可空类型意味着业务上允许缺失,不应仅为了消除报错而随意添加。
- 用 readonly 属性、枚举和值对象表达不可变状态和受限取值。
- 配合 PHPStan 或 Psalm 逐步提高检查级别,把类型问题放进持续集成。
推荐的实践步骤
从系统边界开始改造最有效:把 HTTP 字符串、数据库记录转换成经过验证的 DTO 或值对象,再进入核心业务。函数内部就不必反复判断格式。对于历史代码,可以先补返回类型和最稳定的参数类型,再根据静态分析结果逐模块收紧。
declare(strict_types=1);
enum OrderStatus: string {
case Pending = 'pending';
case Paid = 'paid';
}
final readonly class Money {
public function __construct(
public int $cents,
public string $currency
) {}
}
示例用于说明实现思路,实际项目还应结合所使用的框架版本、部署环境和业务约束进行调整。重要配置要进入版本管理,并在测试环境验证后再发布。
常见误区
- 为了兼容所有输入而大量使用 mixed,会失去类型系统带来的保护。
- 把 null 同时表示未知、不存在和失败,会让调用方难以做正确处理。
- 一次性对大型旧项目开启最高静态分析级别,容易产生难以消化的改造量。
上线前检查清单
- 确认“在新代码入口启用 strict_types,并为公开方法补齐参数与返回类型”已经通过代码审查或运行验证。
- 确认“联合类型只表达真实存在的有限输入形态,避免用 mixed 逃避建模”已经通过代码审查或运行验证。
- 确认“可空类型意味着业务上允许缺失,不应仅为了消除报错而随意添加”已经通过代码审查或运行验证。
- 确认“用 readonly 属性、枚举和值对象表达不可变状态和受限取值”已经通过代码审查或运行验证。
- 为失败路径、边界条件和回滚方案准备测试或演练记录。
- 上线后观察错误率、延迟和资源消耗,确认变化符合预期。
总结
PHP 8 类型系统实战:严格类型、联合类型与空安全的关键在于把隐含假设变成可执行的约束,并通过测试、监控和复盘持续验证。先从影响最大的真实场景开始,小步调整并保留回滚能力,通常比一次性大范围改造更安全,也更容易积累可复用的工程经验。