PHP的try-catch结构简明教程
基本语法
try {
// 可能出错的代码
} catch (Exception $e) {
// 出错时执行的代码
} finally {
// 无论是否出错都会执行的代码(可选)
}运行逻辑
try块:程序先执行try中的代码
正常情况:如果try中的代码全部执行成功,跳过catch块
异常情况:如果try中抛出异常,立即停止try块执行,跳转到匹配的catch块
finally块:无论是否发生异常,最后都会执行finally中的代码
简单示例
示例1:基本用法
try {
$result = 10 / 0; // 这里会抛出除零错误
echo "结果: ".$result;
} catch (DivisionByZeroError $e) {
echo "出错了: ".$e->getMessage(); // 输出错误信息
} finally {
echo "<br>计算结束"; // 无论是否出错都会执行
}示例2:文件操作
try {
$file = fopen("不存在的文件.txt", "r");
if (!$file) {
throw new Exception("无法打开文件"); // 手动抛出异常
}
// 读取文件内容...
} catch (Exception $e) {
echo "文件操作错误: ".$e->getMessage();
} finally {
if (isset($file) && $file) {
fclose($file); // 确保文件资源被释放
}
}示例3:数据库操作
try {
$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
$stmt = $pdo->query("SELECT * FROM 不存在的表");
$results = $stmt->fetchAll();
} catch (PDOException $e) {
echo "数据库错误: ".$e->getMessage();
} finally {
$pdo = null; // 关闭数据库连接
}关键点总结
try:包裹可能出错的代码
catch:捕获特定类型的异常(可以多个catch块)
finally:清理资源的理想位置
$e->getMessage():获取错误信息
可以手动用
throw new Exception()抛出异常
这种结构让错误处理更加优雅,避免了代码被大量if-else判断语句污染。
复杂示例
以下是一个完整的数据库事务处理系统示例,展示多层try-catch、自定义异常和事务回滚:
实现了完整的银行转账事务处理流程
包含多层try-catch嵌套结构
使用自定义异常类区分业务异常和系统异常
数据库事务确保数据一致性
完善的错误处理和资源清理机制
输入验证与业务逻辑分离
支持事务回滚和错误日志记录
<?php
class DatabaseException extends Exception {}
class ValidationException extends Exception {}
class TransactionSystem {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function transferFunds($fromAccount, $toAccount, $amount) {
try {
// 开启事务
$this->pdo->beginTransaction();
// 验证输入
$this->validateInput($fromAccount, $toAccount, $amount);
// 检查账户余额
$this->checkBalance($fromAccount, $amount);
// 执行转账
$this->executeTransfer($fromAccount, $toAccount, $amount);
// 记录交易日志
$this->logTransaction($fromAccount, $toAccount, $amount);
// 提交事务
$this->pdo->commit();
return "转账成功: {$amount}元 从 {$fromAccount} 到 {$toAccount}";
} catch (ValidationException $e) {
// 输入验证错误,无需回滚
return "验证错误: " . $e->getMessage();
} catch (DatabaseException $e) {
// 数据库错误,回滚事务
if ($this->pdo->inTransaction()) {
$this->pdo->rollBack();
}
return "数据库错误: " . $e->getMessage();
} catch (Exception $e) {
// 其他未知错误,回滚事务
if ($this->pdo->inTransaction()) {
$this->pdo->rollBack();
}
error_log("未知错误: " . $e->getMessage());
return "系统错误,请稍后再试";
} finally {
// 清理资源
$this->cleanup();
}
}
private function validateInput($from, $to, $amount) {
if (!is_numeric($amount) || $amount <= 0) {
throw new ValidationException("金额必须为正数");
}
if ($from === $to) {
throw new ValidationException("不能转账给自己");
}
}
private function checkBalance($account, $amount) {
try {
$stmt = $this->pdo->prepare("SELECT balance FROM accounts WHERE id = ?");
$stmt->execute([$account]);
$balance = $stmt->fetchColumn();
if ($balance < $amount) {
throw new ValidationException("账户余额不足");
}
} catch (PDOException $e) {
throw new DatabaseException("查询余额失败: " . $e->getMessage());
}
}
private function executeTransfer($from, $to, $amount) {
try {
// 扣除转出账户金额
$stmt = $this->pdo->prepare(
"UPDATE accounts SET balance = balance - ? WHERE id = ?"
);
$stmt->execute([$amount, $from]);
// 增加转入账户金额
$stmt = $this->pdo->prepare(
"UPDATE accounts SET balance = balance + ? WHERE id = ?"
);
$stmt->execute([$amount, $to]);
} catch (PDOException $e) {
throw new DatabaseException("转账操作失败: " . $e->getMessage());
}
}
private function logTransaction($from, $to, $amount) {
try {
$stmt = $this->pdo->prepare(
"INSERT INTO transactions (from_account, to_account, amount) VALUES (?, ?, ?)"
);
$stmt->execute([$from, $to, $amount]);
} catch (PDOException $e) {
throw new DatabaseException("交易记录失败: " . $e->getMessage());
}
}
private function cleanup() {
// 关闭数据库连接等清理操作
}
}
// 使用示例
try {
$pdo = new PDO('mysql:host=localhost;dbname=bank', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$system = new TransactionSystem($pdo);
echo $system->transferFunds('A1001', 'B2002', 500);
} catch (PDOException $e) {
echo "数据库连接失败: " . $e->getMessage();
}
?>