簡體   English   中英

如何在 PHP 8 中修復此動態 SQL 查詢 function?

[英]How to fix this dynamic SQL query function in PHP 8?

在我的舊項目中,我在進行查詢時使用了 function 來“縮短”我的代碼。

而不是使用通常的方法

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

我得到了一個 function 來代替我做這件事,叫做dynamic_db_reader($mysqli, $param, $qry)

它返回一個數組(或 null),如: $array[index]['column_name'] = value

或者至少,這就是它在以前的版本中所做的。 (在 PHP 7.4.16 工作)

這是我的 function 的代碼:

/**
 * 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 添加了名為 arguments。 當您解壓一個數組以用作 arguments 時,它的鍵用作參數名稱。 mysqli_stmt::bind_result()不接受命名 arguments 就像你傳遞它一樣。

如果我們簡化這段代碼,那么它應該看起來像這樣:

/**
 * 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。

PS 請確保您已啟用 mysqli 錯誤報告。 如何獲取 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));


更新:這里的網站解釋了 PHP8 中的新“命名參數”

它指出:

str_replace(time_limit: "mi");

將產生錯誤:

致命錯誤:未捕獲的錯誤:未知的命名參數 $time_limit in...

https:php.watch 說:

all call_user_func_array() function calls must be aware that PHP 8.0 will interpret associative arrays and numeric arrays different.

作為預防措施,如果參數數組可能包含非數字鍵,則 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 及更高版本中的結果相同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM