MySQLi 动态准备语句奇怪的行为


MySQLi Dynamic Prepared Statements strange behavior

所以我成功地编写了一个脚本,该脚本将准备任何参数并将其绑定到 sql 查询,但我在绑定结果部分遇到了问题。

这是我的脚本:

public function query( $query_string, $params = null, $param_types = null) {
    //prepare the statement
    $stmt = $this->db->prepare($query_string);
    //check if the sql query is wrong.
    if($stmt === false) {
        echo "Wrong SQL: " . $query_string . "<br />Error: " . $this->db->errno . " " . $this->db->error;
    }
    if($params != null && $param_types != null) {
        //build the array that needs to pass the args to bind_param.
        $a_params = array();
        $a_params[] = &$param_types;
        for($i=0; $i<count($params); $i++)
            $a_params[] = &$params[$i];
        // $stmt->bind_param('s', $param); equivalent
        call_user_func_array(array($stmt, 'bind_param'), $a_params);
    }
    //run the query
    $stmt->execute();
    $data = $stmt->result_metadata();
    $fields = array();
    $out = array();
    $count = 0;
    while($field = $data->fetch_field()) {
        $fields[$count++] = &$out[$field->name];
    }    
    call_user_func_array(array($stmt, 'bind_result'), $fields);
    $results = array();
    $k = 0;
    // loop through all result rows
    while ($stmt->fetch()) {
        $results[$k++] = $out;
        print_r($out);
        echo "<br />";
    }
    $stmt->close();
    return $results;
}

当我使用 print_r 输出时,它似乎正确绑定,但是当我添加到数组以返回时(用于以后的脚本,它有奇怪的行为)。

对方法的示例调用:

$users = $TCS->query("SELECT name, age, id FROM test");
foreach($users as $user) {
    echo $user['name'] . ': <br />';
    echo '    age: ' . $user['age'] . '<br />';
    echo '     id: ' . $user['id'] . '<br />';
}

但这是我的输出:

Array ( [name] => jake [age] => 18 [id] => 1 ) 
Array ( [name] => ryan [age] => 19 [id] => 2 ) 
Array ( [name] => stephen [age] => 16 [id] => 3 ) 
stephen: 
age: 16
id: 3
stephen: 
age: 16
id: 3
stephen: 
age: 16
id: 3

TL;DR

$results[$k++] = $out;更改为$results[$k++] = array_flip(array_flip($out));

以下是对正在发生的事情的解释。

var_dump($out);
// first iteration
array(2) {
  ["name"]=>
  &string(4) "mike"
  ["id"]=>
  &int(1)
}
// second interation
array(2) {
  ["name"]=>
  &string(6) "amanda"
  ["id"]=>
  &int(2)
}

这里要注意的重要一点是与号,它们意味着idname的值是参考。当$out['id']$out['name']时,这些引用所指向的内容将更改。因此语句$results[$k++] = $out;表示复制$out并将其分配给$results[$k],这包括复制引用。在下一次迭代中,$results[$k-1]中引用的内容将更改为您背后的新值。

这是一个简单的例子;

$i = 1;
$a = array('id' => &$i);
$b = $a;
$i = 2;
var_dump($a,$b);
// output
array(1) {
    ["id"]=>
    &int(2)
}
array(1) {
    ["id"]=>
    &int(2)
}

您可以通过修改以下行来解决此问题:

$results[$k++] = $out;$results[$k++] = array_flip(array_flip($out));

内部array_flip将取消引用这些值并使其成为键,外部翻转将反转该过程。

但我建议重写下面的所有内容$stmt->execute()如下所示。

//run the query
$stmt->execute();
$result = $stmt->get_result();
if (!$result) { return array(); }
$ret = array();
while ($row = $result->fetch_assoc()) {
    array_push($ret, $row);
}
return $ret;