Skip to content

三元运算符

三元运算符根据条件在两个值里选一个,本身是表达式(能赋给变量、当参数),不是 if 那种语句。条件语句 里用过它,本篇把 ? :?:?? 放在一起对照。

php
<?php

$age = 20;
$label = $age >= 18 ? "成年" : "未成年";
echo $label, "\n";

一览

运算符名称何时用右边 / 第三段
$a ? $b : $c三元$a 为真取 $b,为假取 $c
$a ?: $bElvis(简写三元)$a 为假时取 $b,否则取 $a
$a ?? $b空合并$anull 或未定义时取 $b
$a ??= $b空合并赋值赋值运算符

真假规则与 逻辑运算符 相同:false00.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++、写文件这类副作用

练习

  1. ? : 根据 $loggedIn(bool)得到 已登录 / 未登录
  2. $title 分别为 null"""首页" 时,对照 ?:?? 的输出。
  3. $_GET["page"] ?? 1 取出页码并 (int) 转换;不传参数时不应 Warning。
  4. 尝试写无括号的嵌套三元,看 PHP 8 报什么错,再改成 if 或加括号的版本。

上一篇:逻辑运算符
下一篇:条件语句

基于 VitePress 构建的 PHP 基础学习指南