当前位置: 首页> 最新文章列表> 如何封装 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