Динамические подготовленные операторы плохие? (с php + mysqli) - PullRequest
7 голосов
/ 14 октября 2008

Мне нравится гибкость динамического 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.

У кого-нибудь есть мнение или другие предложения? Есть ли проблемы, о которых мне нужно знать?

Ответы [ 3 ]

14 голосов
/ 14 октября 2008

Я думаю, здесь eval() опасно.

Попробуйте это:

  • итерация массива params для построения строки SQL с вопросительными знаками "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);
0 голосов
/ 05 января 2019

Я сделал функцию фильтра, которая получает массив в виде асоциативного массива, как $ _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);
}

И прекрасно работает для фильтрации результатов

0 голосов
/ 15 октября 2008

Вам не нужны подготовленные операторы и связанные аргументы, потому что вы всегда можете использовать 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));
...