错误和异常不是一回事
在写 PHP 脚本时,很多人把“错误”和“异常”混着用,其实它们是两码事。比如访问一个不存在的数组键,会触发 Notice;除以零可能抛出 Warning。这些属于“错误”,而“异常”是通过 throw 手动抛出来的,需要用 try-catch 捕获。
举个例子:你写了个脚本去读配置文件,结果文件被误删了。这时候 fopen 失败不会自动抛异常,而是返回 false 并打个 Warning。如果你没检查返回值,程序就可能带着脏数据往下跑,最后输出错得离谱的结果。
用 try-catch 捕住关键操作
数据库操作、远程 API 调用、文件读写这些容易出问题的地方,一定要包在 try-catch 里。虽然 PHP 内部函数大多不主动抛异常,但你可以自己封装。
比如用 PDO 连数据库时,开启异常模式更省心:
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);这样一旦执行 SQL 出错,就会抛出 PDOException,能被 catch 到。
自定义异常处理让日志更清晰
系统默认的报错方式太粗暴,页面直接崩给用户看,不合适。我们可以在脚本开头注册自己的处理器:
set_exception_handler(function($exception) {
error_log("[EXCEPTION] " . $exception->getMessage() .
" in " . $exception->getFile() .
" on line " . $exception->getLine());
echo "系统正忙,请稍后再试。";
});这样即使漏掉某个 try-catch,也不会暴露敏感路径或代码结构。
别让小问题拖垮整个脚本
有个定时任务脚本,每天处理几百个用户数据。某天其中一个用户的数据格式异常,导致脚本直接终止,后面的全没处理。后来加了异常包裹:
foreach ($users as $user) {
try {
processUser($user);
} catch (Exception $e) {
error_log("处理用户 {$user['id']} 失败:" . $e->getMessage());
continue;
}
}现在单个用户的错误不会影响整体运行,还能记录下具体哪条出了问题,方便后续修复。
合理使用 finally 做收尾工作
有些资源需要无论成败都释放,比如锁文件、临时连接。finally 就派上用场了。
$fp = fopen('/tmp/lock.txt', 'w');
try {
if (!flock($fp, LOCK_EX | LOCK_NB)) {
throw new RuntimeException('无法获取锁');
}
// 执行关键操作
doSomething();
} catch (Exception $e) {
error_log($e->getMessage());
throw $e; // 重新抛出
} finally {
fclose($fp); // 保证文件句柄关闭
}这样即使中间出错,锁文件也能正常释放,避免后续任务被卡住。