强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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+)

matchswitch 的现代替代,返回值、使用严格比较、支持复合条件。

<?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 对比

特性switchmatch
返回值❌ 需要变量赋值✅ 是表达式,直接返回
比较方式松散 (==)严格 (===)
多值匹配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 章 — 函数