PHP 7.4.18 更新:Mysqlnd -- 现在在 fetch 之后调用 stmt_store_result 会抛出错误
PHP 7.4.18 update: Mysqlnd -- Calling stmt_store_result after fetch now throws an error
我们有很多网站使用共享的 MySQLi class。
PHP 7.4 正在使用这些并且没有看到生成错误。
现在,PHP Bug #80837 has been fixed with release of PHP 7.4.18
问题是这个修复意味着我们所有的网站都抛出这个致命错误,我看不出如何修复这个错误。
异常:
[06-May-2021 08:04:19 UTC] PHP Fatal error: Uncaught mysqli_sql_exception: Commands out of sync; you can't run this command
now in /usr/local/lib/php/databaseClass.php:169 Stack trace:
#0 /usr/local/lib/php/databaseClass.php(169): mysqli_stmt->store_result()
#1 /usr/local/lib/php/databaseClass.php(897): Database->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} thrown in databaseClass.php on line 169
数据库Class'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' 方法调用的数据库 Class '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
方法的正确解决方法是什么?
(如您所知,我们完全不知道之前的这个问题,也没有出现在任何错误日志中,所以很意外)
您正在呼叫 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
时,它将缓冲结果并为您提供一个您可以使用的熟悉的对象。这可以显着简化您的抽象 class。
下面是我将如何重构这个 class:
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);
}
我们有很多网站使用共享的 MySQLi class。 PHP 7.4 正在使用这些并且没有看到生成错误。
现在,PHP Bug #80837 has been fixed with release of PHP 7.4.18
问题是这个修复意味着我们所有的网站都抛出这个致命错误,我看不出如何修复这个错误。
异常:
[06-May-2021 08:04:19 UTC] PHP Fatal error: Uncaught mysqli_sql_exception: Commands out of sync; you can't run this command
now in /usr/local/lib/php/databaseClass.php:169 Stack trace:
#0 /usr/local/lib/php/databaseClass.php(169): mysqli_stmt->store_result()
#1 /usr/local/lib/php/databaseClass.php(897): Database->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} thrown in databaseClass.php on line 169
数据库Class'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' 方法调用的数据库 Class '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
方法的正确解决方法是什么?
(如您所知,我们完全不知道之前的这个问题,也没有出现在任何错误日志中,所以很意外)
您正在呼叫 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
时,它将缓冲结果并为您提供一个您可以使用的熟悉的对象。这可以显着简化您的抽象 class。
下面是我将如何重构这个 class:
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);
}