MySQLi准备语句错误报告

85

我试图理解MySQli,但在错误报告方面感到困惑。我正在使用MySQLi的“prepare”语句的返回值,在执行SQL时检测错误,例如:

$stmt_test =  $mysqliDatabaseConnection->stmt_init();
if($stmt_test->prepare("INSERT INTO testtable VALUES (23,44,56)"))
{
 $stmt_test->execute();
 $stmt_test->close();
}
else echo("Statement failed: ". $stmt_test->error . "<br>");

但准备语句的返回值只检测SQL语句准备过程中是否有错误,而不检测执行过程中的错误吗?如果是这样,那么我应该将执行行更改为标记错误,像这样:

if($stmt_test->execute()) $errorflag=true;

执行该语句后,为了保险起见,我是否还需要执行以下操作:

if($stmt_test->errno) {$errorflag=true;}

我一开始的做法是正确的吗?MySQLi prepare语句的返回值是否能捕获与查询执行完整过程相关的所有错误?

谢谢 C


为什么在查询字符串中没有变量部分时,你会首选使用prepare/execute()而不是query()呢?或者这只是一个过于简化的例子? - VolkerK
是的,抱歉。这只是简化了一下,以显示我在哪里难以理解如何从准备好的语句中获得确定性的错误报告。 - Columbo
3个回答

165

mysqli的每个方法都可能失败。幸运的是,现在mysqli可以向您报告出现的每个问题,您所需要做的就是要求它这样做。只需将此一行代码添加到连接代码中即可:

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

之后,每个错误都会显露出来。不需要测试任何返回值,只需立即编写语句:

$stmt = $mysqli->prepare("INSERT INTO testtable VALUES (?,?,?)");
$stmt->bind_param('iii', $x, $y, $z);
$stmt->execute();
当错误发生在任何步骤时,它将抛出一个通常的PHP异常,可以像处理其他PHP错误一样处理或报告。只需确保正确配置了PHP错误报告,即在开发服务器上将错误显示在屏幕上,在生产服务器上不显示错误,而是记录错误。

2
谢谢。我刚刚进行了一些测试,可以看到你所说的情况。如果我创建一个查询,将重复的主键值插入表中,那么检查准备就不会显示插入失败的信息。另一方面,如果我不检查准备,则执行永远不会发生(如果警告被打开,我会收到一些警告)。所以我可以看出你是正确的。谢谢。 - Columbo
哦,是的,约束条件是一个完美的例子。如果可能的话,我会仅因此给“这个问题”再+1;-) - VolkerK
2
如果bind_param()由于类型定义字符串中的元素数量与绑定变量数量不匹配而失败,则会触发E_WARNING错误。您将无法在$stmt->error属性中找到该错误。 - Accountant م
@会计师,幸运的是,这种麻烦已经成为历史。在现代PHP版本中,bind_param也会抛出异常。 - Your Common Sense

22

完整性

您需要检查$mysqli$statement。如果它们是false,您需要分别输出$mysqli->error$statement->error

效率

对于可能终止的简单脚本,我使用简单的一行代码触发带有消息的PHP错误。对于更复杂的应用程序,应该激活错误警告系统,例如通过抛出异常来实现。

用法示例1:简单脚本

# This is in a simple command line script
$mysqli = new mysqli('localhost', 'buzUser', 'buzPassword');
$q = "UPDATE foo SET bar=1";
($statement = $mysqli->prepare($q)) or trigger_error($mysqli->error, E_USER_ERROR);
$statement->execute() or trigger_error($statement->error, E_USER_ERROR);

使用示例2:应用程序

# This is part of an application
class FuzDatabaseException extends Exception {
}

class Foo {
  public $mysqli;
  public function __construct(mysqli $mysqli) {
    $this->mysqli = $mysqli;
  }
  public function updateBar() {
    $q = "UPDATE foo SET bar=1";
    $statement = $this->mysqli->prepare($q);
    if (!$statement) {
      throw new FuzDatabaseException($mysqli->error);
    }

    if (!$statement->execute()) {
      throw new FuzDatabaseException($statement->error);
    }
  }
}

$foo = new Foo(new mysqli('localhost','buzUser','buzPassword'));
try {
  $foo->updateBar();
} catch (FuzDatabaseException $e)
  $msg = $e->getMessage();
  // Now send warning emails, write log
}

  1. 永远不应该使用die()。
  2. mysqli能够自行抛出异常,请参见mysqli_report()。3.在每个数据库驱动函数之后编写“发送警告电子邮件,写日志”的代码非常冗余。
- Your Common Sense
再说一遍,这是胡说八道。当你需要性能、低内存占用和少量依赖时,你就只做那些事情。 - cmc
3
同时提到$mysqli->error和$statement->error让我受益匪浅,谢谢^^ - Fernando Silva
2
@FernandoSilva 很高兴听到这个好消息,你刚刚让我也很开心! - cmc
1
@YourCommonSense 对于应用程序来说,减少样板代码可能是有益的,但如何最好地做到这一点取决于应用程序。该示例肯定是一个不错的起点。 - cmc
显示剩余3条评论

6

不确定这是否回答了您的问题。如果没有,很抱歉。

要从mysql数据库中报告有关查询的错误,请使用连接对象作为焦点。

因此:

echo $mysqliDatabaseConnection->error

会回显mysql关于您查询的错误信息。

希望这有所帮助。


谢谢。所以,如果我请求实际连接对象的错误,那么它将为该连接给出最后一个错误。由于只有在所有先前的步骤都成功完成时执行才会成功,因此这将告诉我是否一切顺利。我想,通过像Col Shrapnel所推荐的那样仅检查执行命令的错误也可以生成相同的结果。因此,我认为检查准备语句的成功/失败标志没有任何实际意义,我的想法正确吗? - Columbo
我想你可能已经在上面得到了答案,但出于礼貌仍会回复。 本质上,像VolekrK所说,Prepare可能会失败,但不会返回mysql错误。因此,您需要通过获取mysql连接错误来找出为什么准备失败,这将为您提供有关准备语句中的查询失败位置的指示。 我不确定execute命令错误。 - andyface

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接