微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 PHP 8 中修复这个动态 SQL 查询函数?

如何解决如何在 PHP 8 中修复这个动态 SQL 查询函数?

在我的旧项目中,我使用一个函数在执行查询时稍微“缩短”我的代码

而不是使用通常的方法

$conn = [...]
$stmt = $conn->prepare(...)
$stmt->bind_param(...)
$stmt->execute();
$stmt->close();
$conn->close();

我有一个函数可以在我之前执行此操作,名为 dynamic_db_reader($MysqLi,$param,$qry)

它返回一个数组(或空值),如:$array[index]['column_name'] = value

或者至少,这是它在以前的版本中所做的。 (适用于 PHP 7.4.16)

这是我的函数代码

/**
 * Dynamically executes a given sql statement as prepared statement (?-placeholder).
 * Expects correct parameters as an array to replace ?.
 * Returns an array with ($arr[index]['column_name'] = value),or null.
 *
 * @param $ms       MysqLi
 * @param $params   array
 * @param $qry      string
 * @return array|null
 */
function dynamic_db_reader($ms,$params,$qry){

    $fields = array();
    $results = array();

    // Replace prefix (DBPREF in: inc/config.PHP)
    if (strpos($qry,'prefix_') !== false){
        $qry = str_replace('prefix',DBPREF,$qry);
    }

    // Set charset
    MysqLi_set_charset($ms,'utf8mb4');

    if ($stmt = $ms->prepare($qry)){

        // Dynamically bind parameters from $params
        if (!isset($params) || !empty($params)){
            // Parameters are set
            $types = '';

            foreach($params as $param){
                // Set parameter data type
                if (is_string($param)){
                    $types .= 's';              // Strings
                } else if (is_int($param)){
                    $types .= 'i';              // Integer
                } else if (is_float($param)){
                    $types .= 'd';              // Double/Float
                } else {
                    $types .= 'b';              // Default: Blob and unkNown types
                }
            }

            $bind_names[] = $types;
            for ($i = 0; $i < count($params); $i++){
                $bind_name = 'bind' . $i;
                $$bind_name = $params[$i];
                $bind_names[] = &$$bind_name;
            }

            call_user_func_array(array($stmt,'bind_param'),$bind_names);
        }

        $stmt->execute();

        $Meta = $stmt->result_Metadata();

        // Dynamically create an array to bind the results to
        while ($field = $Meta->fetch_field()){
            $var = $field->name;
            $$var = null;
            $fields[$var] = &$$var;
        }

        // Bind results
        call_user_func_array(array($stmt,'bind_result'),$fields); // --> Error :(

        // Fetch results
        $i = 0;
        while ($stmt->fetch()){
            $results[$i] = array();
            foreach($fields as $k => $v){
                $results[$i][$k] = $v;
            }
            $i++;
        }

        // Close statement
        $stmt->close();

        if (sizeof($results) > 0){
            return $results;
        }
    }
    return NULL;
}

错误

Fatal error:  Uncaught ArgumentCountError: MysqLi_stmt::bind_result() does not accept unkNown named parameters in [...]\inc\db.PHP:87
Stack trace:
#0 [...]\root\inc\db.PHP(87): MysqLi_stmt->bind_result(data_key: NULL,data_value: NULL)
#1 [...]\root\inc\func\common.PHP(76): dynamic_db_reader(Object(MysqLi),Array,'SELECT * FROM v...')
#2 [...]\root\www\index.PHP(22): getTestArray()
#3 {main}
  thrown in [...]\root\inc\db.PHP on line 87

如何修复此代码,使其也适用于 PHP 8?

解决方法

对于如此简单的事情来说,这是一个非常长的方法。 PHP 8 添加了命名参数。当您解包用作参数的数组时,其键将用作参数名称。 mysqli_stmt::bind_result() 不接受您传递的命名参数。

如果我们简化这段代码,它应该看起来像这样:

/**
 * Dynamically executes a given sql statement as prepared statement (?-placeholder).
 * Expects correct parameters as an array to replace ?.
 * Returns an array with ($arr[index]['column_name'] = value),or null.
 */
function dynamic_db_reader(mysqli $ms,array $params,string $qry): ?array
{
    // Replace prefix (DBPREF in: inc/config.php)
    if (strpos($qry,'prefix_') !== false) {
        $qry = str_replace('prefix',DBPREF,$qry);
    }

    // Set charset
    $ms->set_charset('utf8mb4');

    $stmt = $ms->prepare($qry);

    // Dynamically bind parameters from $params
    if ($params) {
        $stmt->bind_param(str_repeat('s',count($params)),...$params);
    }

    $stmt->execute();

    return $stmt->get_result()->fetch_all(MYSQLI_ASSOC) ?: null;
}

如果由于某种原因你正在使用从 libmysql 客户端编译的 mysqli,那么......好吧,是时候弄清楚如何启用 mysqlnd 或切换到 PDO。

附言请确保您已启用 mysqli 错误报告。 How to get the error message in MySQLi?。此外,每次设置字符集也没有意义。建立连接后立即设置。

,

我看到的情况是 call_user_func_array 被传递为 $fields 作为 ['array_key_txt'=>'array_value'] 导致错误。

尝试将您的 $fields 数组包装在“array_values”中

call_user_func_array(array($stmt,'bind_result'),array_values($fields));


更新:网站 here 解释了 PHP8 中新的“命名参数”

它指出:

str_replace(time_limit: "mi");

会产生错误:

致命错误:未捕获错误:未知命名参数 $time_limit in...

https:php.watch 说:

所有 call_user_func_array() 函数调用必须注意 PHP 8.0 将解释关联数组和数值数组不同。

作为预防措施,call_user_func_array() 调用可以使用 array_values 如果参数数组可能包含非数字键。

$params = [
      'replace' => 'Iron Man','search' => 'Inevitable','subject' => 'I am Inevitable',];  

echo call_user_func_array('str_replace',array_values($params));

使用 array_values 调用,PHP 将始终使用位置调用 模式,确保结果在 PHP 8.0 及更高版本中相同。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。