谁是 Elvis ?他和 PHP 有什么关系?
\n\n如果你写过 PHP,你很可能在别人的代码中见过这种奇怪的写法:$name ?: 'Guest' 或 $name ?? 'Guest'。初看之下,它们似乎一样。但实际上,这两个运算符隐藏着完全不同的机制,要么能拯救你的代码,要么会带来麻烦。
在 Stack Overflow 上,"PHP 短三元(Elvis)运算符 vs 空合并运算符" 这个问题已经获得了数万次浏览。这绝非偶然。开发者们会搞混、犯错,然后花很长时间排查错误。让我们最终搞清楚谁是谁,别再猜测了。
\n\n在本文中,我们不仅仅列出差异。我们将深入 PHP 的 底层,审视真实的生产案例,并理解何时该使用哪个运算符。开始吧!
\n\n快速入门:它们到底是什么?
\n\n三元运算符(经典)
\n首先,回顾一下基础。三元运算符是 if-else 的缩写形式:
\n\n$age = 20;\n$status = ($age >= 18) ? 'Adult' : 'Child';\n// $status = 'Adult'\n\n很简单:如果条件为真,取第一个值;否则取第二个。
\n\n短三元(即 Elvis 运算符)— ?:
\n这是当你检查变量是否为“真值”并想使用其值时的快捷方式:
\n\n$name = 'Alexey';\n$greeting = $name ?: 'Guest';\n// $greeting = 'Alexey'\n\n$name = '';\n$greeting = $name ?: 'Guest';\n// $greeting = 'Guest'\n\n它的工作原理是:如果 $name 计算为 true,则取 $name。否则,取 "Guest"。
空合并运算符 — ??
\n它出现在 PHP 7.0 中,并迅速成为开发者的最爱:
\n\n$name = 'Alexey';\n$greeting = $name ?? 'Guest';\n// $greeting = 'Alexey'\n\n$name = null;\n$greeting = $name ?? 'Guest';\n// $greeting = 'Guest'\n\n与 Elvis 的区别:检查 仅针对 null,而不是广义上的“假值”。
\n\n主要区别:什么被认为是“空的”?
\n\n这就是有趣的地方。让我们看看不同 $value 值下的对比表:
$value = false;\necho $value ?: 'default'; // 'default' (false 是 "假值")\necho $value ?? 'default'; // false (false 不是 null)\n\n$value = 0;\necho $value ?: 'default'; // 'default' (0 是 "假值")\necho $value ?? 'default'; // 0 (0 不是 null)\n\n$value = '';\necho $value ?: 'default'; // 'default' (空字符串是 "假值")\necho $value ?? 'default'; // '' (空字符串不是 null)\n\n$value = [];\necho $value ?: 'default'; // 'default' (空数组是 "假值")\necho $value ?? 'default'; // [] (空数组不是 null)\n\n$value = '0';\necho $value ?: 'default'; // 'default' (字符串 "0" 是 "假值"!)\necho $value ?? 'default'; // '0' (字符串 "0" 不是 null)\n\n看到区别了吗?Elvis 执行真值/假值检查,而空合并是严格检查变量是否已定义且不为 null。
\n\n底层原理:在引擎级别如何工作
\n\n让我们看看 PHP 的源代码(好吧,差不多)。如果你曾经从源码编译过 PHP,你会知道这两个运算符都在解析阶段处理。
\n\n短三元(?:):
\n实际上,它是语法糖。$a ?: $b 变成 $a ? $a : $b。但有一个细微差别:PHP 不会对 $a 求值两次。它会将结果保存在一个临时变量中。这对于带有副作用的函数很重要:
function getData() {\n echo "Function called!\\n";\n return rand(0, 1) ? 'data' : '';\n}\n\n// Elvis 只调用函数一次\n$result = getData() ?: 'default';\n// "Function called!" — 一次\n\n如果 PHP 对 $a 求值两次,