动态准备语句是否糟糕?(使用php+mysqli)

动态准备语句是否糟糕?(使用php+mysqli),php,sql,dynamic,eval,prepared-statement,Php,Sql,Dynamic,Eval,Prepared Statement,我喜欢动态SQL的灵活性,喜欢预先准备好的语句的安全性和改进的性能。所以我真正想要的是动态准备语句,这很麻烦,因为bind_param和bind_result接受固定数量的参数。所以我使用了一个eval语句来回避这个问题。但我觉得这是个坏主意。下面是我的意思的示例代码 // array of WHERE conditions $param = array('customer_id'=>1, 'qty'=>'2'); $stmt = $mysqli->stmt_init();

我喜欢动态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);
最后的评估报告是个坏主意吗?我试图通过在变量名$param后面封装值来避免代码注入


有人有意见或其他建议吗?是否有需要注意的问题?

我认为在这里使用eval是危险的

试试这个:

迭代params数组以构建带问号的SQL字符串SELECT*fromt1,其中p1=?p2=? 打电话准备 使用call_user_func_数组调用bind_param,传入动态参数数组。 守则:

call_user_func_array(array($stmt, 'bind_param'), array($types)+$param);

您并不需要准备好的语句和绑定参数,因为您可以始终使用mysql\u real\u escape\u字符串。你是对的;动态生成的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));

我制作了一个过滤函数,它接收一个数组,一个类似$\u 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);
}

并且可以很好地过滤结果

您可能需要重新表述您的问题标题,以便其他人可以更轻松地找到您的问题。绑定参数除了转义之外,还增加了额外的安全性。使用它们是一种很好的做法。你能告诉我它们提供了什么额外的安全性吗?与我的解决方案相比,绑定参数可以保护您免受什么样的攻击?因为如果你指的是程序员犯错误的能力,我宁愿使用功能更强大的工具而不是约束工具。如果$param是一个索引为0的数组,那么$param中的该值将被删除,取而代之的是$types,并且您将因为变量少于字符串中所指示的变量而收到错误。