PHP 8.1.16联合类型是编译期特性,由parser和compiler处理:语法解析(zend_language_parser.y)、词法扫描(zend_language_scanner.l)及语义编译(zend_compile.c)协同完成,存储为带ZEND_TYPE_IS_UNION标志的zend_type单向链表,运行时仅用于参数与返回值类型检查。

直接看源码前,先明确一点:PHP 8.1.16 的联合类型(int|string)和交集类型(Loggable&Cacheable)是编译期语法特性,它们的解析逻辑集中在词法分析、语法分析和语义检查三个阶段,不涉及运行时 zval 结构变更。
聚焦 parser 和 compiler 目录
联合类型的处理不在 Zend VM 或执行引擎里,而在编译前端。关键路径是:
-
Zend/zend_language_parser.y:Yacc/Bison 语法文件,定义了type非终结符如何匹配T_STRING '|' T_STRING这类结构 -
Zend/zend_language_scanner.l:词法扫描器,识别竖线|为独立 token(T_PIPE),不是运算符而是类型分隔符 -
Zend/zend_compile.c:核心语义处理函数如zend_compile_type和zend_do_begin_function_declaration,负责把解析出的多个类型存入zend_type结构体链表
理解 zend_type 内部表示
PHP 不用数组或哈希存联合类型,而用单向链表 + 标志位。每个 zend_type 是一个结构体,关键字段有:
-
type_mask:位掩码,例如ZEND_TYPE_IS_UNION表示这是联合类型 -
type_list:指向第一个子类型的指针,后续通过next字段串联 -
name:仅当是单一命名类型(如DateTime)时有效;联合类型中该字段为空
所以 string|null 在内存中是一个 zend_type 实例,type_mask 含 ZEND_TYPE_IS_UNION,type_list 指向两个子节点:一个代表 string,一个代表 null。
立即学习“PHP免费学习笔记(深入)”;
留意类型检查的触发点
联合类型不参与运行时判断,只在以下环节起作用:
- 函数调用时参数绑定:
zend_check_arg_type遍历联合类型链表,任一子类型匹配即通过 - 返回值验证:
zend_verify_return_type对返回值做同样遍历检查 - 静态分析工具(如 PHPStan)会读取这些 AST 节点,但 PHP 自身不提供反射 API 暴露联合类型细节——
ReflectionParameter::getType()返回的是字符串(如"string|null"),不是结构化对象
避开常见误解
不要在 Zend/zend_execute.c 或 Zend/zend_variables.c 里找联合类型的实现,那里处理的是值(zval),不是类型声明。也不要在 ext/standard 下翻,联合类型是语言核心机制,和 array_intersect() 这类函数完全无关。



















