?? 用于空合并,仅当左侧为 null 时返回右侧默认值;??= 用于空合并赋值,仅在左侧未定义或为 null 时赋值,二者均不处理 false、0、空字符串等假值。

?? 用来安全读取可能为 null 的值
当你要从数组、对象或变量中取一个值,但不确定它是否存在或是否为 null,?? 是最直接的兜底方案。它只在左侧操作数为 null(注意:不是 false、空字符串或 0)时返回右侧默认值。
常见错误是误以为 ?? 等价于“空值判断”,比如:$name ?? 'guest' 在 $name = '' 或 $name = 0 时仍返回原值,不会触发默认。这点和 ?: 有本质区别。
适用场景包括:处理 API 返回的可选字段、表单未提交的字段、配置项 fallback。
示例:
立即学习“PHP免费学习笔记(深入)”;
$user = ['id' => 123]; $name = $user['name'] ?? 'anonymous'; // 'anonymous' $age = $user['age'] ?? 0; // 0
??= 专用于“仅当变量为 null 时才赋默认值”
??= 不是“判断后赋值”,而是“存在性赋值”——它只在左侧变量尚未定义,或明确等于 null 时,才执行赋值;其他所有情况(包括 false、''、0、[])都跳过。
它和 $var = $var ?? $default 行为一致,但更简洁、更原子——且 PHP 会避免重复求值左侧表达式(比如函数调用),这点很关键。
容易踩的坑:误用于需要“空值覆盖”的逻辑。例如初始化缓存变量时写成 $cache ??= get_data(),但如果 $cache = [],它不会重新计算,这可能是你想要的,也可能不是。
无序列表说明典型用法:
- 初始化类属性:
$this->config ??= $this->loadConfig(); - 函数内懒加载局部变量:
$pdo ??= new PDO($dsn); - 避免重复调用开销大的函数:
$result ??= expensiveCalculation();(expensiveCalculation()只在$result为null时执行一次)
别用 ??= 替代 isset() + 赋值组合
如果逻辑依赖的是“变量是否已声明”,而不是“是否为 null”,就该用 isset()。因为 ??= 对 undefined 和 null 一视同仁,但两者语义不同:未定义是“还没出生”,null 是“出生了但值为空”。
比如处理超全局变量:$_POST['page'] ??= 1 在 $_POST['page'] 未提交时设为 1,但如果它被显式设为 null(如前端传 {"page": null}),也会被覆盖——这可能违背业务意图。
此时更稳妥的是:$_POST['page'] = $_POST['page'] ?? 1;(显式控制)或 $_POST['page'] = $_POST['page'] ?? ($_POST['page'] === null ? null : 1);(极端情况需分层判断)。
兼容性与性能差异很小,但语义不能含糊
?? 和 ??= 都要求 PHP >= 7.0,实际项目中基本无需考虑降级。性能上它们比等效的 isset() + if 略快,但差异微乎其微,不用为此做取舍。
真正要注意的是:一旦用了 ??=,你就承诺了“这个变量的语义是以 null 为未初始化标志”。如果后续逻辑里又允许它合法地为 false 或 0,就容易埋下隐性 bug——比如某个函数返回 0 表示“查无结果”,而你用 $count ??= getCount(),那第二次调用就永远拿不到真实 0 了。



















