PHP 完全指南 / 第 6 章 — 控制流
第 6 章 — 控制流:条件语句、循环与模式匹配
6.1 if / elseif / else
<?php
$score = 85;
if ($score >= 90) {
$grade = 'A';
} elseif ($score >= 80) {
$grade = 'B';
} elseif ($score >= 70) {
$grade = 'C';
} elseif ($score >= 60) {
$grade = 'D';
} else {
$grade = 'F';
}
echo "成绩等级: {$grade}"; // B
替代语法(模板中常用)
<?php if ($score >= 60): ?>
<p style="color: green;">✅ 恭喜通过</p>
<?php else: ?>
<p style="color: red;">❌ 未通过</p>
<?php endif; ?>
6.2 match 表达式(PHP 8.0+)
match 是 switch 的现代替代,返回值、使用严格比较、支持复合条件。
<?php
// 基本 match
$httpCode = 404;
$message = match ($httpCode) {
200 => 'OK',
301 => 'Moved Permanently',
302 => 'Found',
400 => 'Bad Request',
404 => 'Not Found',
500 => 'Internal Server Error',
default => 'Unknown Status',
};
// 多值匹配
$lang = 'zh';
$greeting = match ($lang) {
'en', 'us' => 'Hello',
'zh', 'zh-chs' => '你好',
'ja' => 'こんにちは',
default => 'Hi',
};
// 无默认值 — 未匹配时抛出 UnhandledMatchError
$type = match ($mimeType) {
'text/html' => 'html',
'application/json' => 'json',
'image/png' => 'image',
};
// 类型化匹配
$result = match (true) {
$age < 0 => throw new InvalidArgumentException('Invalid age'),
$age < 18 => '未成年',
$age < 65 => '成年人',
default => '老年人',
};
// 复杂表达式
$action = match (true) {
$user->isAdmin() => 'admin_panel',
$user->hasRole('editor') => 'editor_panel',
$user->isVerified() => 'user_dashboard',
default => 'login',
};
match vs switch 对比
| 特性 | switch | match |
|---|---|---|
| 返回值 | ❌ 需要变量赋值 | ✅ 是表达式,直接返回 |
| 比较方式 | 松散 (==) | 严格 (===) |
| 多值匹配 | case 1: case 2: | 1, 2 => |
| 复合条件 | ❌ | ✅ true => 模式 |
| fall-through | ⚠️ 需要 break | ✅ 无 fall-through |
| 未匹配处理 | 静默继续 | 抛出 UnhandledMatchError |
6.3 switch 语句(传统)
<?php
$day = date('N'); // 1=周一, 7=周日
switch ($day) {
case 1:
case 2:
case 3:
case 4:
case 5:
echo '工作日';
break;
case 6:
echo '周六';
break;
case 7:
echo '周日';
break;
}
注意:
switch使用松散比较,switch(1)会匹配case "1"。现代 PHP 推荐使用match。
6.4 for 循环
<?php
// 基本 for 循环
for ($i = 0; $i < 10; $i++) {
echo $i . ' ';
}
// 输出: 0 1 2 3 4 5 6 7 8 9
// 倒序
for ($i = 9; $i >= 0; $i--) {
echo $i . ' ';
}
// 多变量
for ($i = 0, $j = 10; $i < $j; $i++, $j--) {
echo "i={$i}, j={$j} ";
}
// i=0,j=10 i=1,j=9 i=2,j=8 ...
// 九九乘法表
for ($i = 1; $i <= 9; $i++) {
for ($j = 1; $j <= $i; $j++) {
printf("%d×%d=%-4d", $j, $i, $i * $j);
}
echo "\n";
}
6.5 while / do-while
<?php
// while — 先判断再执行
$n = 1;
while ($n <= 100) {
if ($n % 15 === 0) {
echo "FizzBuzz ";
} elseif ($n % 3 === 0) {
echo "Fizz ";
} elseif ($n % 5 === 0) {
echo "Buzz ";
} else {
echo "{$n} ";
}
$n++;
}
// do-while — 先执行再判断(至少执行一次)
do {
$input = readline('请输入密码: ');
} while (strlen($input) < 8);
echo "密码已设置";
6.6 foreach
foreach 是遍历数组和可迭代对象的核心结构。
<?php
// 遍历索引数组
$fruits = ['苹果', '香蕉', '橙子'];
foreach ($fruits as $fruit) {
echo "{$fruit}\n";
}
// 带键遍历
foreach ($fruits as $index => $fruit) {
echo "{$index}: {$fruit}\n";
}
// 遍历关联数组
$user = ['name' => 'Alice', 'age' => 30, 'city' => 'Shanghai'];
foreach ($user as $key => $value) {
echo "{$key}: {$value}\n";
}
// 解构遍历(PHP 7.1+)
$users = [
['name' => 'Alice', 'age' => 30],
['name' => 'Bob', 'age' => 25],
['name' => 'Eve', 'age' => 28],
];
foreach ($users as ['name' => $name, 'age' => $age]) {
echo "{$name} is {$age}\n";
}
修改数组元素
<?php
$prices = [100, 200, 300];
// 使用引用修改
foreach ($prices as &$price) {
$price *= 0.9; // 打九折
}
unset($price); // ⚠️ 必须取消引用!
// 值拷贝修改(PHP 7.4+)
$prices = array_map(fn($p) => $p * 0.9, $prices);
6.7 循环控制:break 与 continue
<?php
// break — 跳出循环
for ($i = 0; $i < 100; $i++) {
if ($i === 10) {
break; // 跳出整个 for 循环
}
echo "{$i} ";
}
// continue — 跳过当前迭代
for ($i = 0; $i < 10; $i++) {
if ($i % 2 === 0) {
continue; // 跳过偶数
}
echo "{$i} ";
}
// 输出: 1 3 5 7 9
// break/continue 接受数字参数(跳出多层循环)
for ($i = 0; $i < 5; $i++) {
for ($j = 0; $j < 5; $j++) {
if ($j === 3) {
break 2; // 跳出两层循环
}
}
}
6.8 替代语法(Alternative Syntax)
PHP 提供了适合嵌入 HTML 模板的替代语法:
<?php // 模板文件:templates/user-list.php ?>
<?php if (!empty($users)): ?>
<table>
<thead>
<tr><th>姓名</th><th>年龄</th></tr>
</thead>
<tbody>
<?php foreach ($users as $user): ?>
<tr>
<td><?= htmlspecialchars($user['name']) ?></td>
<td><?= (int)$user['age'] ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p>暂无用户数据。</p>
<?php endif; ?>
6.9 return / exit / die
<?php
// return — 从函数/文件返回值
function getConfig(string $key): mixed
{
$config = require __DIR__ . '/config.php';
return $config[$key] ?? null;
}
// exit / die — 终止脚本执行
function checkAuth(): void
{
if (!isset($_SESSION['user'])) {
http_response_code(401);
exit('Unauthorized'); // 或 die('Unauthorized')
}
}
// exit 接受整数(CLI 退出码)
exit(0); // 成功
exit(1); // 失败
6.10 业务场景:订单状态机
<?php
declare(strict_types=1);
enum OrderStatus: string
{
case Pending = 'pending';
case Paid = 'paid';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
case Refunded = 'refunded';
}
class OrderStateMachine
{
private const TRANSITIONS = [
OrderStatus::Pending => [OrderStatus::Paid, OrderStatus::Cancelled],
OrderStatus::Paid => [OrderStatus::Shipped, OrderStatus::Cancelled, OrderStatus::Refunded],
OrderStatus::Shipped => [OrderStatus::Delivered],
OrderStatus::Delivered => [OrderStatus::Refunded],
OrderStatus::Cancelled => [],
OrderStatus::Refunded => [],
];
public function __construct(
private OrderStatus $status = OrderStatus::Pending,
) {}
public function transition(OrderStatus $newStatus): self
{
$allowed = self::TRANSITIONS[$this->status] ?? [];
if (!in_array($newStatus, $allowed, true)) {
throw new \RuntimeException(
"不能从 {$this->status->value} 转换到 {$newStatus->value}"
);
}
$this->status = $newStatus;
return $this;
}
public function getStatus(): OrderStatus
{
return $this->status;
}
public function canTransitionTo(OrderStatus $target): bool
{
return in_array($target, self::TRANSITIONS[$this->status] ?? [], true);
}
}
// 使用示例
$order = new OrderStateMachine();
echo $order->getStatus()->value; // pending
$order->transition(OrderStatus::Paid);
echo $order->getStatus()->value; // paid
$order->transition(OrderStatus::Shipped);
echo $order->getStatus()->value; // shipped
// $order->transition(OrderStatus::Pending);
// RuntimeException: 不能从 shipped 转换到 pending
6.11 扩展阅读
上一章:第 5 章 — 运算符 下一章:第 7 章 — 函数