我喜欢Dynamic SQL的灵活性,我喜欢准备语句的安全性+改进性能。所以我真正想要的是动态准备语句,这很麻烦,因为bind_param和bind_result接受“固定”数量的参数。所以我使用了一个eval()语句来解决这个问题。但我觉得这是一个坏主意。这是我的意思的示例代码
// array of WHERE conditions
$param = array('customer_id'=>1, 'qty'=>'2');
$stmt = $mysqli->stmt_init();
$types = ''; $bindParam = array(); $where = ''; $count = 0;
// build the dynamic sql and param bind conditions
foreach($param as $key=>$val)
{
$types .= 'i';
$bindParam[] = '$p'.$count.'=$param["'.$key.'"]';
$where .= "$key = ? AND ";
$count++;
}
// prepare the query -- SELECT * FROM t1 WHERE customer_id = ? AND qty = ?
$sql = "SELECT * FROM t1 WHERE ".substr($where, 0, strlen($where)-4);
$stmt->prepare($sql);
// assemble the bind_param command
$command = '$stmt->bind_param($types, '.implode(', ', $bindParam).');';
// evaluate the command -- $stmt->bind_param($types,$p0=$param["customer_id"],$p1=$param["qty"]);
eval($command);
最后一个eval()语句是个坏主意吗?我试图通过将值封装在变量名$ param。
后面来避免代码注入有没有人有意见或其他建议?我需要注意哪些问题?
答案 0 :(得分:14)
我认为在这里使用eval()
是危险的。
试试这个:
"SELECT * FROM t1 WHERE p1 = ? AND p2 = ?"
prepare()
call_user_func_array()
来调用bind_param()
,传入动态参数数组。代码:
call_user_func_array(array($stmt, 'bind_param'), array($types)+$param);
答案 1 :(得分:0)
我制作了一个过滤器函数,该函数将一个数组作为一个关联数组,如$ _GET:
在模型类中,我定义了几个属性,包括模式:
private $table_name = "products";
protected $schema = [
'id' => 'INT',
'name' => 'STR',
'description' => 'STR',
'size' => 'STR',
'cost' => 'INT',
'active' => 'BOOL'
];
然后是过滤器方法,该方法可检索条件的关联数组:
function filter($conditions)
{
$vars = array_keys($conditions);
$values = array_values($conditions);
$where = '';
foreach($vars as $ix => $var){
$where .= "$var = :$var AND ";
}
$where =trim(substr($where, 0, strrpos( $where, 'AND ')));
$q = "SELECT * FROM {$this->table_name} WHERE $where";
$st = $this->conn->prepare($q);
foreach($values as $ix => $val){
$st->bindValue(":{$vars[$ix]}", $val, constant("PDO::PARAM_{$this->schema[$vars[$ix]]}"));
}
$st->execute();
return $st->fetchAll(PDO::FETCH_ASSOC);
}
非常适合过滤结果
答案 2 :(得分:-1)
你真的不需要预准备语句和绑定参数,因为你总是可以使用mysql_real_escape_string()。而你是对的;动态生成的SQL更灵活,更有价值。
这是一个使用常规mysql_ *接口的简单示例:
// Array of WHERE conditions
$conds = array("customer_id" => 1, "qty" => 2);
$wherec = array("1");
foreach ($conds as $col=>$val) $wherec[] = sprintf("`%s` = '%s'", $col, mysql_real_escape_string($val));
$result_set = mysql_query("SELECT * FROM t1 WHERE " . implode(" AND ", $wherec);
当然,这是一个简单的例子,为了使它变得有用,你必须对它进行大量的构建和改进,但是它显示了这些想法并且它非常有用。例如,这是一个完全通用的函数,用于将新行插入到任意表中,其中列填充了关联数组中的值,并完全安全地注入SQL:
function insert($table, $record) {
$cols = array();
$vals = array();
foreach (array_keys($record) as $col) $cols[] = sprintf("`%s`", $col);
foreach (array_values($record) as $val) $vals[] = sprintf("'%s'", mysql_real_escape_string($val));
mysql_query(sprintf("INSERT INTO `%s`(%s) VALUES(%s)", $table, implode(", ", $cols), implode(", ", $vals)));
}
// Use as follows:
insert("customer", array("customer_id" => 15, "qty" => 86));