Как сделать готовое утверждение с помощью multi_query в PHP - PullRequest
2 голосов
/ 28 января 2020

Я сейчас пытаюсь опубликовать некоторые метаданные о пользователе в mysql db, используя php и mysqli. Я получаю массив с парами ключ / значение, соответствующими парам ключ / значение, которые предполагается хранить как в БД.

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = mysqli_connect("127.0.0.1", "root", "", "test");

    foreach($metaData as $key => $value) {
        $metaDataSql .= "insert into usermeta (user_id, meta_key, meta_value) values ($user_id, '$key', '$value');";
    }

    if(!$conn->multi_query($metaDataSql)) {
        echo("Failed to execute the queries." . $conn->error);
    }

    mysqli_close($conn);
}

Мой следующий шаг должен быть защищен от SQL инъекций. Я пытался и не смог сделать этот метод выше с подготовленными заявлениями. Это была моя попытка:

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = mysqli_connect("127.0.0.1", "root", "", "test");

    foreach($metaData as $key => $value) {
        $metaDataSql .= $conn->prepare("insert into usermeta (user_id, meta_key, meta_value) values (?, '?', '?');");
        $metaDataSql->bind_param("iss", $user_id, $key, $value);
    }

    if(!$conn->multi_query($metaDataSql)) {
        echo("Failed to execute the queries." . $conn->error);
    }

    mysqli_close($conn);
}

Можно ли вообще так сделать? если нет - как лучше всего обрабатывать несколько вставок в базе данных, оставаясь защищенным от SQL инъекций.

1 Ответ

4 голосов
/ 28 января 2020

Вы не можете подготовить mysqli::multi_query - но используя подготовленный оператор, вы можете выполнить итерацию по одному и тому же запросу, только с разными значениями. Это более оптимизировано, чем повторение и запуск стандартного query().

mysqli_stmt::bind_param() по ссылке, поэтому вам просто нужно связать его один раз и выполнить для каждой итерации.

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = new mysqli("127.0.0.1", "root", "", "test");
    $stmt = $conn->prepare("INSERT INTO usermeta (user_id, meta_key, meta_value) VALUES (?, ?, ?");
    $stmt->bind_param("iss", $user_id, $key, $value);
    foreach($metaData as $key => $value) {
        $stmt->execute();
    }
    $stmt->close();
    $conn->close();
}

Вы также можете создать один запрос, если хотите выполнить его один раз. Это не обязательно намного быстрее, чем выше, но может быть немного быстрее. Используйте оператор распаковки (или «оператор сплат») ..., чтобы распаковать весь массив, где вы его связали.

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = new mysqli("127.0.0.1", "root", "", "test");
    $query = "INSERT INTO usermeta (user_id, meta_key, meta_value) VALUES ";
    $data = [];

    // Build the query, by appending values and inserting into the $data array
    foreach($metaData as $key => $value) {
        $query .= "(?, ?, ?), ";
        $data[] = $user_id;
        $data[] = $key;
        $data[] = $value;
    }

    // Trim away trailing comma
    $query = rtrim($query, ",");

    // If there are any data to bind, we can execute the query
    if (count($data)) {
        $stmt = $conn->prepare($query);
        $stmt->bind_param(str_repeat("iss", count($metaData)), ...$data);
        $stmt->execute();
        $stmt->close();
    }
    $conn->close();
}

Sidenote
Обычно очень плохая идея инициализировать соединение внутри функции! Вместо этого вы должны передать глобальное соединение в качестве параметра функции. Прямо сейчас, новое соединение устанавливается каждый раз, когда вы используете эту функцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...