try/catch仅捕获Exception或Throwable子类异常,不捕获E_NOTICE、E_WARNING等传统错误;需用set_error_handler转错误为异常或@抑制符配合返回值检查。

PHP 5.3+ 中 try/catch 能捕获哪些异常
try/catch 只捕获继承自 Exception 或 Throwable 的异常,**不捕获 E_NOTICE、E_WARNING 等传统错误**。这是最常被误解的一点:写个 try 包住 file_get_contents('missing.txt'),照样报 Warning: file_get_contents(...): failed to open stream...,根本进不了 catch。
实际可用的组合方式只有两种:
- 用
set_error_handler()把错误转成异常,再由try/catch统一处理(推荐) - 对特定函数搭配错误抑制符
@+ 手动检查返回值(简单场景可用,但会丢失错误上下文)
用 set_error_handler 实现错误转异常的兼容写法
PHP 7+ 支持把大多数错误(除 E_PARSE、E_COMPILE_ERROR 等编译期错误)转为 Error 实例,而 PHP 5.3–5.6 只能靠自定义 handler 抛出 Exception。下面这段代码在 PHP 5.3 至 8.3 均可运行:
function error_to_exception($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return;
}
throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler('error_to_exception');
之后你就能这样写:
立即学习“PHP免费学习笔记(深入)”;
try {
$data = file_get_contents('/nonexistent');
} catch (ErrorException $e) {
echo "文件读取失败:", $e->getMessage();
}
- 注意必须在
set_error_handler()后再触发错误,否则无效 -
ErrorException是 PHP 内置类,兼容所有版本,比手写新异常类更稳妥 - 别忘了在 CLI 模式下,
error_reporting(E_ALL)可能默认关闭部分级别,建议显式开启
@ 错误抑制符的真实行为与风险
@ 不是“静默忽略”,而是临时将 error_reporting 设为 0,执行完立刻恢复。它**无法抑制 Parse Error、Fatal Error,也不能跨作用域生效**(比如函数内用了 @,但错误发生在被调用的另一个函数里,仍会报出)。
常见误用:
-
@mysql_connect(...)—— 已废弃且不安全,PHP 7+ 直接报致命错误 -
@$arr['missing_key']—— 有效,但掩盖了本该用isset()或??处理的逻辑问题 -
@fopen(...) ?: die('fail')—— 表面简洁,实则跳过了错误类型判断(是权限问题?路径不存在?磁盘满?)
真正需要容错时,优先用 is_readable()、file_exists() 等前置检查,而非依赖 @。
PHP 7+ 的 Throwable 兼容陷阱
PHP 7 引入 Throwable 作为 Exception 和 Error 的共同父接口,但 PHP 5.x 完全不识别该接口。若你在共用代码中写:
catch (Throwable $e) { ... }
—— 在 PHP 5.6 下直接解析失败,报 Parse error: syntax error, unexpected 'catch'。
安全写法是分层捕获:
try {
// ...
} catch (Error $e) {
// PHP 7+
} catch (Exception $e) {
// PHP 5.3+
}
- 不要在 PHP 5 兼容项目中使用
catch (Throwable $e) -
Error类(如TypeError、ParseError)在 PHP 5 中不存在,不能作为catch参数 - 如果必须统一处理,就只 catch
Exception,再用class_exists('Error')判断是否支持Error分支
跨版本异常兼容的核心,不是语法糖,而是对错误生命周期的理解:PHP 的错误和异常从来就不是一回事,硬凑到一个 catch 里,迟早掉坑里。











