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
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) { … } 更干净。
"\$items is empty: " . empty(\$items))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()。