當前位置: 首頁> 最新文章列表> 如何封裝mysqli::get_warnings 成通用警告捕捉模塊

如何封裝mysqli::get_warnings 成通用警告捕捉模塊

gitbox 2025-05-26

在使用PHP 連接和操作MySQL 數據庫時, mysqli擴展提供了諸如mysqli::querymysqli::prepare等豐富的方法來執行SQL。但在實際開發中,除了關注SQL 是否執行成功,我們有時還需要獲取執行過程中的,這些警告可能不會導致SQL 執行失敗,但卻暗藏潛在問題。

PHP 的mysqli::get_warnings方法能夠獲取連接中最後一次操作產生的所有警告。然而,它的使用通常比較零散,如果我們能將其封裝成一個模塊,就能方便地在項目中復用並保持代碼整潔。

本文將介紹如何封裝mysqli::get_warnings成一個可複用的MySQL 警告捕捉模塊。

1. 背景知識

mysqli::get_warnings()方法返回一個指向mysqli_warning對象鍊錶的引用,可以通過它訪問所有的警告信息。每個mysqli_warning對象包含如下信息:

  • message :警告信息

  • sqlstate :SQLSTATE 錯誤碼

  • errno :MySQL 錯誤碼

2. 模塊設計思路

我們的目標是實現一個WarningCollector類,它能夠:

  • 綁定到一個mysqli實例;

  • 執行SQL 語句並自動收集警告信息;

  • 將警告信息格式化後返回或記錄到日誌中;

  • 提供警告清理方法,避免警告信息殘留影響下一次操作。

3. 模塊代碼實現

以下是完整實現代碼:

 <?php

class WarningCollector
{
    private mysqli $conn;
    private array $warnings = [];

    public function __construct(mysqli $connection)
    {
        $this->conn = $connection;
    }

    public function execute(string $sql): bool|mysqli_result
    {
        $result = $this->conn->query($sql);
        $this->collectWarnings();
        return $result;
    }

    private function collectWarnings(): void
    {
        $this->warnings = []; // 清空舊數據
        $warning = $this->conn->get_warnings();
        while ($warning) {
            $this->warnings[] = [
                'errno'    => $warning->errno,
                'sqlstate' => $warning->sqlstate,
                'message'  => $warning->message,
            ];
            $warning = $warning->next();
        }
    }

    public function getWarnings(): array
    {
        return $this->warnings;
    }

    public function hasWarnings(): bool
    {
        return !empty($this->warnings);
    }

    public function logWarnings(string $logPath = '/tmp/mysql_warnings.log'): void
    {
        if ($this->hasWarnings()) {
            foreach ($this->warnings as $w) {
                $entry = sprintf(
                    "[%s] MySQL Warning - Errno: %d, SQLSTATE: %s, Message: %s\n",
                    date('Y-m-d H:i:s'),
                    $w['errno'],
                    $w['sqlstate'],
                    $w['message']
                );
                file_put_contents($logPath, $entry, FILE_APPEND);
            }
        }
    }
}

4. 使用示例

使用這個模塊非常簡單。只需要在執行SQL 前用它封裝連接即可:

 <?php

$mysqli = new mysqli('localhost', 'user', 'pass', 'database');
$collector = new WarningCollector($mysqli);

// 執行 SQL
$sql = "INSERT INTO demo (name) VALUES ('duplicate-key')";
$collector->execute($sql);

// 檢查警告
if ($collector->hasWarnings()) {
    $collector->logWarnings();
    print_r($collector->getWarnings());
}

5. 實際應用場景

這種封裝在以下幾類項目中特別有用:

  • 數據遷移腳本:警告可能揭示字段截斷或類型不匹配;

  • 批量導入工具:插入大批數據時容易觸發約束相關的警告;

  • 數據質量監控:通過日誌系統將警告集中收集供後期分析;

如果你使用如Laravel、Symfony 等框架,也可以基於此思路封裝成中間件或服務類以供依賴注入使用。

6. 注意事項

  • mysqli::get_warnings()只能獲取上一次操作產生的警告,因此務必在每次SQL 執行後立即調用;

  • 若你使用的是prepared statements ,建議確保execute後再獲取警告;

  • 不同MySQL 版本對警告的支持程度略有差異,建議開發和生產環境版本一致;

7. 結語

通過將mysqli::get_warnings封裝成模塊,我們不僅可以更方便地捕捉和處理SQL 警告,也有助於提升系統的可維護性和健壯性。你可以根據自己的項目結構進一步擴展,例如添加日誌通道支持、警告級別過濾等。

完整項目示例可參考:
https://gitbox.net/example/mysql-warning-collector