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

标签 php arrays mysqli prepared-statement php-8

在我的旧项目中,我使用一个函数在进行查询时稍微“缩短”我的代码。
而不是使用通常的方法

$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);
    }

    $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;
}


mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'user', 'password', 'test');
$mysqli->set_charset('utf8mb4');

$results = dynamic_db_reader($mysqli, ['foo'], 'SELECT ?');
如果出于某种原因,您正在使用从 libmysql 客户端编译的 mysqli,那么……好吧,是时候弄清楚如何启用 mysqlnd 或切换到 PDO。
附言请确保您已启用 mysqli 错误报告。 How to get the error message in MySQLi? .此外,每次设置字符集也没有意义。建立连接后立即设置。

关于php - 如何在 PHP 8 中修复这个动态 SQL 查询函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66986315/

相关文章:

php - 正则表达式 : how to match everythings that is not into a pattern?

C 指向函数内部结构数组的指针/引用

java - 使用数组随机组合字符串

php - 一个 php 文件中的多个查询,我尝试编写代码,但我没有完成

php - 我必须在 PHP 中使用结果之前或之后关闭准备好的语句吗

javascript - 从 Javascript 调用 perl 文件/函数和函数来更新 HTML

PHP 警告:DOMDocument::load():I/O 警告:加载外部实体失败

php - mysqli_connect()上的PHP fatal error ;

PHP 处理表单时出现空白页

c++ - 应用于 C++ 中对象列表的类似 Strcmp 的行为