PHP调试可不用xdebug:①debug_backtrace()查调用链,需加DEBUG_BACKTRACE_IGNORE_ARGS防敏感信息;②assert()作条件式调试开关,开发启用、生产禁用零开销;③register_shutdown_function()捕获致命错误,配合error_get_last()兜底。

PHP 调试不一定要靠 xdebug 或 IDE 断点;很多隐藏但实用的手段,能在没有调试环境、线上受限、或临时排查时快速定位问题。
用 debug_backtrace() 看调用链而不是只看 echo
单纯 var_dump($var) 只告诉你“值是多少”,但不知道“谁传进来的、在哪被改的”。debug_backtrace() 能立刻补全上下文:
function handle_user($id) {
error_log(print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), true));
// …
}
常见误用:debug_backtrace() 默认包含全部参数(可能泄露敏感数据),线上务必加 DEBUG_BACKTRACE_IGNORE_ARGS;另外它开销不小,别放在高频循环里。
- 想精简输出?传入
DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS - 只想看最近 3 层?用
debug_backtrace(…, 3) - 配合
error_log()写入日志比echo更安全,避免污染 HTTP 响应
assert() 不只是断言,还能当条件式调试开关
很多人以为 assert() 只用于测试,其实它在开发中可作轻量级调试钩子——启用时触发,禁用时零开销:
立即学习“PHP免费学习笔记(深入)”;
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_BAIL, 0);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_CALLBACK, function($file, $line, $code) {
error_log("[ASSERT] {$file}:{$line} — {$code}");
});
// 任意位置插入
assert('$user !== null && $user->is_active');
assert('count($items) > 0 && $items[0]->id > 100');
关键点:assert() 在 zend.assertions = 1(开发)下生效,生产设为 -1 就彻底移除,不执行也不编译。比 if (DEBUG) { … } 更干净。
- 字符串形式的 assert 表达式支持变量插值(如
"\$items is empty: " . empty(\$items)) - 不要在 assert 中调用有副作用的函数(如
assert('save_log()')),因为上线后这行会被跳过
用 register_shutdown_function() 捕获最后的致命错误
try/catch 抓不住 Fatal error,而 set_error_handler() 对致命错误无效。这时候 register_shutdown_function() 是兜底关键:
register_shutdown_function(function () {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
error_log(sprintf('[FATAL] %s in %s:%d', $error['message'], $error['file'], $error['line']));
// 可选:记录 trace(需手动触发 debug_backtrace)
}
});
注意:error_get_last() 只返回最后一次错误,且仅在脚本终止前有效;如果用了 fastcgi_finish_request(),得在它之前调用。
- 别在里面做耗时操作(如发邮件、写大文件),容易超时
- 结合
ob_get_contents()可捕获已输出但未发送的内容,辅助判断输出中断点
这些技巧真正难的不是写法,而是记得在部署前关掉 assert、清空临时 error_log 调用、以及确认 display_errors=Off —— 很多“线上突然变慢”就源于忘了删掉一个 debug_backtrace()。











