【问题标题】:Dynamically bind mysqli_stmt parameters and then bind result (PHP)动态绑定mysqli_stmt参数,然后绑定结果(PHP)
【发布时间】:2011-03-14 15:00:32
【问题描述】:

我正在尝试动态绑定 mysql_stmt 参数并在关联数组中获取结果。我在 stackoverflow 上找到了这篇文章,其中 Amber 使用以下代码发布了答案:

原帖: How to make a proper mysqli extension class with prepared statements?

“假设您实际上想要编写自己的版本(而不是利用其他答案建议的现有库之一 - 这些也是不错的选择)......

这里有几个函数,您可能会发现它们对检查很有用。第一个允许您将查询结果绑定到关联数组,第二个允许您传入两个数组,一个是有序的键数组,另一个是这些键的关联数据数组,并将该数据绑定到准备好的声明:"

function stmt_bind_assoc (&$stmt, &$out) {
    $data = mysqli_stmt_result_metadata($stmt);
    $fields = array();
    $out = array();

$fields[0] = $stmt;
$count = 1;

while($field = mysqli_fetch_field($data)) {
    $fields[$count] = &$out[$field->name];
    $count++;
}
call_user_func_array(mysqli_stmt_bind_result, $fields);

}

function stmt_bind_params($stmt, $fields, $data) {
    // Dynamically build up the arguments for bind_param
    $paramstr = '';
    $params = array();
    foreach($fields as $key)
    {
        if(is_float($data[$key]))
            $paramstr .= 'd';
        elseif(is_int($data[$key]))
            $paramstr .= 'i';
        else
            $paramstr .= 's';
        $params[] = $data[$key];
    }
    array_unshift($params, $stmt, $paramstr);
    // and then call bind_param with the proper arguments
    call_user_func_array('mysqli_stmt_bind_param', $params);
}

我尝试研究代码以了解它的作用,并且我已使第二个功能正常工作,但我不知道应该做什么才能使用第一个功能。如何使用它来检索类似于 mysqli_result::fetch_assoc() 的数组?

我希望能够像以前那样使用结果:

while ($row = mysql_fetch_array($result)){
  echo $row['foo']." ".$row['bar'];
}

请帮助我在这方面取得一些进展:)

【问题讨论】:

  • 说真的,没有一个答案?
  • 我正在尝试做同样的事情。如果我弄清楚了,会发布答案。

标签: php dynamic prepared-statement sqlbindparameter


【解决方案1】:

好的,这是一种方法:

已编辑,以修复获取多行时的错误

$sql = "SELECT `first_name`,`last_name` FROM `users` WHERE `country` =? AND `state`=?";
$params = array('Australia','Victoria');

/*
    In my real app the below code is wrapped up in a class 
    But this is just for example's sake.
    You could easily throw it in a function or class
*/

// This will loop through params, and generate types. e.g. 'ss'
$types = '';                        
foreach($params as $param) {        
    if(is_int($param)) {
        $types .= 'i';              //integer
    } elseif (is_float($param)) {
        $types .= 'd';              //double
    } elseif (is_string($param)) {
        $types .= 's';              //string
    } else {
        $types .= 'b';              //blob and unknown
    }
}
array_unshift($params, $types);

// Start stmt
$query = $this->connection->stmt_init(); // $this->connection is the mysqli connection instance
if($query->prepare($sql)) {

    // Bind Params
    call_user_func_array(array($query,'bind_param'),$params);

    $query->execute(); 

    // Get metadata for field names
    $meta = $query->result_metadata();

    // initialise some empty arrays
    $fields = $results = array();

    // This is the tricky bit dynamically creating an array of variables to use
    // to bind the results
    while ($field = $meta->fetch_field()) { 
        $var = $field->name; 
        $$var = null; 
        $fields[$var] = &$$var; 
    }


    $fieldCount = count($fieldNames);

// Bind Results                                     
call_user_func_array(array($query,'bind_result'),$fields);

$i=0;
while ($query->fetch()){
    for($l=0;$l<$fieldCount;$l++) $results[$i][$fieldNames[$l]] = $fields[$fieldNames[$l]];
    $i++;
}

    $query->close();

    // And now we have a beautiful
    // array of results, just like
    //fetch_assoc
    echo "<pre>";
    print_r($results);
    echo "</pre>";
}

【讨论】:

  • 如果 sql 语句是 $sql = "SELECT first_name,last_name FROM users",它是如何工作的?
  • @lilsizzo 是的,我认为这行不通。我也遇到了这个问题并解决了。我现在没有时间用我的新代码编辑上面的答案,但是给我发电子邮件:eman[AT]onlythebible[DOT]com,如果你愿意,我可以给你发电子邮件给你。
  • 这部分代码 $results[] = $fields;将始终将最后一行的值分配给结果。如果结果不止一行怎么办?
  • @lilsizzo - 我不太明白你的意思;该行$results[] = $fields; 将所有结果加载到数组$results 中。只需执行print_r($results) 即可查看所有结果。您需要通过 foreach 循环访问它们。
  • 只有在选择了一行时才起作用!如果选择了多行,$results 为每一行包含一个数组,但该数组始终填充结果集中的最后一个条目!
【解决方案2】:

如果只选择一行,Emmanuel 的答案就可以正常工作!如果查询选择多行,$results-Array 为每一行保存一个结果,但结果总是用最后一个条目填充。对 fetch() 稍作改动——虽然它运行良好。

$sqlStmt 是一个字符串,用 mysql-query 填充

$params 是一个数组,里面填充了应该传递的变量

$results 是一个空数组,用于保存结果

    if (!is_string($sqlStmt) || empty($sqlStmt)) {
        return false;
    }

    // initialise some empty arrays
    $fields = array();
    $results = array();

    if ($stmt = $this->prepare($sqlStmt)) {
        // bind params if they are set
        if (!empty($params)) {
            $types = '';
            foreach($params as $param) {
                // set param type
                if (is_string($param)) {
                    $types .= 's';  // strings
                } else if (is_int($param)) {
                    $types .= 'i';  // integer
                } else if (is_float($param)) {
                    $types .= 'd';  // double
                } 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);
        }

        // execute query
        $stmt->execute();

        // Get metadata for field names
        $meta = $stmt->result_metadata();

        // This is the tricky bit dynamically creating an array of variables to use
        // to bind the results
        while ($field = $meta->fetch_field()) { 
            $var = $field->name; 
            $$var = null; 
            $fields[$var] = &$$var;
        }

        // Bind Results
        call_user_func_array(array($stmt,'bind_result'),$fields);

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

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

【讨论】:

  • 请注意:这实质上复制了一种MYSQLI_ASSOC 提取的结果集,因为您将得到$result[0]['field_name'] = $field_value; 的效果 - 真的不是问题,只是需要注意的事情如果您更喜欢返回带有数字索引的记录(出于某种原因),则需要稍微调整代码。
  • 我没有得到任何结果......我只能得到字段/列,没有行值......有什么想法吗?
  • 这可行,但我只得到最后一列的值。例如,如果我有...“从用户中选择姓名、年龄、ID”...它将运行并且对于每个用户,如果我调用 $user['name'] 它将返回 ID。与 $user['age'] 相同,它只存储所有 name、age 和 id 键中 id 的最后一个值。
【解决方案3】:

只是将@Emmanuel 和@matzino 的出色答案与选择PDO 而不是mysqli 时可以获得的代码进行比较:

$sql = "SELECT `first_name`,`last_name` FROM `users` WHERE `country` =? AND `state`=?";
$params = array('Australia','Victoria');

$stm = $query->prepare($sql);
$stm->execute($params); 
$results = $stm->fetchAll(); // or fetch() or fetchColumn() depends on expected type

哎呀,就这些?

【讨论】:

  • 确实不错。该函数是 fetch_all() ,您不能在语句上执行 fetch_all() ,而是在结果集上执行。正确的实现是: $resultset = $stm->get_result(); $results = $resultset->fetch_all();
  • @MajidFouladpour 你应该在评论“PDOStatement::fetchAll — 返回一个包含所有结果集行的数组”之前阅读它是一个 pdo 函数,问题是关于 mysqli
【解决方案4】:

使用上面的答案后,我发现自己需要进行一些清理,尤其是“fieldNames[]”部分。下面的代码是程序风格的。我希望它对某人有用。

我从一个可以动态查询数据的类中截取了代码。为了便于阅读,我删除了一些内容。在我的课程中,我允许用户定义定义表和外键,以便限制前端的数据输入以及所述相关数据的过滤和排序选项。这些都是我删除的参数以及自动查询生成器。

$query = "SELECT `first_name`,`last_name` FROM `users` WHERE `country` =? AND `state`=?";
$params = array('Australia','Victoria');

////////////// GENERATE PARAMETER TYPES IF ANY //////////////
// This will loop through parameters, and generate types. ex: 'ss'
$types = '';
$params_size = sizeof($params);
if($params_size > 0)
{
    foreach($params as $param)
    {
        if(is_int($param))
        {
            $types .= 'i';              //integer
        }else if(is_float($param))
        {
            $types .= 'd';              //double
        }else if(is_string($param))
        {
            $types .= 's';              //string
        }else
        {
            $types .= 'b';              //blob and unknown
        }
    }
    array_unshift($params, $types);
}
////////////////////////////////////////////////////////////


// This is the tricky part to dynamically create an array of
// variables to use to bind the results

//below from http://php.net/manual/en/mysqli-result.fetch-field.php
/*
name        The name of the column
orgname     Original column name if an alias was specified
table       The name of the table this field belongs to (if not calculated)
orgtable    Original table name if an alias was specified
def         Reserved for default value, currently always ""
db          Database (since PHP 5.3.6)
catalog     The catalog name, always "def" (since PHP 5.3.6)
max_length  The maximum width of the field for the result set.
length      The width of the field, as specified in the table definition.
charsetnr   The character set number for the field.
flags       An integer representing the bit-flags for the field.
type        The data type used for this field
decimals    The number of decimals used (for integer fields)
*/

/// FIELD TYPE REFERENCE ///
/*
numerics
-------------
BIT: 16
TINYINT: 1
BOOL: 1
SMALLINT: 2
MEDIUMINT: 9
INTEGER: 3
BIGINT: 8
SERIAL: 8
FLOAT: 4
DOUBLE: 5
DECIMAL: 246
NUMERIC: 246
FIXED: 246

dates
------------
DATE: 10
DATETIME: 12
TIMESTAMP: 7
TIME: 11
YEAR: 13

strings & binary
------------
CHAR: 254
VARCHAR: 253
ENUM: 254
SET: 254
BINARY: 254
VARBINARY: 253
TINYBLOB: 252
BLOB: 252
MEDIUMBLOB: 252
TINYTEXT: 252
TEXT: 252
MEDIUMTEXT: 252
LONGTEXT: 252
*/

if($stmt = mysqli_prepare($db_link, $query))
{
    // BIND PARAMETERS IF ANY //
    if($params_size > 0)
    {
        call_user_func_array(array($stmt, 'bind_param'), makeValuesReferenced($params));
    }

    mysqli_stmt_execute($stmt);

    $meta = mysqli_stmt_result_metadata($stmt);


    $field_names = array();
    $field_length = array();
    $field_type = array();
    $output_data = array();

    /// THIS GET THE NAMES OF THE FIELDS AND ASSIGNS NEW VARIABLES USING THE FIELD NAME. THESE VARIABLES ARE THEN SET TO NULL ///
    $count = 0;
    while($field = mysqli_fetch_field($meta))
    {
        $field_names[$count] = $field->name;// field names
        $var = $field->name;
        $$var = null;
        $field_names_variables[$var] = &$$var;// fields variables using the field name
        $field_length[$var] = $field->length;// field length as defined in table
        $field_type[$var] = $field->type;// field data type as defined in table (numeric return)
        $count++;
    }
    setFieldLengthInfo($field_length);
    setFieldTypesInfo($field_type);

    $field_names_variables_size = sizeof($field_names_variables);
    call_user_func_array(array($stmt, 'bind_result'), $field_names_variables);

    $count = 0;
    while(mysqli_stmt_fetch($stmt))
    {
        for($l = 0; $l < $field_names_variables_size; $l++)
        {
            $output_data[$count][$field_names[$l]] = $field_names_variables[$field_names[$l]];/// THIS SETS ALL OF THE FINAL DATA USING THE DYNAMICALLY CREATED VARIABLES ABOVE
        }
        $count++;
    }
    mysqli_stmt_close($stmt);


    echo "<pre>";
    print_r($output_data);
    echo "</pre>";
}


function makeValuesReferenced($arr)
{
    $refs = array();
    foreach($arr as $key => $value)
        $refs[$key] = &$arr[$key];
    return $refs;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-04
    • 1970-01-01
    • 2015-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    相关资源
    最近更新 更多