简体   繁体   English

使call_user_func_array()与准备好的sql语句一起使用

[英]Getting call_user_func_array() to work with prepared sql statements

I have to build up a sql statement and the params from a $place object that has a variable number of properties. 我必须从具有可变数量属性的$ place对象构建一个sql语句和参数。 When I use prepared sql statements by building the sql statement and params the long and bad practice way it works (returns all the rows from the database that it should): 当我通过构建sql语句和参数来使用准备好的sql语句时,它会采用长期有效的做法(从数据库中返回它应该返回的所有行):

<?

function buildSQLWhereClause($query, $conn, $place) {
    if ($place['suburb']){
        if($place['city'] && $place['province'] && $place['country']) {
            $query .= "s.country = ? and 
                    s.province = ? and
                    s.city = ? and 
                    s.suburb = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("ssss", $place['country'], $place['province'], $place['city'], $place['suburb']);
        } else if ($place['province'] && $place['country']) {
            $query .= "s.country = ? and 
                    s.province = ? and
                    s.suburb = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("sss", $place['country'], $place['province'], $place['suburb']);
        } else if ($place['city'] && $place['province']) {
            $query .= "s.province = ? and
                    s.city = ? and 
                    s.suburb = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("sss", $place['province'], $place['city'], $place['suburb']);
        } else if ($place['city'] && $place['country']) {
            $query .= "s.country = ? and
                    s.city = ? and 
                    s.suburb = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("sss", $place['country'], $place['city'], $place['suburb']);
        } else if ($place['city']) {
            $query .= "s.city = ? and 
                    s.suburb = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("ss", $place['city'], $place['suburb']);
        } else if ($place['province']) {
            $query .= "s.province = ? and 
                    s.suburb = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("ss", $place['province'], $place['suburb']);
        } else if ($place['country']) {
            $query .= "s.country = ? and 
                    s.suburb = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("ss", $place['country'], $place['suburb']);
        } else {
            $query .= "s.suburb = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("s", $place['suburb']);
        }
//////////////////////////// NO SUBURB ///////////////////////////////////////////////////        
    } else if ($place['city']) {
        if ($place['province'] && $place['country']) {
            $query .= "s.country = ? and 
                    s.province = ? and
                    s.city = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("sss", $place['country'], $place['province'], $place['city']);
        } else if ($place['province']) {
            $query .= "s.province = ? and
                    s.city = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("ss", $place['province'], $place['city']); 
        } else if ($place['country']) {
            $query .= "s.country = ? and
                    s.city = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("ss", $place['country'], $place['city']); 
        } else {
            $query .= "s.city = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("s", $place['city']); 
        }
//////////////////////// NO SUBURB OR CITY ////////////////////////////////////////////////////////
    } else if ($place['province']) {
        if ($place['country']) {
            $query .= "s.country = ? and
                    s.province = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("ss", $place['country'], $place['province']); 
        } else {
            $query .= "s.province = ?";

            // prepare and bind
            $stmt = $conn->prepare($query);

            $stmt->bind_param("s", $place['province']); 
        }
//////////////////////////////// NO SUBURB, CITY, OR PROVINCE ///////////////////////////////        
    } else if ($place['country']) {
        $query .= "s.country = ?";

        // prepare and bind
        $stmt = $conn->prepare($query);

        $stmt->bind_param("s", $place['country']); 
    }

    return $stmt;
}

function queryDbForProducts($conn, $place)
{   

    $query = "SELECT p.*, s.* 
            FROM product p 
            INNER JOIN product_shop ps 
            ON ps.p_id = p.p_id 
            INNER JOIN shop s 
            ON s.s_id = ps.s_id 
            WHERE ";

    $stmt = buildSQLWhereClause($query, $conn, $place);

    $stmt->execute();
    $meta = $stmt->result_metadata();

    while ($field = $meta->fetch_field()) {
        $parameters[] =& $row[$field->name];
    }

When I use sql prepared statements by building up the sql statement and params the much better way, it doesn't work: 当我通过更好的方式构建sql语句和参数来使用sql prepare语句时,它不起作用:

<?

function buildSQLWhereClause($place) {
    $query = "SELECT p.*, s.* FROM product p INNER JOIN product_shop ps ON ps.p_id = p.p_id INNER JOIN shop s ON s.s_id = ps.s_id WHERE ";
    $queryParams = [];
    $queryParamTypes = "";
    $i = 0;
    $len = count($place);
    foreach ($place as $key => $value) {
        if ($i == $len - 1) {
            $query .= "$key = ?";
            $queryParams[] = $value;
            $queryParamTypes .= "s";
        } else {
            $query .= "$key = ? AND ";
            $queryParams[] = $value;
            $queryParamTypes .= "s";
        }
        $i++;
    }

    return array(
            "query" => $query,
            "queryParams" => $queryParams,
            "queryParamTypes" => $queryParamTypes
        );
}

function queryDbForProducts($conn, $place)
{   
    $queryObject = buildSQLWhereClause($place);
    $query = $queryObject['query'];
    $queryParams = $queryObject['queryParams'];
    $queryParamTypes = $queryObject['queryParamTypes'];


    // prepare and bind
    $stmt = $conn->prepare($query);
    $stmt->bind_param($queryParamTypes, $queryParams); 
    $stmt->execute();
    $meta = $stmt->result_metadata();

Hovering over the $stmt in the debugger shows: 将鼠标悬停在调试器中的$ stmt上显示:

affected_rows:-1
insert_id:0
num_rows:0
param_count:4
field_count:13
errno:2031
error:"No data supplied for parameters in prepared statement"
error_list:array(1)
sqlstate:"HY000"
id:1

No data supplied? 没有数据? Hovering over the $queryParams parameter in the debugger shows: 将鼠标悬停在调试器中的$ queryParams参数上将显示:

0:"Grey Lynn"
1:"Auckland"
2:"Auckland"
3:"New Zealand"

So I did provide the query parameters to the $stmt->bind_param() function. 因此,我确实向$stmt->bind_param()函数提供了查询参数。 Did I provide them in the wrong format? 我提供的格式错误吗?

Hovering over $QueryParamTypes shows: 将鼠标悬停在$ QueryParamTypes上将显示:

"ssss"

Hovering over $query shows: 将鼠标悬停在$ query上显示:

"SELECT p.*, s.* FROM product p INNER JOIN product_shop ps ON ps.p_id = p.p_id INNER JOIN shop s ON s.s_id = ps.s_id WHERE suburb = ? AND city = ? AND province = ? AND country = ?"

How come it works when done with the code at the top of the question and it doesn't work when done with the code without all the is statements? 在问题顶部使用代码完成后,它如何工作,而在没有所有is语句的情况下使用代码时,它不起作用?

bind_param does not take an array as an argument, it takes varargs. bind_param不使用数组作为参数,而是使用varargs。 You will need to use call_user_func_array if you want to call it with a dynamic number of arguments. 如果要使用动态数量的参数调用它,则需要使用call_user_func_array

ie

$params = array_unshift($queryParams, $queryParamTypes);
call_user_func_array(array($stmt, "bind_param"), $params);

The params passed to call_user_func_array, parameter 2, need to be referenced. 需要引用传递给call_user_func_array参数2的参数。

This is the working solution: 这是可行的解决方案:

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

$stmt = $conn->prepare($query);
//$stmt->bind_param($queryParamTypes, $queryParams); 
call_user_func_array(array($stmt, 'bind_param'), makeValuesReferenced($queryParams));
$stmt->execute();

As per this answer 按照这个答案

Not knowing about the reference thing mucked me around for a long time. 不了解参考资料使我困扰了很长时间。 Hope this helps some people. 希望这对某些人有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM