PHP 条件判断与循环

AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。

PHP 8.4

PHP 的流程控制语法与其他语言相似,但有两点是 PHP 特有的:match 表达式(PHP 8 起,比 switch 好用得多)和模板替代语法。

if / elseif / else

$score = 85;

if ($score >= 90) {
    echo "优秀";
} elseif ($score >= 60) {
    echo "及格";
} else {
    echo "不及格";
}

elseif 在 PHP 里也可以写成 else if,两者等价,风格上统一用 elseif。

三元运算符与空值合并:

$label = $score >= 60 ? "及格" : "不及格";
$name  = $_GET["name"] ?? "匿名";   // 仅当 null / 未设置时兜底

?? 可以链式使用:$a ?? $b ?? "默认"。

switch 的问题

switch ($status) {
    case "pending":
        echo "等待处理";
        break;
    case "done":
        echo "已完成";
        break;
    default:
        echo "未知状态";
}

switch 用 ==(松散比较)且必须手动 break,漏写会继续执行下一个分支。"1" 与 1 会互相匹配,这是很多 bug 的来源。

match 表达式(推荐)

PHP 8 引入 match,三个改进:严格比较(===)、无需 break、有返回值。

$status = "pending";

$message = match ($status) {
    "pending" => "等待处理",
    "done"    => "已完成",
    "failed"  => "失败",
    default   => "未知状态",
};

echo $message;   // 等待处理

多个条件合并与在分支里执行逻辑:

$level = match (true) {
    $score >= 90 => "优秀",
    $score >= 60 => "及格",
    default      => "不及格",
};

没有匹配且没有 default 时会抛 UnhandledMatchError,这比 switch 静默跳过更安全。

结论:PHP 8+ 里需要等值匹配时优先 match,switch 只用于需要 fall-through 的少数场景。

for

次数确定的循环:

for ($i = 0; $i < 3; $i++) {
    echo $i . "\n";
}

遍历数组时不要用 for,用 foreach——PHP 数组的键不一定是连续整数,for 会漏项或报未定义下标。

foreach(主力)

$colors = ["red", "green", "blue"];

foreach ($colors as $color) {
    echo $color . "\n";
}

foreach ($colors as $index => $color) {
    echo "$index => $color\n";
}

foreach 遍历的是副本。要在循环中修改元素值:

foreach ($colors as &$color) {
    $color = strtoupper($color);
}
unset($color);   // 必须断开引用

忘记 unset 会导致之后再使用 $color 这个变量名时,意外修改数组最后一个元素——这是很难查的 bug。

while 与 do-while

次数不确定时用 while:

$retry = 0;

while ($retry < 3) {
    echo "第 " . ($retry + 1) . " 次尝试\n";
    $retry++;
}

do-while 至少执行一次:

do {
    $input = trim(fgets(STDIN));
} while ($input === "");

写 while 时先确认退出条件可达,否则死循环。

break 与 continue

foreach ([1, 2, 3, 4, 5] as $n) {
    if ($n === 2) {
        continue;    // 跳过本次
    }
    if ($n === 4) {
        break;       // 结束整个循环
    }
    echo $n . "\n";
}
// 输出 1 3

continue 2 / break 2 可以指定跳出几层嵌套(用于嵌套循环):

foreach ($rows as $row) {
    foreach ($row as $cell) {
        if ($cell === null) {
            continue 2;   // 跳过外层本次迭代
        }
        echo $cell;
    }
}

数字层数容易写错,嵌套超过两层就该考虑拆成函数。

模板替代语法

在 HTML 模板里混用大括号会降低可读性,PHP 提供了冒号 + end 关键字的替代写法:

<?php if ($loggedIn): ?>
    <p>欢迎回来</p>
<?php else: ?>
    <p><a href="/login">请登录</a></p>
<?php endif; ?>

<ul>
<?php foreach ($items as $item): ?>
    <li><?= htmlspecialchars($item, ENT_QUOTES, "UTF-8") ?></li>
<?php endforeach; ?>
</ul>

几点说明:

  • <?= 是 <?php echo 的简写,PHP 5.4 起始终可用。
  • 输出到 HTML 必须转义(htmlspecialchars),详见本系列第 5 篇。
  • endif / endforeach / endwhile / endfor 与对应的冒号开头配对。

一个完整例子:分组渲染

<?php
declare(strict_types=1);

$orders = [
    ["id" => 1, "status" => "pending"],
    ["id" => 2, "status" => "done"],
    ["id" => 3, "status" => "pending"],
];

$groups = [];
foreach ($orders as $order) {
    $groups[$order["status"]][] = $order["id"];
}

foreach ($groups as $status => $ids) {
    $label = match ($status) {
        "pending" => "待处理",
        "done"    => "已完成",
        default   => "其他",
    };
    echo $label . ":" . implode(", ", $ids) . PHP_EOL;
}
// 待处理:1, 3
// 已完成:2

常见错误与注意事项

  1. switch 漏写 break:连续执行后续分支。改用 match 可从根上避免。
  2. switch 的松散比较:"1" 匹配 1,match 用 ===。
  3. match 没有 default 且未匹配:抛 UnhandledMatchError,记得给 default。
  4. foreach 引用后不 unset:后续同名变量会污染数组最后一个元素。
  5. 用 for 遍历关联数组:下标不存在,报未定义偏移。一律用 foreach。
  6. while 循环变量不更新:死循环。

小结

  • 等值分支用 match(严格比较、有返回值、无需 break)。
  • 遍历数组用 foreach,不要用 for。
  • 引用遍历后必须 unset。
  • HTML 模板用替代语法 if: ... endif; 与 <?= ?> 短输出。

下一步