在使用 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