PHP自定义错误处理器不生效主因是被后注册的处理器覆盖,需在入口文件顶部立即调用set_error_handler;致命错误须用register_shutdown_function+error_get_last()捕获;记录日志应包含文件、行号及过滤后的上下文。

PHP 自定义错误处理器不生效?检查 set_error_handler 是否被覆盖
自定义错误处理失效,最常见的原因是其他框架或 Composer 包(比如 Monolog、Whoops)在你调用 set_error_handler 之前已注册了自己的处理器,并且没做链式传递。PHP 只允许一个用户级错误处理器生效,后注册的会完全替换前一个。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在入口文件最顶部(
index.php或public/index.php)立即调用set_error_handler,不要等 autoload 或框架初始化之后 - 若必须兼容已有处理器,改用
set_error_handler($handler, E_ALL, true)(PHP 8.0+),启用链式模式;旧版本需手动保存原处理器并转发 - 确认没在
try/catch中忽略E_ERROR等致命错误——它们不会触发set_error_handler,只能靠register_shutdown_function捕获
记录错误日志时丢失上下文?别只记 $errno 和 $errstr
仅记录错误级别和消息,基本没法定位问题。PHP 的 set_error_handler 回调函数实际接收 5 个参数:$errno、$errstr、$errfile、$errline、$errcontext。最后这个 $errcontext 是当前作用域所有变量的快照,但默认是空数组——因为 PHP 默认禁用它(track_errors = Off)。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在
php.ini中开启track_errors = On(注意:该配置已废弃于 PHP 8.2+,新版本应改用error_get_last()+debug_backtrace()补全) - 务必记录
$errfile和$errline,否则连出错位置都找不到 -
$errcontext含敏感数据(如密码、token),写入日志前要过滤,不能直接print_r
为什么 error_log() 写不进文件?权限、路径、SAPI 模式都得看
用 error_log($msg, 3, '/var/log/php/app.log') 明明指定了路径,却始终没生成日志,大概率不是代码问题,而是运行环境卡住了。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 确认 PHP 进程用户(如
www-data或nginx)对目标目录有写权限,且父目录可执行(chmod 755 /var/log/php) - 路径必须是绝对路径;相对路径行为取决于 SAPI(CLI 下相对当前工作目录,FPM 下可能相对 webroot)
- 某些共享主机禁用
error_log的文件模式(3),此时返回false但不报错,建议加判断:if (false === error_log(...)) { /* fallback to syslog or echo */ }
致命错误(E_ERROR)无法捕获?用 register_shutdown_function 补漏
set_error_handler 对 E_ERROR、E_PARSE、E_CORE_ERROR 无效——它们会让脚本立即中止,不走错误处理器。真正能兜底的,是 register_shutdown_function 配合 error_get_last()。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在入口处注册:
register_shutdown_function(function () { $e = error_get_last(); if ($e && in_array($e['type'], [E_ERROR, E_PARSE, E_CORE_ERROR])) { /* 记录 $e */ } }); - 注意:该函数也会在正常脚本结束时触发,所以必须先判空
$e,再判断类型 - 如果用了输出缓冲(
ob_start),记得在 shutdown 函数里ob_end_flush(),否则日志可能被丢弃
真正难的不是写 handler,而是让每种错误都落到对应通道里:警告进 debug 日志,用户触发的错误进监控系统,致命错误进告警。路径、权限、SAPI 差异、PHP 版本演进,漏掉任何一环,日志就断了。



















