【问题标题】:Dynamically build a prepared statement with call_user_func_array() [duplicate]使用 call_user_func_array() 动态构建准备好的语句 [重复]
【发布时间】:2016-05-01 07:51:59
【问题描述】:

我需要根据用户输入动态构建 SQL 语句和参数。 sql 语句的长度和参数的数量根据用户输入而变化。我正在尝试使用 this tutorial 并将其应用于我的代码。代码如下:

$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 s.country = ?";
        $a_params[] = $place['country'];
        $a_param_type[] = "s";
    // prepare and bind

    $param_type = '';

    foreach ($place as $key => $value) {
        if ($key === "country") {
            continue;
        }
        $query .= " and s.$key = ?";
        $a_params[] = $value;
        $a_param_type[] = "s";
    }
    /* Prepare statement */
    $stmt = $conn->prepare($query);
    if($stmt === false) {
        trigger_error('Wrong SQL: ' . $sql . ' Error: ' . $conn->errno . ' ' . $conn->error, E_USER_ERROR);
    }

    $a_params[] =  $a_param_type;

    /* use call_user_func_array, as $stmt->bind_param('s', $param); does not accept params array */
    call_user_func_array(array($stmt, 'bind_param'), $a_params);

    /* Execute statement */
    $stmt->execute();   
    $meta = $stmt->result_metadata();

我知道$place['country'] 将始终被填充。 sql语句是正确的。它是:

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

不要介意 "\n" 字符,它们对 sql 语句没有影响。

在:

call_user_func_array(array($stmt, 'bind_param'), $a_params);

$a_params 的值为:

0:"New Zealand"
1:"Grey Lynn"
2:"Auckland"
3:"Auckland"
4:array(4)
0:"s"
1:"s"
2:"s"
3:"s"

在:

$meta = $stmt->result_metadata();

$meta 的值变成:

current_field:0
field_count:13
lengths:null
num_rows:0
type:1

表示没有从数据库中选择任何行。我已经在数据库上手动执行了这个 sql,它返回了行。我的代码有什么问题,导致它没有从数据库返回任何行?

编辑:我看到this answer 说要把“ssss”放在$params 的开头,所以我这样做了,并在$stmt 对象中得到了这个错误:

errno:2031
error:"No data supplied for parameters in prepared statement"
error_list:array(1)
propertyNode.hasAttribute is not a function

【问题讨论】:

  • 你的call_user_func_array() 参数不应该有点不同吗?更像string of types, value1,value2 等等。
  • @RST 我试过这样:0:"New Zealand" 1:"Grey Lynn" 2:"Auckland" 3:"Auckland" 4:"ssss" 和这个:0:"ssss" 1:"New Zealand" 2:"Grey Lynn" 3:"Auckland" 4:"Auckland" 但它没有返回任何行。
  • 在使用 $stmt->bind_param('s', $param);implode() 数组之前会发生什么
  • @RST 你能给我一个代码示例吗?我想确保我尝试正确。
  • ssss 是字符串还是数组?

标签: php mysql mysqli


【解决方案1】:

我不明白你尝试了什么方法,但我会尽力回答:

根据bind_param manual

bind_param 的第一个参数是一个字符串,如'ssss'

second 和其他参数 - 是要插入到查询中的值。

所以,你的 $a_params 数组应该是

0:"New Zealand"
1:"Grey Lynn"
2:"Auckland"
3:"Auckland"
4:array(4)
0:"s"
1:"s"
2:"s"
3:"s"

但是:

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

看到了吗?所有值都是字符串。而占位符的类型是第一个。

还要考虑$a_params 中的参数顺序必须与bind_param 中的参数顺序相同。这意味着,即$a_params 喜欢

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

错了。因为$a_params 的第一个元素将是bind_param 的第一个参数,在这种情况下它不是"ssss" 字符串。

所以,这意味着在你用值填充$a_params 后,占位符的字符串应该添加到$a_params 的开头,例如array_unshift

// make $a_param_type a string
$str_param_type = implode('', $a_param_type);

// add this string as a first element of array
array_unshift($a_params, $str_param_type);

// try to call
call_user_func_array(array($stmt, 'bind_param'), $a_params);

如果这不起作用,您可以参考answer you provided 的一部分,其中$a_params 的值通过引用传递给另一个数组$tmp,在您的情况下,您可以尝试以下操作:

// make $a_param_type a string
$str_param_type = implode('', $a_param_type);

// add this string as a first element of array
array_unshift($a_params, $str_param_type);

$tmp = array();
foreach ($a_params as $key => $value) {
    // each value of tmp is a reference to `$a_params` values
    $tmp[$key] = &$a_params[$key];  
}

// try to call, note - with $tmp, not with $a_params
call_user_func_array(array($stmt, 'bind_param'), $tmp);

【讨论】:

    【解决方案2】:

    记住:call_user_func_array 的第二个参数需要被引用,而不仅仅是一个普通的数组。这是关键。接受的答案很好,但只是缺少一件事,使参数被引用:

        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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-26
      • 1970-01-01
      • 2016-08-30
      • 1970-01-01
      • 2013-08-30
      • 1970-01-01
      相关资源
      最近更新 更多