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

时间:2014-11-15 22:38:26

标签: php mysqli

所以我已经成功编写了一个脚本,它将准备并将任何参数绑定到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

1 个答案:

答案 0 :(得分:1)

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;