PHP类型系统是围绕可表达性与可验证性的权衡设计,类型检查限于函数边界,strict_types=1仅作用于当前文件,联合类型和可为空类型有明确语义与限制,PHP 8.9为虚构版本,当前最新稳定版是8.3。

PHP类型系统不是“从弱到强”的单向升级,而是围绕可表达性和可验证性反复权衡的结果。你写function foo(?string $s): array时,PHP 7.1+ 能校验调用时传入的是字符串或null,但不会阻止你在函数体内把$s当int用——类型检查只发生在函数边界,不贯穿全程。
strict_types=1 不是全局开关,只作用于当前文件
很多人以为加了declare(strict_types=1)就能让整个项目变“严格”,其实它只影响该文件中声明了类型(参数/返回值)的函数调用。比如foo(int $x)在严格模式下收到"123"会直接抛TypeError;但同一个文件里调用bar($x)(bar没声明类型),就不会触发任何检查。
常见错误现象:
- 在入口文件加了
strict_types=1,但类方法里没声明类型,误以为已生效 - include/require 的其他文件不受影响,类型松散行为照旧
- 第三方库未启用严格模式,传参时隐式转换仍会发生
联合类型(string|int)不等于运行时类型判断
PHP 8.0+ 支持string|int这类联合类型,但它只是声明契约,不是运行时自动分发机制。你不能靠这个语法让json_encode()对string走一种逻辑、对int走另一种逻辑。
立即学习“PHP免费学习笔记(深入)”;
使用场景:
- 明确接口输入范围,比如
function setId(string|int $id)比mixed更清晰 - 配合静态分析工具(如PHPStan)提前发现
setId([])这类非法调用 - IDE能据此提供更准的补全(例如知道
$id不可能是array)
容易踩的坑:
-
is_string($id) || is_int($id)仍是必要运行时校验,类型声明不替代逻辑分支 - 联合类型中不能混用
?和|:写?string|int是语法错误,必须写成string|int|null -
object不能参与联合类型(PHP 8.2起才支持object|string等有限组合)
可为空类型(?Type)不是语法糖,它改变了调用方责任
?User和User|null在PHP 7.1+ 是等价的,但语义重心不同:?User强调“这是User的可空变体”,而User|null是显式联合。关键差异在于它强制调用方处理null路径。
实操建议:
- 不要用
?array代替array来“规避空数组警告”——这会让调用方误以为null是合法业务状态 - 构造函数参数慎用可空类型,否则可能绕过对象初始化完整性检查
- 与
??运算符天然契合:$user = findUser(123) ?? new Guest();比if (is_null($u)) { ... }更简洁
性能影响很小,但类型系统推导会更精确:PHPStan 对?User能识别出->getName()前必须判空,而mixed做不到这点。
PHP 8.9 还不存在,别被“前瞻资料”带偏节奏
所有提到PHP 8.9的资料(包括联合类型增强、泛型运行时保留、模块化类型别名)都是社区假想提案或技术预演,并非已发布特性。截至2026年5月,PHP 官方最新稳定版仍是8.3,下一个正式版本是8.4(预计2024年11月发布,现已进入RC阶段)。
这意味着:
- 你无法在生产环境用
class Stack<t></t>或type CurrencyCode = non-empty-string - 所谓“PHP 8.9反射获取泛型参数”目前只能靠
DocBlock @template注解 + PHPStan 配合模拟 - 看到“PHP 8.9性能提升41%”之类说法,实际基准测试对象是
8.3 + 自定义补丁,不可直接对标
真正需要关注的是8.3已落地的能力:只读类、const 枚举、match 表达式改进、以及更严格的联合类型推导(比如string|int|null在if (is_string($x)) { ... }分支内,后续代码会被推导为int|null)——这些才是你现在能用、该用、且有文档保障的特性。



















