首页 > 解决方案 > PHP 7.4.18 更新:Mysqlnd -- 在 fetch 之后调用 stmt_store_result 现在会引发错误

问题描述

我们有很多网站使用共享的 MySQLi 类。PHP 7.4 正在使用这些,并且没有看到任何错误产生。

现在,PHP 错误 #80837已在 PHP 7.4.18 版本中得到修复

问题是这个修复意味着我们所有的网站都抛出这个致命错误,我看不到如何修复错误本身。

例外:

[06-May-2021 08:04:19 UTC] PHP 致命错误:未捕获的 mysqli_sql_exception:命令不同步;您现在无法
在 /usr/local/lib/php/databaseClass.php:169 中运行此命令 堆栈跟踪:
#0 /usr/local/lib/php/databaseClass.php(169): mysqli_stmt->store_result()
#1 /usr/local/lib/php/databaseClass.php(897): 数据库->fetchResult(Object(mysqli_stmt), true)
#2 /home/account/public_html/.../file.php(45): DatabaseObject->getSelect('SELECT (bad_log...', Array, true)
#3 /home/katemawdsley/public_html/start.php(10): include('/home/account...')
#4 {main在第 169 行的 databaseClass.php 中抛出

数据库类 'fetchResult' 方法:

/***
 * For use with MySQLi->dbiLink Object.
 * Returns the result as an array of KEY=>VALUE pairs from the Database.
 * @param $result mysqli_result object.
 * @return array|mixed
 ***/
private function fetchResult($result)
{
    if ($result instanceof \mysqli_stmt) {
        $result->store_result(); // This is line 169 

        $variables = [];
        $data = [];
        $meta = $result->result_metadata();

        //error_log(print_r(debug_backtrace(),true));
        while ($field = $meta->fetch_field()) {
            $variables[] = &$data[$field->name]; // pass by reference
        }
        \call_user_func_array([$result, 'bind_result'], $variables);

        $i = 0;
        while ($result->fetch()) {
            $resultsArray[$i] = [];
            foreach ($data as $k => $v) {
                $resultsArray[$i][$k] = $v;
                if ($this->numericIndexes) {
                    $resultsArray[$i][] = $v;
                }
            }
            unset($k, $v);
            $i++;
        }
    } elseif ($result instanceof \mysqli_result) {

        $rowNumber = 0;
        while ($row = $result->fetch_assoc()) {
            $resultsArray[$rowNumber] = $row;
            if ($this->numericIndexes) {
                foreach ($row as $numerical) {
                    $resultsArray[$rowNumber][] = $numerical;
                }
                unset($numerical);
            }
            $rowNumber++;
        }
        $i = 0;
        unset($row, $rowNumber);
    }

    return $resultsArray;
} 

从“select”方法调用的数据库类“fetchResult”方法:

/***
 *  Function for retrieving SQL query data from the Database
 * @param $sql string the SQL select query
 * @param null $data string|array the data to check in the query with
 * @return array|bool|mixed
 ***/
public function select($sql, $data = null){


    /***
     * Now prepare the SQL
     ***/
    try {
        $query = $this->databaseObject->prepare($sql);

        /****
         * $query is prepared here....
         ***/

         ...

        $query->execute();
    }
    catch (\mysqli_sql_exception $ex) {
        $this->exceptionalError($ex);
        return false;
    }
    catch (\Exception $ex) {
        $this->exceptionalError($ex);
        return false;
    }

    if (\mb_strtoupper($reduce, "UTF-8") !== "NORETURN") {
        if ($query->field_count > 0 ) {
            $query->store_result();
        }
        $this->rowsFound[] = $query->num_rows;
        $output            = $this->fetchResult($query);  // Call to fetch result method
        if ($query->field_count > 0) {
            $query->free_result();
        }
        $query->close();
        unset($query);
        return $output;
    }

    /***
     * NORETURN value so no result to return.
     ***/
    $successValue = $this->rowsAffected[] = $query->affected_rows;
    $query->close();
    unset($query);
    return $successValue;
}

fetchResult根据更新如何构建此方法的正确解决方法是什么?

(如您所知,我们完全不知道上一个问题,并且它没有出现在任何错误日志中,所以令人惊讶)

标签: phpmysqli

解决方案


你打store_result()了两次电话。你不能那样做。一旦结果在 PHP 中被缓冲,连接线是空闲的 - 没有更多的结果等待获取。第二次调用store_result()将抛出“不同步”错误。

你先在这里调用它:

    if (\mb_strtoupper($reduce, "UTF-8") !== "NORETURN") {
        if ($query->field_count > 0 ) {
            $query->store_result();
        }

然后在这里

private function fetchResult($result)
{
    if ($result instanceof \mysqli_stmt) {
        $result->store_result(); // This is line 169 

删除一种用法,你应该没问题。

我会建议与之合作mysqli_result。当您调用get_result它时,它将缓冲结果并为您提供可以使用的熟悉对象。这可以显着简化您的抽象类。


这是我将如何重构这个类:

public function select($sql, $data = null)
{

    /***
     * Now prepare the SQL
     ***/
    try {
        $query = $this->databaseObject->prepare($sql);

        /****
         * $query is prepared here....
         ***/

        $query->execute();
    } catch (\mysqli_sql_exception $ex) {
        $this->exceptionalError($ex);
        return false;
    } catch (\Exception $ex) {
        $this->exceptionalError($ex);
        return false;
    }

    $result = $query->get_result();
    if (\mb_strtoupper($reduce, "UTF-8") !== "NORETURN") {
        $this->rowsFound[] = $result->num_rows;
        if ($result) {
            return $this->fetchResult($result);  // Call to fetch result method
        }
    }

    /***
     * NORETURN value so no result to return.
     ***/
    $successValue = $this->rowsAffected[] = $query->affected_rows;
    return $successValue;
}

private function fetchResult(mysqli_result $result)
{
    return $result->fetch_all($this->numericIndexes ? MYSQLI_BOTH : MYSQLI_ASSOC);
}

推荐阅读