PHP函数使用准备好的SQL语句 - 使用implode输入用逗号分隔的数组值作为函数的参数

时间:2014-07-12 22:02:55

标签: php sql function mysqli arguments

我最近询问了一些有关针对SQL注入漏洞的安全性的问题。我决定创建一个使用预准备语句进行sql查询的函数,这样我就不必为每个查询写出如此多的代码行:

function secure_sql($query, $values, $datatypes){
    global $link;
    $stmt = mysqli_prepare($link, $query);
    foreach($values as &$value){
        $value = mysqli_real_escape_string($link, $value);
        mysqli_stmt_bind_param($stmt, substr($datatypes, 0), $value);
        $datatypes = substr($datatypes, -(strlen($datatypes)-1));
    }
    mysqli_stmt_execute($stmt);
    mysqli_stmt_bind_result($stmt, implode(", ", $values));
    $results = mysqli_stmt_get_result($stmt);
    return $results;
}

其中query是准备好的查询(带有?s),$ values是替换占位符的所有变量的数组(按顺序),$ datatypes是包含变量的所有数据类型的字符串。 $ link是一个数据库连接。

所以我有两个主要问题。

1)它不起作用。我认为这必须是因为在这种情况下可能没有正确使用内爆。我会用什么呢?我不能使用call_user_func_array,因为我还需要将$ stmt作为参数。我已经尝试使用array_unshift将$ stmt添加到参数的开头,但它不起作用。

2)如果我确实让它发挥作用,可以采取哪些措施来改善它?我仍然是PHP和SQL的初学者。

编辑:我现在已经解决了这个问题。谢谢大家的有益评论和答案!

1 个答案:

答案 0 :(得分:1)

不是直接给你我提出的代码,我觉得解释它是必要的,看看你的一些思维方式是不正确的。

首先,mysqli_stmt_bind_param()的使用让我困惑 - 你的第二个参数表达式(substr($datatypes, 0))返回所有数据类型的值,但你只绑定一个。我认为你打算提出的是:

mysqli_stmt_bind_param($stmt, $datatypes[0] /* <-- retrieves the first character */, $value);

但更重要的是,你应该只调用mysqli_stmt_bind_param() 一次,这会给你带来更大的困难......要省略foreach - 循环,{{1 }}? (实际上,将call_user_func_array()作为参数很有可能):

$stmt

如果您对上述内容感到困惑,请以这种方式查看:

call_user_func_array("mysqli_stmt_bind_param", array_merge(array($stmt, $datatypes), $values)); 
//calls mysqli_stmt_bind_param($stmt, $datatypes, $values[0], $values[1]... values[n]);

但是,这需要您的call_user_func_array //a call to mysqli_stmt_bind_param, with all the appropriate parameters ( "mysqli_stmt_bind_param", //the function to call array_merge //an array consisting of the statement, the datatype and all the values ( array($stmt, $datatypes), //statement and datatype-parameters $values //all the values ) ); - 数组由引用组成,因为$values期望您的值是这样的。如果您仍想将它们作为值传递到函数中,可以添加它,然后将mysqli_stmt_bind_param传递到$ref_values函数中:

call_user_func_array()

现在我们开始使用foreach ($values as &$value) $ref_values[] = &$value; ,这也是不正确的。参考PHP手册:

内爆(返回值):

  

返回一个字符串,其中包含相同顺序的所有数组元素的字符串表示形式,每个元素之间都有粘合字符串。

mysqli_stmt_bind_result(第二个参数):

  

要绑定的变量。

所以你在这里尝试做的是让implode()string变量返回,这没有任何意义。幸运的是,mysqli_stmt_get_result()返回一个在执行后获取的对象,这意味着不需要bind_result函数。所以尝试删除该行。

总而言之,我会重写这样的代码:

implode()

对我而言,这听起来应该有效,但如果它不起作用,请告诉我(我可能会遗漏某些东西或在某处错误思考)。

要回答第二个问题,这一切看起来都不错,除非我不建议您使用function secure_sql($query, $values, $datatypes) { global $link; $stmt = mysqli_prepare($link, $query); foreach ($values as &$value) $ref_values[] = &$value; call_user_func_array("mysqli_stmt_bind_param", array_merge(array($stmt, $datatypes), $ref_values)); mysqli_stmt_execute($stmt); return mysqli_stmt_get_result($stmt); } - 函数,因为它们最终会被删除(如PHP-manual中所述)。如果您计划使用对象,那么大多数对象在我的更改时都是相似的(除了您需要使用对象属性和对象方法而不是...),除了mysqli_功能。幸运的是,通过将数组指定为第一个参数,包括对象和方法名称(call_user_func_array()),也可以使用它调用方法。

编辑:考虑到它的必要性,我创建了一个执行相同操作的函数,但改为使用mysqli对象:

call_user_func_array($prepared_obj, "bind_param") ...)