生产环境应设 display_errors = Off 且 log_errors = On,否则易致静默失败;需确认 error_reporting 级别合理、error_log 路径可写,并区分 FPM/CLI 配置,Fatal Error 需靠 register_shutdown_function 补充捕获。

PHP 错误不显示只记录,先看 display_errors 和 log_errors 是否冲突
生产环境默认关掉错误显示是合理行为,但如果你连 500 都没看到、日志也没内容,大概率是两个开关被同时设错。PHP 不会“智能选择”显示还是记录——它按配置直执行:display_errors = Off 就真不输出到页面,log_errors = On 才往 error_log 写。两者不互斥,但常被误设为 Off + Off,结果就是“静默失败”。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 检查
php.ini中这两项:确认display_errors = Off(生产必须),且log_errors = On(否则无日志) - 若用
.htaccess或ini_set()动态覆盖,优先查运行时生效值:var_dump(ini_get('display_errors'), ini_get('log_errors')); -
error_log路径必须可写,常见坑是设了error_log = /var/log/php_errors.log但 PHP 进程用户(如www-data)没权限写入
用 error_reporting 控制哪些错误进日志,别全靠 E_ALL
即使 log_errors = On,如果 error_reporting 级别太低(比如只设 E_ERROR),E_NOTICE 或 E_DEPRECATED 就不会记录——而这类错误恰恰容易引发后续异常。但反过来,E_ALL 在老版本 PHP(如 5.6)可能包含已废弃的报告类型,导致日志刷屏或性能抖动。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- PHP 7.2+ 推荐用
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT,兼顾完整性与干净度 - 避免在代码里用
error_reporting(0)全局屏蔽,它会覆盖php.ini设置,调试时难发现 - 若用 Composer 自动加载,
autoload.php顶部的error_reporting()调用会生效,注意检查
CLI 和 FPM 的错误日志路径可能不同,别只盯一个
Web 请求走 PHP-FPM,命令行跑脚本走 CLI SAPI,两者读的不是同一份 php.ini,error_log 默认路径也不同:fpm 常写到 /var/log/php-fpm/www-error.log,cli 默认输出到 stderr(终端),除非你显式配置了 error_log。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 查 FPM 配置:看
www.conf里的php_admin_value[error_log],不是php.ini里的那个 - 查 CLI 配置:
php --ini看加载的 ini 文件,再用php -r "echo ini_get('error_log');"确认路径 - 临时测试 CLI 日志:加参数运行
php -d error_log=/tmp/cli.log script.php,比改全局配置快
try/catch 捕获不到 Fatal Error,得靠 register_shutdown_function
很多“没报错也没日志”的情况,其实是 Fatal Error(如调用未定义函数、内存耗尽)触发了 PHP 紧急终止——它不经过 try/catch,也不会受 error_reporting 控制,但会走 log_errors 流程。前提是没被 register_shutdown_function 里提前 exit 或 ob_clean 干扰。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在入口文件(如
index.php)开头注册钩子:register_shutdown_function(function () { $e = error_get_last(); if ($e && in_array($e['type'], [E_ERROR, E_PARSE, E_CORE_ERROR])) error_log(print_r($e, 1)); }); - 注意:FPM 下该钩子能记录,但 CLI 下若脚本直接
exit可能来不及执行,优先保证log_errors = On更可靠 - 某些扩展(如 Xdebug)会干扰 shutdown 流程,调试时可临时禁用
真正麻烦的是错误被多层拦截:Nginx 的 fastcgi_intercept_errors on 会把 500 当成普通响应吞掉;某些框架的异常处理器又把 throw new Error 转成 200 响应。查问题得一层层剥开,别只盯着 PHP 配置。











