【问题标题】:Obtaining error message when mysqli->bind_param() fails for graceful error handling当 mysqli->bind_param() 失败时获取错误消息以进行优雅的错误处理
【发布时间】:2021-05-26 03:31:01
【问题描述】:

我有以下 PHP 代码:

$inserted = false ;
printf("Record must be inserted.  Old DB ID is %d.\n", $old_db_id) ;
$insertSQL = "INSERT INTO GoogleAuth ("
     .                               "script"
     .                             ", client_id"
     .                             ", client_secret"
     .                             ", refresh_token"
     .                             ", auth_token"
     .                             ", refresh_token_date"
     .                             ", auth_token_date"
     .                             ", client_info_date"
     .                             ", last_used_date"
     .                             ")"
     .            " VALUES(?, ?, ?, ?, ?, ?, ?, ?, NULL) "
                   ;
printf("Preparing statement....\n") ;
$stmt = $mySQL->prepare($insertSQL) ;
if ($stmt) {
    printf("Binding parameters...\n") ;
    if ($stmt->bind_param("sssssiiii"
                        , $page_file
                        , $clientId
                        , $clientSecret
                        , $refreshToken
                        , $authToken
                        , $authTokenDate
                        , $refreshTokenDate
                        , $clientInfoDate
                        , $now
                         )) {
        print("Bind returned true\n") ;
        if ($stmt->execute()) {
            $inserted = true ;
            }
        }
    else {
        printf("Bind failed.  %s\n", $stmt->error) ;
        }
    }
if ($inserted) {
    printf("row inserted.\n") ;
    }
else {
    printf("Insert failed! %s\n", $mySQL->error) ;
    trigger_error("Insert failed.  Dang it!", E_USER_ERROR) ;
    }

当我执行这段代码时,会打印以下内容:

Record must be inserted.  Old DB ID is 0.
Preparing statement....
Binding parameters...
Bind failed.
Insert failed!

注意 $stmt->error 值和 $mySQL->error 值都是空的!

您能否帮助我了解如何可靠地获取与此类故障相关的错误消息(和/或错误代码)? (我最终发现我用 9 个变量代替了 8 个替换标记……这不是重点。重点是要弄清楚如何从 mysql 中获取实际错误。)

谢谢!

编辑

顺便说一句,我看过像this 这样的文章,其中谈到有趣的变量名重用。这不适用于这里。 $stmt 未在此应用程序的其他任何地方使用。

EDIT2

有人建议这是this problem 的副本。但是,不同之处在于,这里的目标是获取失败消息,以便对其进行分析以进行正确处理。 (根据具体情况,一些错误可能是可以管理的。)无论如何,这样的过程可以让程序优雅地关闭,而不是抛出失败错误所带来的粗鲁和直接的结果。当我阅读the other problem 时,目标是确保错误进入日志,并且程序被中止。

【问题讨论】:

  • 这能回答你的问题吗? Turning query errors to Exceptions in MySQLi
  • 这不是 MySQL 错误。这是一个 PHP 错误。您是否启用了 PHP 错误报告?
  • 语义,@Dharman - 取决于你的观点。这是要求 MySQL 做某事的结果。正如我在其他地方所避免的那样,优雅地处理错误对于一个好的程序来说是必不可少的。例如,如果某些 INSERT 由于重复键而失败,则可以调整并继续。另一种方法是转储整个过程,让受害者自己弄清楚如何重新启动。

标签: php mysql mysqli


【解决方案1】:

失败(太多类型)会产生警告,这就是为什么stmt->error什么都不返回,你可以通过调用error_get_last函数来检索它。

if(!$stmt->bind_param(....))
{
    $err = error_get_last();
    if($err)
        printf("Bind failed.  %s\n", $err['message']) ;
}

【讨论】:

  • 非常感谢@shingo ...这困扰了我多年!我希望 MySQL 的开发人员在查找错误的位置上更加一致,但是我又一次做出了一两个错误的选择。好吧,可能无论如何。 :)
  • @Dennis 不要向 mysql 开发人员抱怨,因为这是 php 开发人员的工作。 PHP 在真正绑定之前检查参数计数,如果计数与准备语句不匹配,则会引发 php 级别的错误。如果 mysql 在绑定参数时出现任何错误,错误会转到 stmt->error。所以你也可以在 bind_param 返回 false 时检查它。
  • 我正在努力理解您的评论@shingo。从纯 PHP 的角度来看,参数的数量是可变的,对吧?如果没有来自 mysqli 的输入,PHP 就无法对此说好或坏?从 PHP 的角度来看,$stmt->bind_param("???", $val1) 是完全有效的。只有mysqli可以确定参数不正确。但它并没有把这个事实放入 ->error 中。相反,它依赖 PHP 来处理它。 (或者更确切地说,它让开发人员去寻找它。)这就是我所说的“糟糕”选择的一个例子。
  • PHP知道,在调用mysql函数之前,它会先检查mysqli_stmt::$param_count,第一个参数$types中的字符和传递给bind_param的其余参数count是否相等。
  • 我猜 PHP 认为这是一个参数错误而不是 mysql 错误。因为在调用mysql的bind_param函数之前,PHP需要根据prepare语句中定义的参数计数来分配结构。如果计数不相等,PHP 不知道要填充它们。
【解决方案2】:

在@shingo 的带领下 - 接受他的回答后,我做了以下功能,希望可以帮助该领域的其他人。

function GetMySqlError($mysql = null, $stmt = null) {
    // MySQL error reporting is inconsistent.  Sometimes failure error
    // message will be in $mysql->error, sometimes in $stmt->error,
    // and sometimes it's necessary to inspect error_get_last().  Sheesh!

    // This routine will inspect all three, and will return the
    // FIRST nonblank value found in:
    //     stmt->error
    //     mysql->error
    //     error_get_last()['message']

    $messageMySql = "" ;
    $messageStmt  = "" ;
    $err          = null;
    $messageLast  = "" ;
    $returnValue  = null ;

    if (is_object($stmt) && property_exists($stmt, "error")) {
        $messageStmt = $stmt->error ;
        }
    if (is_object($mysql) && property_exists($mysql, "error")) {
        $messageMySql = $mysql->error ;
        }
    if ($err = error_get_last()) {
        $messageLast = $err['message'] ;
        }
    if (strlen($messageStmt) > 0) $returnValue = $messageMySql ;
    elseif (strlen($messageMySql) > 0) $returnValue = $messageStmt ;
    elseif (strlen($messageLast) > 0) $returnValue = $messageLast ;
    return $returnValue ;
    }

这可以这样称呼:

GetMySqlError($mysql_object) ;
GetMySqlError($myaql_object, $statement_object) ;
GetMySqlError($statement_object) ;

Example:
    printf("MySQL/statement error: %s\n", GetMySqlError($mySql, $stmt)) ;

再次感谢@shingo

注意:我认为没有必要在此处包含连接错误。可以这样做,但我已经有自己的例程了。

注意:这可以提高效率。我只为这个答案选择了可读性。

【讨论】:

  • 你为什么需要这样的东西?为什么不只启用 PHP 错误?
  • 如果对语句执行方法失败,则会引发致命错误。如果连接对象发生错误,则会引发致命错误。如果您将不正确的参数传递给任何方法,则会引发致命错误。这个功能什么时候能派上用场?
  • @Dharman,你的假设都是不正确的。除非 mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) 否则会引发警告;发出。至于您的第一条评论,如果您看不到价值,那可能不适合您。对我自己来说,它有助于调试过程。此外,优雅地处理错误对我来说非常重要,而且似乎主要是一门失传的艺术。如果 INSERT 由于某些重复键而失败,您认为整个过程都失败了吗?或者也许处理它并继续?
  • @Dharman “赛马是因为意见不同。”马克吐温知道他在说什么。这很好,但只处理重复键。其他一些问题仍然会终止进程。如前所述,我试图尽可能多地避免这种情况。
猜你喜欢
  • 1970-01-01
  • 2019-09-19
  • 1970-01-01
  • 2013-11-01
  • 2022-01-04
  • 1970-01-01
  • 2023-03-25
  • 2018-01-01
  • 1970-01-01
相关资源
最近更新 更多