主题
三元运算符
三元运算符根据条件在两个值里选一个,本身是表达式(能赋给变量、当参数),不是 if 那种语句。条件语句 里用过它,本篇把 ? :、?:、?? 放在一起对照。
php
<?php
$age = 20;
$label = $age >= 18 ? "成年" : "未成年";
echo $label, "\n";一览
| 运算符 | 名称 | 何时用右边 / 第三段 |
|---|---|---|
$a ? $b : $c | 三元 | $a 为真取 $b,为假取 $c |
$a ?: $b | Elvis(简写三元) | $a 为假时取 $b,否则取 $a |
$a ?? $b | 空合并 | $a 是 null 或未定义时取 $b |
$a ??= $b | 空合并赋值 | 见 赋值运算符 |
真假规则与 逻辑运算符 相同:false、0、0.0、""、"0"、[]、null 为假。
标准三元 ? :
php
<?php
$score = 75;
$result = $score >= 60 ? "及格" : "不及格";
echo $result, "\n";等价于:
php
<?php
if ($score >= 60) {
$result = "及格";
} else {
$result = "不及格";
}可以嵌在函数参数或字符串里,但一长就难读:
php
<?php
echo $score >= 60 ? "及格" : "不及格", "\n";条件、真值、假值都是表达式,各自都可以再运算。不要在分支里塞带副作用的调用(写文件、发请求),那种情况用 if。
PHP 8 禁止无括号嵌套
多个 ? : 叠在一起、又不加括号,PHP 8 直接语法错误(7.x 会按容易搞错的规则去算):
php
<?php
// $a ? $b : $c ? $d : $e; // PHP 8:CompileError
$level = $score >= 90 ? "优" : ($score >= 60 ? "及格" : "不及格");能加括号也不如改回 if / elseif。三元只适合「一眼能看完的二选一」。
Elvis ?:
$a ?: $b 是 $a ? $a : $b 的简写:左边为真就用左边,为假才用右边。左边只求值一次。
php
<?php
$name = "";
$display = $name ?: "游客";
echo $display, "\n"; // 游客
$name = "小明";
$display = $name ?: "游客";
echo $display, "\n"; // 小明适合「这个值能用就用,否则给个兜底」。注意 0、"0"、[] 都会被当成假而换成兜底:
php
<?php
$count = 0;
echo $count ?: "没有"; // 输出「没有」,可能不是你想要的数量为 0 仍然合法时,不要用 ?:,改用下面的 ??,或显式 !== null。
空合并 ??
$a ?? $b 只在左边是 null 或未定义时才用右边。0、""、false、[] 都算「有值」,会留下。
php
<?php
$title = null;
echo $title ?? "未命名", "\n"; // 未命名
$title = "";
echo $title ?? "未命名", "\n"; // 空字符串,不会换成「未命名」
echo $missing ?? "默认", "\n"; // 未定义,不 Warning,输出「默认」读数组、超全局时几乎总是它,避免 PHP 8 的 Undefined array key / Undefined variable:
php
<?php
$page = (int) ($_GET["page"] ?? 1);
$email = $user["email"] ?? "";可以连写,从左到右取第一个「有定义且不是 null」的值:
php
<?php
$name = $user["nickname"] ?? $user["name"] ?? "游客";?? 右边是短路的:左边已经有值时,右边不会执行。
只在为空时写入变量,用 ??=,见 赋值运算符。
三张图放在一起
php
<?php
$input = 0;
var_dump($input ? "真" : "假"); // "假"
var_dump($input ?: "兜底"); // "兜底"(0 为假)
var_dump($input ?? "兜底"); // int(0)(0 不是 null)
$input = null;
var_dump($input ? "真" : "假"); // "假"
var_dump($input ?: "兜底"); // "兜底"
var_dump($input ?? "兜底"); // "兜底"
$input = "小明";
var_dump($input ? "真" : "假"); // "真"
var_dump($input ?: "兜底"); // "小明"
var_dump($input ?? "兜底"); // "小明"| 左边的值 | ? : 走哪支 | ?: | ?? |
|---|---|---|---|
"小明" | 真支 | 留下边 | 留下边 |
0 / "" / [] / false | 假支 | 用右边 | 留下边 |
null / 未定义 | 假支 | 用右边 | 用右边 |
选哪个:
- 两个完全不同的结果(及格 / 不及格)→
? : - 值本身能用就用,假值才替换(空字符串当没填)→
?: - 只补
null/ 缺键(页码、配置、$_GET)→??
优先级
?? 高于 ? :,两者都低于 ||、高于赋值。拿不准就加括号。
php
<?php
$a = null;
echo $a ?? "x" ? "yes" : "no", "\n";
// 先算 $a ?? "x" 得到 "x",再当条件 → yes不要把 || 和 ?? 混着当默认值:$name = $_GET["name"] || "游客" 得到的是布尔值,不是字符串。默认值用 ??。
和 if、match 怎么选
| 场景 | 选用 |
|---|---|
| 二选一得到一个值,表达式很短 | ? : |
缺省 null / 缺键 | ?? / ??= |
假值(含 0、"")要替换 | ?: |
| 多分支、要执行语句 | if / elseif |
| 一个变量对几个常量 | match |
相关写法见 条件语句。
相关:nullsafe ?->(了解)
PHP 8 的 ?-> 不是三元运算。对象为 null 时整条属性链得到 null,不报错:
php
<?php
$city = $user?->address?->city ?? "未知";现在能看懂即可,讲对象时再展开。缺数组键仍然用 ??,不要写成 $arr?["k"](没有这种语法)。
书写建议
- 三元只写一行能看完的;再长就
if - 不要无括号嵌套
? : - 读请求、读配置、读可能不存在的数组键,默认
?? ?:会把0吃掉,用在数量、金额上先想清楚- 分支里不要藏
$i++、写文件这类副作用
练习
- 用
? :根据$loggedIn(bool)得到已登录/未登录。 $title分别为null、""、"首页"时,对照?:和??的输出。- 用
$_GET["page"] ?? 1取出页码并(int)转换;不传参数时不应 Warning。 - 尝试写无括号的嵌套三元,看 PHP 8 报什么错,再改成
if或加括号的版本。