Union Types 在反射 API 中的获取方式
解读
国内一线/二线互联网公司的 PHP 面试,常把「类型系统」作为区分初中高级的重要分水岭。Union Types(联合类型)在 PHP 8.0 正式落地后,面试官喜欢追问「如何拿到运行时信息」。表面看是考反射,实质是验证候选人对「类型收窄、静态分析、IDE 友好」的理解深度。答不上来会被直接判定为「只用 PHP 写脚本,没写过企业级代码」。
知识点
- PHP 8.0+ 的 ReflectionNamedType 与 ReflectionUnionType 继承关系
- ReflectionParameter::getType() / ReflectionFunction::getReturnType() 返回值类型
- ReflectionUnionType::getTypes() 迭代子类型
- 区分「nullable」与「union 包含 null」的两种写法:?T 与 T|null
- 与 static analysis 工具(phpstan/psalm)的联动:反射结果直接影响 level 6+ 的推导
- 性能注意:反射在 OPcache 预热阶段做一次即可,切勿在请求循环里反复 new ReflectionFunction
答案
function demo(int|string $id, float|null $rate): array|bool { … }
$refl = new ReflectionFunction('demo');
// 获取参数联合类型
$param = $refl->getParameters()[0];
$type = $param->getType(); // ReflectionUnionType
foreach ($type->getTypes() as $t) {
echo $t->getName(); // 依次输出 int、string
}
// 获取返回联合类型
$returnType = $refl->getReturnType();
if ($returnType instanceof ReflectionUnionType) {
$names = array_map(fn($t) => $t->getName(), $returnType->getTypes());
// $names = ['array', 'bool']
}
// 判断可否为空
$allowsNull = $returnType->allowsNull(); // false,因为 array|bool 没写 null
一句话总结:先拿 ReflectionParameter/ReflectionFunctionAbstract 的 getType(),判断 instanceof ReflectionUnionType,再用 getTypes() 遍历即可。
拓展思考
- 如果联合类型里包含 class 名,如何进一步拿到 ReflectionClass?
答:t->getName()),但要 catch 自动加载异常。 - PHP 8.2 的 DNF(Disjunctive Normal Form)类型如 (A&B)|C 时,getTypes() 只会返回顶层 ReflectionIntersectionType 与 ReflectionNamedType,需要递归解析。
- 在 Laravel 容器里,Controller 依赖注入的 union 参数可通过 Illuminate\Container\Util::getParameterClassName() 统一收口,底层同样用 ReflectionUnionType 判断,源码是值得背诵的面试加分项。