← 返回首页

PHP 中 Error 与 Exception 的区别及异常处理最佳实践

一句话结论

Error 表示引擎级严重错误,Exception 表示业务级可恢复异常,二者共同实现 Throwable 接口。

体系讲解

原理:PHP 7 的异常体系重构

PHP 7 之前,引擎级错误(如类型错误、除零)通过 E_ERROR、E_WARNING 等错误级别触发,无法用 try-catch 捕获,只能通过 set_error_handler() 处理部分非致命错误。致命错误会直接终止脚本,开发者束手无策。

机制:Error vs Exception 的核心区别

特性 Error Exception
含义 引擎/编译级严重错误 业务逻辑可恢复异常
常见子类 TypeError、ParseError、DivisionByZeroError RuntimeException、LogicException、InvalidArgumentException
是否应该捕获 通常不应在业务层捕获(说明代码有 bug) 应该按场景捕获并处理
公共接口 Throwable Throwable
可以 catch PHP 7+ 可以 一直可以

三种错误处理机制的关系

  1. try-catch-finally:捕获 Throwable(含 Error 和 Exception)。finally 块无论是否抛出异常都会执行。
  2. set_error_handler():捕获传统错误级别(E_WARNING、E_NOTICE 等)。不能捕获 E_ERROR。
  3. set_exception_handler():兜底处理未被 catch 的异常/错误,通常用于全局异常日志记录。

结论:对开发的直接影响

  • 全局异常处理器(set_exception_handler)是生产环境的安全网,应在项目入口注册。
  • 业务层 catch 应只捕获预期的 Exception 子类,不要无脑 catch (\Throwable $e) 吞掉所有错误。
  • TypeError 等 Error 说明代码本身有缺陷(传参类型错误),应修复代码而非捕获 Error。

考察意图

  • 验证候选人是否理解 PHP 7+ 的异常体系结构,而非停留在 PHP 5 的认知
  • 考察对 Error 和 Exception 设计意图的区分——是否知道"什么该捕获、什么不该捕获"
  • 判断候选人是否具备工程化的异常处理习惯(自定义异常、全局兜底、日志记录)

追问链

  1. 为什么不应该在业务代码中 catch (\Throwable $e) 或 catch (\Error $e)?

    简答:Error 通常意味着代码 bug(如类型错误、调用不存在的方法),应该暴露并修复,而非捕获后静默处理。无差别捕获 Throwable 会掩盖真实问题,导致 bug 难以排查。业务层应只 catch 具体的 Exception 子类。

  2. set_error_handler() 能捕获所有传统错误吗?

    简答:不能。set_error_handler() 能捕获 E_WARNING、E_NOTICE、E_USER_* 等,但不能捕获 E_ERROR、E_PARSE、E_CORE_ERROR、E_COMPILE_ERROR。致命错误只能通过 register_shutdown_function() + error_get_last() 做最后的日志记录。

  3. finally 块在什么场景下必须使用?

    简答:需要释放资源的场景:关闭文件句柄、数据库连接、释放锁等。无论 try 块是否抛出异常,finally 都会执行,确保资源不泄漏。即使 try 或 catch 中有 return,finally 仍然会执行。

  4. 如何设计项目的自定义异常体系?

    简答:建议按业务领域划分异常层级。定义一个项目根异常(如 AppException extends RuntimeException),再按模块细分(OrderException、PaymentException)。每个异常携带错误码($code)和上下文数据。API 项目通过全局异常处理器将异常转换为统一的错误响应格式。