調試是開發核心技能。本文系統覆蓋 PHP 內建錯誤機制、Xdebug/ZendDebugger 進階工具、業務邏輯排查方法論、非功能性問題 (內存、性能、死循環) 定位、單元測試與生產環境最佳實踐。
; 顯示所有錯誤、警告、通知、嚴格標準 error_reporting = E_ALL | E_STRICT display_errors = On display_startup_errors = On log_errors = On error_log = /var/log/php/dev_error.log
; 隱藏詳細錯誤,記錄日誌 error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT display_errors = Off display_startup_errors = Off log_errors = On error_log = /var/log/php/prod_error.log ; 可選:syslog、發送郵件、Sentry 等 ; error_log = syslog
網絡/資源函數常提供錯誤代碼/字串輸出參數:
<?php
$fp = fsockopen('example.com', 80, $errno, $errstr, 30);
if (!$fp) {
// $errno: 系統錯誤碼, $errstr: 可讀描述
error_log("連接失敗: $errstr ($errno)");
// 處理失敗...
}
// 類似函數:pfsockopen, stream_socket_client, stream_socket_server
// 數據庫:mysqli_connect_errno(), mysqli_connect_error()
// Socket:socket_last_error(), socket_strerror()
?>
zend_extension=xdebug.so xdebug.mode = debug,develop,trace,profile xdebug.start_with_request = yes xdebug.client_host = 127.0.0.1 xdebug.client_port = 9003 xdebug.idekey = PHPSTORM xdebug.var_display_max_depth = 5 xdebug.var_display_max_children = 256 xdebug.var_display_max_data = 1024
| 功能 | 說明 | 對應模式 |
|---|---|---|
| 美化 var_dump | 顏色、摺疊、限制深度 | develop |
| 堆棧追蹤 | 錯誤自動顯示完整調用鏈 | develop |
| 函數調用追蹤 | 生成 trace 文件分析流程 | trace |
| 代碼覆蓋率 | 配合 PHPUnit 生成報告 | coverage |
| 性能分析 | 生成 cachegrind 文件,KCacheGrind/QCacheGrind 可視化 | profile |
| 遠程斷點調試 | IDE (PhpStorm/VSCode) 設置斷點、單步、變量檢查 | debug |
<?php
// 簡易日誌類
class Logger {
private $path;
public function __construct($file = 'runtime.log') {
$this->path = sys_get_temp_dir() . "/$file";
}
public function info($msg, $context = []) {
$this->write('INFO', $msg, $context);
}
public function error($msg, $context = []) {
$this->write('ERROR', $msg, $context);
}
private function write($level, $msg, $context) {
$line = sprintf(
"[%s] %s: %s %s\n",
date('Y-m-d H:i:s'),
$level,
$msg,
$context ? json_encode($context, JSON_UNESCAPED_UNICODE) : ''
);
file_put_contents($this->path, $line, FILE_APPEND | LOCK_EX);
}
}
// 關鍵點記錄
$log = new Logger('payment.log');
$log->info('支付回調收到', ['order_id' => $orderId, 'amount' => $amount]);
// ...
$log->error('簽名驗證失敗', ['order_id' => $orderId, 'sign' => $sign]);
?>
| 問題 | 定位工具/方法 | 關鍵指標 |
|---|---|---|
| 內存溢出 (OOM) | Xdebug profile + `memory_get_peak_usage()` 埋點 | 峰值內存、循環內未釋放大變量 |
| 執行緩慢 | Xdebug profile (KCacheGrind)、SQL EXPLAIN、strace | 熱點函數、慢查詢、系統調用耗時 |
| 死循環/卡死 | Xdebug 斷點、發送 SIGUSR2 觸發堆棧 (Swoole)、gdb attach | 循環條件、遞歸深度、阻塞 IO |
| 併發競態 | 壓測 (wrk/ab) + 日誌關聯 ID、數據庫行鎖分析 | 事務隔離級、樂觀鎖版本號 |
<?php
// PHPUnit 示例
class CartTest extends TestCase {
public function testAddItemUpdatesQuantity() {
$cart = new Cart();
$cart->add('SKU001', '商品', 100, 1);
$cart->add('SKU001', '商品', 100, 2);
$this->assertEquals(3, $cart->getQty('SKU001'));
$this->assertEquals(300, $cart->total());
}
public function testEmptyCartThrowsOnCheckout() {
$this->expectException(EmptyCartException::class);
(new Cart())->checkout();
}
}
?>
TDD 循環:紅 (寫失敗測試) → 綠 (最小實現通過) → 重構 (優化代碼保持綠燈)