Как мне перевести эту восприимчивую к SQL-функции функцию в подготовленную функцию в MySQLi? - PullRequest
0 голосов
/ 27 октября 2019

У меня есть две функции, которые восприимчивы к sql-инъекциям, мне удалось перевести первую в подготовленную функцию, но я не вижу, как перевести вторую. Это оригинал первый:

    function modify($sql, &$id)
    {
        $link = database_link();

        $result = mysqli_query($link, $sql);

        $insertId = mysqli_insert_id($link);

        return mysqli_affected_rows($link);
    }

Который я перевел на:

    function preparedModify($sql, $types, &$insertId, ...$value)
    {
        $statement = mysqli_prepare(database_link(), $sql);

        $statement->bind_param($types, ...$value);

        $statement->execute();

        $insertId = $statement->insert_id;

        return $statement->affected_rows;
    }

Который сработал, и я доволен. Это моя вторая функция, которую нужно перевести:

    function select($sql, &$rows)
    {

        $link = database_link();

        $result = mysqli_query($link, $sql);

        $rows = array();

        while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
            $rows[] = $row;
        }

        return mysqli_num_rows($result);
    }

Мне совершенно непонятно, как бы я перевел ее как новичка. Не могли бы вы помочь мне, в то же время объясняя свои ответы, чтобы указать, какова стратегия вашего перевода? Потому что моя стратегия для первой функции была просто в поиске альтернативных функций, и таким образом мне удалось сделать первую, но я нигде не достиг второй, используя тот же подход.

Ответы [ 2 ]

1 голос
/ 27 октября 2019

Если у вас установлено mysqlnd (чтобы разрешить использование mysqli_stmt::get_result), изменения могут быть довольно простыми. Первая часть такая же, как preparedModify, затем мы просто используем вызов get_result, а остальная часть функции остается такой же:

function preparedSelect($sql, $types, &$rows, ...$value)
{
    $statement = mysqli_prepare(database_link(), $sql);

    $statement->bind_param($types, ...$value);

    $statement->execute();

    $result = $statement->get_result();

    $rows = array();

    while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
        $rows[] = $row;
    }

    return $statement->num_rows;
}

Как уже было сказано, вам не нужноцикл для генерации $rows, вы можете просто использовать mysqli_result::fetch_all:

$rows = $result->fetch_all(MYSQLI_ASSOC);

Опять же, для этого требуется установить собственный драйвер mysqlnd.

1 голос
/ 27 октября 2019

Я должен признать, что при выполнении подготовленных запросов SELECT есть одна странность: вы не можете просто получить знакомый массив прямо из оператора, поэтому вам потребуется дополнительный вызов функции get_result(). Но, по крайней мере, вы могли бы начать так же, как и с другой функцией, так как это правильный способ запустить функцию с подготовленными операторами.

Еще пара замечаний

  • Полагаю, database_link() создает новое соединение с базой данных каждый раз, когда оно вызывается. Так не должно быть, соединение должно быть создано только один раз. Поэтому сначала создайте его, а затем передайте переменную $link во все вызовы функций.
  • , возвращающий результат функции через параметр, уродлив и нечитабелен, и по этим причинам вызывает недовольство. И вам не нужно возвращать количество строк - это просто не имеет смысла, так как вы всегда можете использовать count($rows), чтобы получить количество.
  • вставлять отдельные переменные в вызов функции крайне неудобно, я научился этому нелегко. Вместо этого поместите их в виде массива. Таким образом, вы сможете иметь отдельные переменные или одну переменную, которая уже содержит все необходимые данные.

Учитывая все это, мы можем создать простую в использовании и чтении процедуру, основанную наmy Вспомогательная функция Mysqli :

function select($link, $sql, $values = [], $types = '')
{
    if (!$values) {
        $result = $link->query($sql);
    } else {
        $types = $types ?: str_repeat("s", count($values));
        $stmt = $mysqli->prepare($sql);
        $stmt->bind_param($types, ...$values);
        $stmt->execute();
        $result = $stmt->get_result();
    }
    return $result->fetch_all(MYSQLI_ASSOC);   
}

$link = database_link();
$rows = select($link, "SELECT * FROM employees WHERE salary > ?", [$gross]);
if ($rows) {
    // you don't actually need even a count() call
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...