php异常必须用try-catch捕获,set_error_handler仅处理错误;catch不生效常因未抛异常、非exception子类、未用throwable(php 7+)、顺序错误或变量作用域问题;finally不执行于exit/die,且不能替代事务回滚;set_exception_handler仅为最后防线,不可恢复执行。

PHP 中的异常只能被 try-catch 捕获,其他方式(比如 set_error_handler)处理的是错误(E_WARNING 等),不是异常;想真正“捕获” throw 出来的对象,必须用 try-catch。
catch 不生效?先确认是不是真抛了异常
常见假象是“写了 catch 却没进”,其实根本没抛出异常,或者抛的不是 Exception 子类:
-
file_get_contents("missing.txt")默认只触发E_WARNING,不抛异常——得配合@抑制再手动throw,或启用stream_context_set_default(['http' => ['ignore_errors' => true]])后自己判断返回值 -
mysql_connect()(已废弃)这类函数返回false,不是抛异常;PDO模式下才默认抛PDOException - 自定义异常类没继承
Exception,比如class MyError {},那catch (Exception $e)完全捕不到 - 子类异常写在父类后面:
catch (Exception $e)写在catch (ValidationException $e)前面,后者永远进不去
PHP 7+ 必须用 Throwable,否则漏掉 Error
Exception 只覆盖传统异常,PHP 7+ 引入的 Error(如 TypeError、ParseError)也实现 Throwable 接口,但不会被 catch (Exception $e) 捕获:
- 正确写法:
catch (Throwable $e)—— 这是 PHP 7+ 的兜底底线 - 若需区分处理,顺序应为:
catch (TypeError $e)→catch (Exception $e)→catch (Throwable $e) - 示例:
json_encode(NAN)在 PHP 7.3+ 中抛TypeError,写catch (Exception $e)就会跳过
finally 并不“总是执行”,两个细节常被忽略
finally 确实会在绝大多数情况下执行,但有两个硬性例外:
- 如果
try或catch中调用了exit()或die(),finally不会执行——这点和 Java 不同 - 变量作用域问题:
$fp = fopen(...)在try里声明,finally里直接用fclose($fp)会报Undefined variable;得提前声明$fp = null或用isset($fp)判断 - 数据库事务场景下,
finally不能替代rollback():该回滚的必须在catch里做,finally只负责关连接
全局未捕获异常靠 set_exception_handler,但它不恢复执行
set_exception_handler 不是 try-catch 的替代品,而是最后一道防线:
- 它只在异常完全没人
catch时触发,且一旦进入,脚本就终止,无法“继续执行” - 适合做日志记录、发送告警、返回标准化错误页,但别指望它能救活流程
- 注册必须在脚本早期完成(如入口文件开头),否则可能失效
- 若同时用了
set_error_handler,注意它只转非致命错误(E_WARNING等)为ErrorException,对Fatal Error无效
最易被忽略的一点:throw new Exception() 的第二参数是整型错误码,传字符串会静默转成 0,导致后续 $e->getCode() 判断失效。业务中需要语义化标识时,建议用自定义异常类,而不是依赖错误码字符串。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











