我可以使用 PHP 检测和处理 MySQL 警告吗?

我可以使用 PHP 检测和处理 MySQL 警告吗?

Can I detect and handle MySQL Warnings with PHP?

我正在处理一个将 JobName 列定义为 UNIQUE 的 MySQL 表。如果有人尝试使用数据库中已有的 JobName 将新 Job 保存到数据库,MySQL 会抛出警告。

我希望能够在我的 PHP 脚本中检测到这个警告,就像一个错误一样,并适当地处理它。理想情况下,我想知道 MySQL 抛出了什么样的警告,以便我可以分支代码来处理它。

这可能吗?如果不是,是因为MySQL没有这个能力,PHP没有这个能力,还是两者都有?


对于 PHP 本身"标记"的警告需要更改 mysql/mysqli 驱动程序,这显然超出了这个问题的范围。相反,您将不得不基本上检查您对数据库进行的每个查询是否有警告:

1
2
3
4
5
6
7
8
9
10
11
12
13
$warningCountResult = mysql_query("SELECT @@warning_count");
if ($warningCountResult) {
    $warningCount = mysql_fetch_row($warningCountResult );
    if ($warningCount[0]  0) {
        //Have warnings
        $warningDetailResult = mysql_query("SHOW WARNINGS");
        if ($warningDetailResult ) {
            while ($warning = mysql_fetch_assoc($warningDetailResult) {
                //Process it
            }
        }
    }//Else no warnings
}

显然,这将非常昂贵地应用 en-mass,因此您可能需要仔细考虑何时以及如何出现警告(这可能会导致您重构以消除它们)。

供参考,MySQL SHOW WARNINGS

当然,您可以省去 SELECT @@warning_count 的初始查询,这样每次执行都会为您节省一个查询,但我将其包括在内是为了书呆子的完整性。


首先,您应该关闭警告,这样您的访问者就不会看到您的 MySQL 错误。其次,当你调用 mysql_query() 时,你应该检查它是否返回 false。如果是这样,请调用 mysql_errno() 以找出问题所在。将返回的数字与此页面上的错误代码匹配。

看起来这是您要查找的错误号:

Error: 1169 SQLSTATE: 23000 (ER_DUP_UNIQUE)

Message: Can't write, because of unique constraint, to table '%s'


1
ini_set('mysql.trace_mode', 1)

可能就是你要找的。

然后可以使用自定义 PHP 错误处理程序来处理 PHP 错误,但您也可以关闭显示 php 错误,因为它们通常会记录到日志文件中(取决于您的 php 配置)。


关于抑制警告的注意事项:通常,阻止显示警告不是一个好主意,因为您可能会遗漏一些重要的东西。如果您出于某种原因绝对必须隐藏警告,您可以通过在语句前面放置 @ 符号来单独执行此操作。这样您就不必关闭所有警告报告,并且可以将其限制为特定实例。

示例:

1
2
 // this suppresses warnings that might result if there is no field titled"field" in the result
 $field_value = @mysql_result($result, 0,"field");

可以得到警告,而且使用 mysqli 比使用 mysql 更有效。

这里是 php.net 手册页上为属性 mysqli-warning_count 建议的代码:

1
2
3
4
5
6
7
8
9
10
$mysqli-query($query);

if ($mysqli-warning_count) {
    if ($result = $mysqli-query("SHOW WARNINGS")) {
        $row = $result-fetch_row();
        printf("%s (%d): %s\
"
, $row[0], $row[1], $row[2]);
        $result-close();
    }
}

您可以使用 mysqli 语句错误号检测唯一键违规。 mysqli 语句返回错误 1062,即 ER_DUP_ENTRY。您可以查找错误 1062 并打印合适的错误消息。如果您还想打印列 (jobName) 作为错误消息的一部分,那么您应该解析语句错误字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  if($stmt = $mysqli-prepare($sql)){
            $stmt-bind_param("sss",
            $name,
            $identKey,
            $domain);


            $stmt-execute();
            if($mysqli-affected_rows != 1)  {
                        //This will return errorno 1062
                trigger_error('mysql error  '.$stmt-errno .'::' .$stmt-error, E_USER_ERROR);
                exit(1);
            }
            $stmt-close();
        } else {

            trigger_error('mysql error  '. $mysqli-errno.'::'.$mysqli-error,E_USER_ERROR);
        }

根据您正在使用的(如果有的话)框架,我建议您自己进行查询以检查作业名称,并为用户创建适当的信息以及表单的其余验证。

根据作业名称的数量,您可以将名称发送到包含表单的视图,并使用 javascript 告诉使用哪个被占用。

如果这对您没有意义,那么总结一下我的观点是:不要设计您的程序和/或用户来尝试做非法的事情并在他们这样做时捕获错误并随后处理它。恕我直言,将系统设计为不产生错误要好得多。将错误保留为实际错误 :)


已更新以删除有关 errno 函数的内容,我现在意识到这些内容不适用于您的情况...

MySQL 中对于 UPDATE 语句要警惕的一件事:即使 WHERE 子句匹配行,mysqli_affected_rows() 也会返回零,但 SET 子句实际上并没有改变数据值。我只提到这一点是因为这种行为导致了我曾经看过的系统中的错误——程序员使用该返回值在更新后检查错误,假设零意味着发生了一些错误。这只是意味着用户在单击更新按钮之前没有更改任何现有值。

所以我想也不能依赖使用 mysqli_affected_rows() 来找到此类警告,除非您的表中有类似 update_time 列的东西,在更新时总是会被分配一个新的时间戳值。不过,这种解决方法似乎有点笨拙。


推荐阅读