PHP流程控制
约 2650 字大约 9 分钟
PHPWeb后端入门
2026-03-04
在任何编程语言中,流程控制 (Control Flow) 都是管理代码执行轨迹的核心机制。如果没有流程控制,代码只能像流水账一样从头读到尾。掌握流程控制,就像掌握了铁路系统的调度开关,你可以决定列车(代码执行)是直行、变道、循环运行还是紧急停车。
1. 流程控制简述
最基础的执行模式是 顺序结构 (Sequential Structure)。 即程序会严格按照代码的物理书写位置,自上而下、逐行执行所有的指令,确保没有任何一句代码被遗漏或跳过。
Start→Statement 1→Statement 2→⋯→End
💡 核心概念
代码执行的默认顺序是从上到下,每一次“流程控制”实际上都是对这种默认顺序的打破。
2. 分支结构
分支结构允许程序根据 条件表达式 (Conditional Expression) 返回的布尔值 (true / false) 来选择不同的执行路径。
2.1 if...else 语句
这是最基本的选择结构。
$score = 85;
if ($score >= 60) {
echo "恭喜你,及格了!";
} else {
echo "很遗憾,不及格。";
}2.2 if...elseif...else 多重判断
当面对多维度的业务判断时,我们需要多径选择。
if ($score >= 90) {
echo "优秀";
} elseif ($score >= 80) {
echo "良好";
} else {
echo "加油";
}2.3 switch 语句
当条件是具体的离散值(如状态码、选项等)而非范围时,switch 更清晰。
$day = "Monday";
switch ($day) {
case "Monday":
echo "新的一周开始了!";
break; // 别忘了 break,否则会通过性执行(Fall-through)
case "Friday":
echo "周末快到了!";
break;
default:
echo "今天是平凡的一天。";
}if...elseif vs switch
为了更直观地理解两种分支结构在“寻路”时的区别,你可以通过下方的交互组件来感受数据流向的差异。
核心观察点:
if...elseif是“阶梯式”的:必须经过上一层判断(即便不成立),才能进入下一层判断。它是纵向深入的排查。switch是“集线器式”的:它首先提取出目标值,然后在 Switch 集线器处像路由一样,直接分发指向对应的匹配入口。
if...elseif...else
switch (模拟值匹配)
注: Switch更适合定值匹配,这里仅为流程对比
计算分档: 'B'
小试牛刀:电商打折计算器
利用刚学的 if...elseif...else,我们来写一个电商 VIP 折扣规则:
- 消费满 1000 元,打 8 折 (
* 0.8) - 消费满 500 元,打 9 折 (
* 0.9) - 不满 500 元,不打折原价
$totalAmount = 650;
$finalPrice = $totalAmount;
if ($totalAmount >= 1000) {
$finalPrice = $totalAmount * 0.8;
echo "尊贵的VIP,已为您打8折。<br>";
} elseif ($totalAmount >= 500) {
$finalPrice = $totalAmount * 0.9;
echo "感谢支持,已为您打9折。<br>";
} else {
echo "还差一点就能享受折扣啦。<br>";
}
echo "最终需要支付:" . $finalPrice . " 元";3. 循环结构 (Loop Structure)
为了最大限度地避免代码重复并提高系统运行效率,遇到需要反复执行的操作时必须引入循环结构。
一个标准的循环通常包含四个核心要素:
- 初始化 (Initialization):
$i = 0 - 边界条件 (Condition):
$i < 10 - 循环体 (Loop Body):
{ echo $i; } - 状态迭代 (Iteration):
$i++
3.1 for 循环
最适用于已知明确执行次数的场景。
for ($i = 1; $i <= 5; $i++) {
echo "第 $i 次循环 <br>";
}3.2 while 循环
秉持先判断后执行的安全策略。
$i = 1;
while ($i <= 5) {
echo "Number: $i <br>";
$i++;
}3.3 do...while 循环
无论条件如何,至少执行一次循环体。
$i = 100; // 即使条件不满足
do {
echo "我至少会被输出一次";
$i++;
} while ($i <= 5);小试牛刀:嵌套循环输出“星号金字塔”
在处理二维数据(如表格、图形)时,我们经常需要循环嵌套(一个循环里面,包含着另一个循环)。 下面我们通过两层 for 循环,输出一个 5 层的星号 * 金字塔:
$floors = 5;
// 外层循环:控制整体的行数(1 到 5 行)
for ($i = 1; $i <= $floors; $i++) {
// 内层循环 1:负责打印星号前面的空格 (逐行递减)
for ($j = 1; $j <= $floors - $i; $j++) {
echo " "; // HTML 中的占位空格
}
// 内层循环 2:负责打印星号 (每行有 2 * $i - 1 个)
for ($k = 1; $k <= 2 * $i - 1; $k++) {
echo "*";
}
echo "<br>"; // 这一行打印完毕,输出换行符
}🧠 挑战: 你能在大脑里推演一遍,当外层变量
$i = 2时,里面的$j和$k循环分别由于满足什么边界条件,而各自循环了多少次吗?
4. 跳转语句
在循环结构持续运行期间,我们常常需要根据突发状况人为干预。这就是 break (跳出) 和 continue (继续/跳过) 的作用。
Break vs Continue 的控制流
这是一个抽象且容易混淆的概念。让我们通过下方“小火车查站”的交互动画来演示两者的区别。
核心观察点: 控制面板设置了当
$i = 3时触发。
- Break 模式: 小火车(控制流)遇到3时,直接爆炸终止,循环彻底死亡,后续的 4 和 5 永远不会抵达。
- Continue 模式: 小火车(控制流)遇到3时,触发跳过魔法,代码不会执行下方的
echo输出,但火车会继续开往 4 和 5。
📝 代码对比
通过上面的动画,我们可以清晰地理解以下两段代码的执行差异:
for ($i = 1; $i <= 5; $i++) {
if ($i == 3) {
break; // 循环在 i=3 时彻底死亡
}
echo $i . " ";
}
// 输出: 1 2for ($i = 1; $i <= 5; $i++) {
if ($i == 3) {
continue; // 仅跳过 i=3 这一轮
}
echo $i . " ";
}
// 输出: 1 2 4 55. 异常处理
为了保障服务器端程序的绝对健壮性,必须建立完善的兜底机制。PHP 提供了 try...catch...finally 结构。
异常处理机制模型
可以将异常处理想象成一个“安全网”。
代码示例
function divide($dividend, $divisor) {
if ($divisor == 0) {
throw new Exception("除数不能为零");
}
return $dividend / $divisor;
}
try {
// 尝试执行危险代码
echo divide(10, 0);
} catch (Exception $e) {
// 捕获异常
echo "🛑 错误捕获: " . $e->getMessage();
} finally {
// 无论是否出错,都会执行
echo "<br>处理结束。";
}6. 文件包含语句
在庞大的项目中,我们通过文件包含实现代码复用(如头部、底部、配置文件)。
| 语句 | 特点 | 失败时后果 | 适用场景 |
|---|---|---|---|
| include | ✋ 宽松 | Warning (警告),脚本继续执行 | 模板片段、非关键视图 |
| require | 🛑 严格 | Fatal Error (致命错误),脚本终止 | 核心配置、数据库连接 |
| include_once | 🔄 防重 | 同 include,但自动检查是否已引入 | 防止函数重定义 |
| require_once | 🔄 防重 | 同 require,但自动检查是否已引入 | 核心类库引入 |
// config.php
<?php $db_host = 'localhost'; ?>
// index.php
<?php
require 'config.php'; // 如果 config.php 不存在,页面直接挂掉
include 'header.php'; // 如果 header.php 不存在,页面只少个头,内容还在
?>小试牛刀:模块化网页拼装
在过去构建大型网站时(如新闻站/博客),每个页面的头部导航和底部版权都是重复的。充分利用文件包含语句,我们可以把这些公共代码剥离为独立文件,让主页面只关心自己的核心业务。
你可以尝试创建下面这 3 个文件并理解它们的组装关系:
<!DOCTYPE html>
<html>
<head>
<title>我的模块化小站</title>
</head>
<body>
<nav>
<a href="index.php">首页</a> |
<a href="about.php">关于我们</a>
</nav>
<hr> <hr>
<footer>
<p>版权所有 © 2026 PHP Fullstack</p>
</footer>
</body>
</html><?php
// 1. 引入公共大头部 (HTML的开始标签也在里面)
include 'header.php';
?>
<!-- 2. 页面专属独立内容 -->
<main>
<h1>欢迎来到主页!</h1>
<p>这是用 PHP include技术,像搭积木一样拼出来的网页。</p>
</main>
<?php
// 3. 引入公共大底部 (HTML的闭合标签在这里)
include 'footer.php';
?>7. 实战项目:多国货币汇率计算器
本节课的终极任务是完成一个汇率计算器。这个项目将串联我们学到的:if分支、Switch、While 循环、Break/Continue 以及异常处理。
项目需求
- 定义一组货币汇率数据(基准:CNY)。
- 模拟用户输入金额和目标货币。
- 计算兑换后的金额。
- 挑战点:
- 如果金额小于等于 0,抛出异常。
- 使用循环遍历查找货币,找到后使用
break提高效率。 - 如果货币不支持,提示错误。
核心代码实现
新建文件 currency_converter.php:
<?php
// 1. 定义汇率数据(模拟数据库或配置文件)
// 结构: 货币代码 => min: (对人民币汇率)
$rates = [
'USD' => 0.14, // 1 CNY = 0.14 USD
'EUR' => 0.13,
'JPY' => 20.5,
'GBP' => 0.11
];
// 2. 模拟用户输入
$amount = 100; // 只有 100 元
$targetCurrency = 'USD';
/**
* 汇率计算核心逻辑
*/
echo "=== 💰 汇率计算器启动 ===<br>";
try {
// 3. 异常检测:金额合法性
if ($amount <= 0) {
throw new Exception("金额必须大于 0!");
}
$isFound = false; // 标记变量
$result = 0;
// 4. 循环结构:查找汇率
// 这里的 foreach 本质上是循环的一种
foreach ($rates as $code => $rate) {
// 5. 分支结构:跳过不想处理的货币 (演示 continue)
if ($code == 'JPY') {
echo "暂不支持日元兑换 (演示 continue)...<br>";
continue;
}
// 6. 分支结构:找到目标货币
if ($code == $targetCurrency) {
$result = $amount * $rate;
$isFound = true;
// 7. 跳转语句:找到后立即结束循环,节省资源 (演示 break)
echo "✅ 找到汇率: $code<br>";
break;
}
}
// 8. 结果输出
if ($isFound) {
echo "💱 兑换结果: $amount CNY = $result $targetCurrency";
} else {
echo "❌ 未找到目标货币或不支持兑换。";
}
} catch (Exception $e) {
// 9. 异常捕获
echo "⚠️ 发生错误: " . $e->getMessage();
} finally {
echo "<br>=== 计算结束,欢迎下次使用 ===";
}
?>思考题
- 如果把
break去掉,程序逻辑是对的吗?效率会有什么影响? - 尝试把
if...else逻辑改成switch结构来实现货币匹配。 - 如果用户输入了一个不在
$rates数组中的货币代码,程序会如何响应?你能改进提示信息吗?
