?? 不是 isset 的语法糖,它只检查“存在且不为 null”,不触发 Notice;等价于 isset 表达式但由引擎原生支持,性能略优;仅支持单表达式,对 0/''/false 不 fallback,右侧操作数在左侧为 null 时无条件执行。

?? 不是 isset 的语法糖,它在语义和行为上都更窄、更确定:只检查“存在且不为 null”,而 isset 还会因变量未定义直接报 Notice(除非被错误抑制)——但 ?? 完全不会触发任何警告。
?? 和 isset 都能避免未定义变量警告,但触发条件不同
当你写 $name = $_GET['user'] ?? 'guest';,PHP 内部等价于 isset($_GET['user']) ? $_GET['user'] : 'guest',但它**不依赖 isset() 函数调用本身**,而是由引擎原生支持的短路求值。这意味着:
- 即使
$_GET['user']根本没声明(比如$_GET是空数组),??也不会抛Notice: Undefined index - 而
isset($_GET['user'])虽然也安全,但它是函数调用,底层仍需解析数组访问 —— 在极少数嵌套极深或动态键场景下,性能略低(可忽略,但存在) -
??只接受表达式(如数组下标、对象属性、函数调用结果),不能像isset()那样传多个参数做“全量检查”
?? 对 null 值敏感,但对 0、''、false 完全不干涉
这是最关键的差异点,也是最容易踩坑的地方:
-
$val = 0 ?? 'default';→ 返回0(因为0已定义且不为null) -
$val = null ?? 'default';→ 返回'default' -
$val = '' ?? 'default';→ 返回''(空字符串不是null) -
isset(0)、isset('')、isset(false)全部返回true,所以它们和??在这些值上行为一致 - 但如果你误以为
??等价于empty()或 “逻辑假值 fallback”,就会漏掉0或false这类合法业务值
链式 ?? 比嵌套 isset 更简洁,但无法替代 array_key_exists
多层数组取值时,?? 的链式能力非常实用:
立即学习“PHP免费学习笔记(深入)”;
$country = $_GET['profile']['address']['country'] ?? 'unknown';
这比下面写法干净得多:
$country = (isset($_GET['profile']) && isset($_GET['profile']['address']) && isset($_GET['profile']['address']['country']))
? $_GET['profile']['address']['country']
: 'unknown';
但注意:?? 依然要求左侧表达式语法合法。如果 $_GET['profile'] 是字符串而非数组,$_GET['profile']['address'] 会触发 Warning: String offset cast occurred —— ?? 拦不住这种类型错误。此时你真正需要的是 array_key_exists() 或先做类型校验。
真正容易被忽略的是:?? 的右侧操作数会在左侧为 null 时**无条件执行**。比如 $x = get_user() ?? new User();,如果 get_user() 返回 null,new User() 必然被构造 —— 这在资源敏感场景可能造成意外开销。



















