【问题标题】:MySQLi prepared statements error reporting [duplicate]MySQLi准备好的语句错误报告[重复]
【发布时间】:2011-02-02 21:33:07
【问题描述】:

我正试图了解 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>");

但是,prepare语句的返回值是不是只检测SQL语句的准备是否出错,而不检测执行错误呢?如果是这样,我应该因此更改我的执行行以标记错误,如下所示:

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

为了安全起见,我还应该在语句执行后执行以下操作:

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

...或者我是否可以开始并且 MySQLi prepare' 语句的返回值捕获与它定义的查询的完整执行相关的所有错误?

谢谢 C

【问题讨论】:

  • 当您的查询字符串中没有可变部分时,为什么首先使用准备/执行()而不是查询()?或者这只是一个过于简单的例子?
  • 是的,对不起。它被简化以显示我如何难以理解从准备好的状态获取明确的错误报告。

标签: php mysql mysqli prepared-statement


【解决方案1】:

mysqli 的每个方法都可能失败。您应该测试每个返回值。如果失败,请考虑继续使用不在您期望的状态的对象是否有意义。 (可能不在“安全”状态,但我认为这不是问题。)

由于每个连接/语句只存储最后一次操作的错误消息,如果在出现问题后继续,您可能会丢失有关是什么导致错误的信息。您可能希望使用该信息让脚本决定是否重试(仅是临时问题)、更改某些内容或完全退出(并报告错误)。而且它使调试变得容易得多。

$stmt = $mysqli->prepare("INSERT INTO testtable VALUES (?,?,?)");
// prepare() can fail because of syntax errors, missing privileges, ....
if ( false===$stmt ) {
  // and since all the following operations need a valid/ready statement object
  // it doesn't make sense to go on
  // you might want to use a more sophisticated mechanism than die()
  // but's it's only an example
  die('prepare() failed: ' . htmlspecialchars($mysqli->error));
}

$rc = $stmt->bind_param('iii', $x, $y, $z);
// bind_param() can fail because the number of parameter doesn't match the placeholders in the statement
// or there's a type conflict(?), or ....
if ( false===$rc ) {
  // again execute() is useless if you can't bind the parameters. Bail out somehow.
  die('bind_param() failed: ' . htmlspecialchars($stmt->error));
}

$rc = $stmt->execute();
// execute() can fail for various reasons. And may it be as stupid as someone tripping over the network cable
// 2006 "server gone away" is always an option
if ( false===$rc ) {
  die('execute() failed: ' . htmlspecialchars($stmt->error));
}

$stmt->close();

六年后的一些笔记......

mysqli 扩展完全能够通过异常报告导致 (mysqli) 错误代码不是 0 的操作,请参阅mysqli_driver::$report_mode
die() 真的非常粗鲁,我不会使用它即使是这样的例子。
所以,请只考虑每个(mysql)操作可能由于多种原因而失败的事实;即使如果同样的事情在之前一千次都顺利......

【讨论】:

  • 谢谢。我刚刚做了一些测试,可以看到你在说什么。如果我创建一个将重复的主键值插入到表中的查询,那么仅检查准备不会显示插入失败。另一方面,如果我不检查准备,那么执行将永远不会发生(如果打开警告,我会收到一些警告)。所以我可以看出你是对的。谢谢。
  • 哦,是的,约束就是一个很好的例子。如果可能的话,我会给问题一个单独的+1 ;-)
  • 如果 bind_param() 由于类型定义字符串中的元素数量与绑定变量的数量不匹配而失败。它会触发 E_WARNING 错误。而且您不会在 $stmt->error property 中找到该错误
【解决方案2】:

完整性

您需要同时检查$mysqli$statement。如果为假,则需要分别输出$mysqli-&gt;error$statement-&gt;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() 2. 在每个数据库驱动函数之后写“发送警告邮件,写日志”的代码是非常多余的
  • 无论如何,你永远不应该使用mysqli API,而只能包装在一些更高级别的库中。
  • 同时提到 $mysqli->error 和 $statement->error 只是节省了我的时间,thks^^
  • @FernandoSilva 很高兴听到费尔南多的消息,而你刚刚做了我的!
  • @YourCommonSense 对于应用程序,是的,减少样板文件可能会很好,但如何最好地做到这一点取决于应用程序。这个例子当然是一个不错的起点。
【解决方案3】:

不确定这是否回答了您的问题。不然很抱歉

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

所以:

echo $mysqliDatabaseConnection->error

会回显从 mysql 发送的关于您的查询的错误。

希望有帮助

【讨论】:

  • 谢谢。因此,如果我请求实际连接对象的错误,那么它将给我该连接的最后一个错误。因为只有在前面的所有步骤都成功的情况下执行才会成功,所以这将告诉我是否一切顺利。我想也可以通过检查执行命令的错误来生成相同的结果,如下面的 Col Shrapnel 认可的那样。因此,我是否认为检查 prepare 语句的成功/失败标志没有真正的目的?
  • 我想你可能已经有了上面的答案,但出于礼貌还是会回复。正如 VolekrK 所说,本质上 Prepare 可能会失败,但它不会返回 mysql 错误。因此,您需要通过获取 mysql 连接错误来找出准备失败的原因,这将为您指示准备语句中的查询失败的位置。我不确定执行命令错误。
猜你喜欢
  • 2014-06-11
  • 2013-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-01
  • 1970-01-01
相关资源
最近更新 更多