php如何自定义错误处理函数_php自定义错误处理函数方法【技巧】

2次阅读

set_Error_handler仅捕获e_warning、e_notice、e_user_error等可恢复错误,不处理e_error、e_parse、e_core_error及未捕获异常;php 7+的typeerror等属于异常,由set_exception_handler处理。

php如何自定义错误处理函数_php自定义错误处理函数方法【技巧】

set_error_handler 能捕获哪些错误

set_error_handler 只能捕获 PHP 的 E_WARNING、E_NOTICE、E_USER_ERROR 这类「可恢复」的运行时错误,**不处理 E_ERROR(如语法错误、致命错误)、E_PARSE、E_CORE_ERROR 或未捕获的异常**。你写错 function 拼写或漏掉分号,根本进不了这个函数——那是解析阶段的事,set_error_handler 压根没机会执行。

常见误判场景:
• 本地开发开了 error_reporting(E_ALL),但生产环境关了部分级别,结果线上收不到通知
• 把 file_get_contents('http://...') 失败当成普通警告来处理,其实它触发的是 E_WARNING,能被捕获,但返回值是 false,得自己判断

  • 必须在错误发生前调用 set_error_handler,比如放在入口文件最顶部
  • 如果启用了 display_errors = On,默认错误仍会输出到页面,需在 handler 里返回 true 才能屏蔽原生输出
  • 想同时记录日志又不想中断流程,handler 函数末尾别 return false(这是默认行为),也别 throw 新异常,除非你真想把它转成异常

自定义 handler 函数的参数和返回值含义

标准签名是 function($errno, $errstr, $errfile, $errline, $errcontext)。其中 $errcontext 容易被忽略:它是错误发生时当前作用域的变量快照(类似 get_defined_vars()),但**默认不开启**——必须在 set_error_handler 第二个参数传 E_ALL 或对应掩码,且 PHP 配置中 zend.ze1_compatibility_mode 关闭(现代版本默认关闭)才有效。

返回值决定后续流程:
• 返回 true:表示你已处理,PHP 不再执行默认错误处理逻辑(比如打印到屏幕)
• 返回 false 或不返回:交还给 PHP 默认机制,可能继续报错甚至终止脚本

立即学习PHP免费学习笔记(深入)”;

  • $errno 是整数,建议用 error_level_to_name($errno) 转成字符串(自己写个映射数组就行)
  • $errstr 可能含敏感路径,上线前记得过滤 $errfile 中的绝对路径,避免泄露
  • 不要在 handler 里调用 trigger_error 或抛出异常,容易引发递归调用崩溃

与 set_exception_handler 混用时的注意事项

两者互不干扰,但实际项目里常一起用:set_exception_handler 捕获 throw 和未 catch 的异常,set_error_handler 捕获警告/通知。问题在于:**PHP 7+ 中类型错误(TypeError)、ParseError 等现在是异常而非错误,不会进 set_error_handler**。

典型踩坑:
• 写了个 function foo(int $x) { },传了字符串,PHP 7.0+ 报 Fatal error: Uncaught TypeError —— 这属于 set_exception_handler 范畴
• 用 @ 抑制错误时,set_error_handler 直接被跳过,连函数都不会调用

  • 若要统一处理,可在 set_exception_handler 里判断异常类型,对 Error 子类(如 TypeError)做特殊记录
  • register_shutdown_function 可补位捕获致命错误(如 E_ERROR),但无法获取完整上下文,仅适合记录“脚本意外终止”
  • 别在 handler 或 exception handler 里做耗时操作(如写文件、发 HTTP 请求),可能拖慢整个请求

生产环境日志落地的最小可行方案

直接写文件最简单,但要注意并发和权限:fopen($log_path, 'a') 在高并发下可能丢日志,file_put_contents($log_path, $msg, FILE_APPEND | LOCK_EX) 更稳妥。关键是格式得让人能快速定位问题。

推荐一行一条结构化日志,包含时间、错误等级、消息、文件行号、请求 URI(如果有):

2024-05-20 14:22:31 [WARNING] array_merge(): Argument #2 is not an array in /var/www/app/helpers.php on line 87 [uri:/api/v1/users]
  • date('Y-m-d H:i:s') 而不是 microtime(true),排查时对齐时间更直观
  • 记录 $_SERVER['REQUEST_URI'] 前先检查是否存在,CLI 脚本里这变量不存在
  • 日志文件路径别写死,从配置读取,并确保 web 用户有写权限(比如 chown www-data:www-data /var/log/myapp/
  • 单文件别超过 100MB,用 logrotate 或代码里判断大小后归档

真正难的不是写 handler,而是怎么让日志里带上下文又不泄露用户数据——比如 $errcontext 里的 $_POST数据库密码字段,得提前过滤。

text=ZqhQzanResources