PHP中异常(Exception)与错误(Error)本质不同:Exception需显式throw,而Warning/Notice属Error类,无法被catch(Exception)捕获;须用set_error_handler()转为ErrorException,并配合set_exception_handler()全局兜底。

PHP 里“异常”和“错误”不是一回事,但很多人混着用,结果 try/catch 捕不到 E_WARNING 或 E_NOTICE,最后程序崩了还找不到原因。
catch 不到 Warning 和 Notice 是正常现象
PHP 的 Exception 类只管“抛出来的异常”,比如 throw new Exception()、PDO 在 PDO::ATTR_ERRMODE = PDO::ERRMODE_EXCEPTION 下触发的 PDOException。而 Warning、Notice、Deprecated 属于 Error 类及其子类(PHP 7+),它们默认不会被 catch(Exception $e) 捕获。
常见错误现象:
- 写了
try/catch却依然看到 “Warning: file_get_contents(): failed to open stream…” - 调用
json_decode($bad_json, true)返回null,但没报异常,也没进catch -
foreach(null as $v)直接报Warning并继续执行,根本没机会拦截
解决办法只有两个方向:
- 用
set_error_handler()拦截并转成异常(需手动throw) - 提前校验输入,比如
is_array($data)再foreach,或用json_last_error()查错
想统一捕获所有问题?得靠 set_exception_handler + set_error_handler 配合
全局兜底不能只靠 set_exception_handler(),它只管未被捕获的 Exception 和 Error(PHP 7+)。但默认情况下,Warning 这类错误根本不会走到那里 —— 它们先被 PHP 默认处理器处理,然后继续执行。
所以真要“一网打尽”,得双管齐下:
-
set_error_handler()拦住E_WARNING、E_NOTICE等,内部throw new ErrorException($message, $code, $severity, $file, $line) -
set_exception_handler()作为最后一道防线,处理所有漏网的Exception和Error
注意:set_error_handler() 对 E_ERROR、E_PARSE、E_CORE_ERROR 等致命错误无效,这些错误发生时脚本已无法继续, handler 根本不会被调用。
PDO 查询出错为什么有时报 Warning 有时抛 PDOException?
这完全取决于 PDO::ATTR_ERRMODE 设置。PHP 8.0+ 默认是 PDO::ERRMODE_EXCEPTION,所以 $pdo->query("SELECT * FROM nonexistent") 会直接 throw new PDOException,能被 catch(PDOException $e) 拦住。
但如果你显式设过 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING),那同一条语句就只会触发 E_WARNING,try/catch 完全无效,必须靠 set_error_handler() 或提前检查表是否存在。
关键点:
-
PDO::__construct()失败永远抛PDOException,无论ATTR_ERRMODE怎么设 -
PDOStatement::execute()是否抛异常,严格取决于当前ATTR_ERRMODE值 - 用
errorInfo()可以在不抛异常的前提下拿到错误细节,适合静默容错场景
自定义异常类别名容易踩的坑
写 class MyValidationException extends Exception 没问题,但要注意:PHP 7+ 要求所有可抛出对象必须实现 Throwable 接口,所以继承 Exception 或 Error 才安全。直接 class Foo {} 然后 throw new Foo 会报 Fatal error: Uncaught TypeError。
另外,catch 的顺序很重要:
- 子类异常必须写在父类前面,否则会被父类
catch(Exception $e)提前吞掉 -
catch(Error $e)和catch(Exception $e)必须分开写,不能只写后者试图覆盖所有 -
catch(Throwable $e)可以一并捕获Exception和Error,但会模糊错误类型,调试时难定位
最常被忽略的是:set_exception_handler() 注册的函数,其参数类型声明必须兼容 Throwable,写成 function($e) 最稳妥,写成 function(Exception $e) 会漏掉 Error。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











