php try catch 默认无法捕获 notice 和 warning,因其属于错误机制而非异常体系;需用 set_error_handler 转为 errorexception 才能捕获;pdo 异常须显式启用 errmode_exception;自定义异常应继承 exception;finally 中 return 会覆盖 catch 返回值。

PHP try catch 捕获不到 Notice 和 Warning?
默认情况下,try catch 只捕获 Exception 及其子类(比如 RuntimeException),而 E_NOTICE、E_WARNING 这类错误是 PHP 错误机制的一部分,不属于异常体系。
常见现象:代码里写了 try { echo $undefined_var; } catch (Exception $e) { },但页面照样报 Notice: Undefined variable,且没进 catch 块。
- 想统一处理所有错误类型,得用
set_error_handler()配合手动抛出异常 - 注意:不能在
set_error_handler里直接 throw,因为部分错误(如E_ERROR)会绕过它;稳妥做法是把E_NOTICE/E_WARNING转成ErrorException再 throw -
error_reporting(E_ALL)要提前设置,否则低级别错误可能被静默忽略
set_error_handler(function($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) return;
throw new ErrorException($message, 0, $severity, $file, $line);
});捕获 PDO 数据库异常必须开启异常模式
很多人写了 try { $pdo->query("SELECT * FROM nonexist"); } catch (PDOException $e) { } 却没生效——根本原因是 PDO 默认不抛异常,而是返回 false 并触发警告。
- 连接时必须显式设置
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION - 只设连接选项不够,如果用
PDOStatement执行查询,异常仍来自该对象,但前提是连接层已启用异常模式 - 别漏掉
PDO::ATTR_EMULATE_PREPARES => false,否则某些 SQL 错误(如语法错)可能被模拟层吞掉,不触发异常
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
]);自定义异常类要继承 Exception 或 Throwable
PHP 7+ 支持 Throwable,但直接 class MyError extends Throwable 是非法的——Throwable 是接口,不能被继承,只能实现,而实际可用的基类只有 Exception 和 Error。
立即学习“PHP免费学习笔记(深入)”;
- 业务异常建议继承
Exception,比如class ValidationException extends Exception - 如果需要区分“程序错误”和“业务错误”,可约定用不同类名,但不要试图继承
Error(它是 PHP 内部错误专用,用户代码 throw 它会被拒绝) - 构造时传参顺序固定:
__construct($message, $code = 0, Throwable $previous = null),别漏掉第三个参数,否则链式异常(比如捕获后重抛)会断掉上下文
finally 中 return 会覆盖 catch 里的 return
这是最容易踩的逻辑坑:你以为 catch 返回了错误信息,结果前端收到的是 finally 里的值。
-
finally块只要执行了return,就一定会覆盖try或catch中的return - 即使
catch抛出了新异常,finally里的return也会压制它(PHP 7.1+ 会报Uncaught Throwable警告) - 安全做法:在
finally里只做清理(关文件、释放锁),绝对不要return或throw
function risky() {
try {
throw new Exception('boom');
} catch (Exception $e) {
return 'handled';
} finally {
return 'ignored? no — this wins'; // ← 实际返回这个
}
}异常处理真正的复杂点不在语法,而在错误分类和传播路径的设计。比如数据库超时该转成 ServiceUnavailableException 还是重试?日志里要不要保留原始堆栈?这些没法靠 try catch 自动解决。











