在使用PHP 連接和操作MySQL 數據庫時, mysqli擴展提供了諸如mysqli::query 、 mysqli::prepare等豐富的方法來執行SQL。但在實際開發中,除了關注SQL 是否執行成功,我們有時還需要獲取執行過程中的,這些警告可能不會導致SQL 執行失敗,但卻暗藏潛在問題。
PHP 的mysqli::get_warnings方法能夠獲取連接中最後一次操作產生的所有警告。然而,它的使用通常比較零散,如果我們能將其封裝成一個模塊,就能方便地在項目中復用並保持代碼整潔。
本文將介紹如何封裝mysqli::get_warnings成一個可複用的MySQL 警告捕捉模塊。
mysqli::get_warnings()方法返回一個指向mysqli_warning對象鍊錶的引用,可以通過它訪問所有的警告信息。每個mysqli_warning對象包含如下信息:
message :警告信息
sqlstate :SQLSTATE 錯誤碼
errno :MySQL 錯誤碼
我們的目標是實現一個WarningCollector類,它能夠:
綁定到一個mysqli實例;
執行SQL 語句並自動收集警告信息;
將警告信息格式化後返回或記錄到日誌中;
提供警告清理方法,避免警告信息殘留影響下一次操作。
以下是完整實現代碼:
<?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);
}
}
}
}
使用這個模塊非常簡單。只需要在執行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());
}
這種封裝在以下幾類項目中特別有用:
數據遷移腳本:警告可能揭示字段截斷或類型不匹配;
批量導入工具:插入大批數據時容易觸發約束相關的警告;
數據質量監控:通過日誌系統將警告集中收集供後期分析;
如果你使用如Laravel、Symfony 等框架,也可以基於此思路封裝成中間件或服務類以供依賴注入使用。
mysqli::get_warnings()只能獲取上一次操作產生的警告,因此務必在每次SQL 執行後立即調用;
若你使用的是prepared statements ,建議確保execute後再獲取警告;
不同MySQL 版本對警告的支持程度略有差異,建議開發和生產環境版本一致;
通過將mysqli::get_warnings封裝成模塊,我們不僅可以更方便地捕捉和處理SQL 警告,也有助於提升系統的可維護性和健壯性。你可以根據自己的項目結構進一步擴展,例如添加日誌通道支持、警告級別過濾等。
完整項目示例可參考:
https://gitbox.net/example/mysql-warning-collector