Mysqli: работа с MySQL в PHP от А до Я
Основное решение: объектно-ориентированный стиль с подготовленными запросами
Расширение mysqli (MySQL Improved) предоставляет интерфейс для взаимодействия с серверами MySQL версии 4.1.3 и выше. Наиболее эффективным подходом считается использование объектно-ориентированного API с подготовленными запросами (prepared statements). Этот метод защищает от SQL-инъекций и повышает производительность при многократном выполнении однотипных запросов.
Как безопасно подключиться к MySQL и выполнить запрос с параметрами?
// Создание объекта mysqli
$mysqli = new mysqli('localhost', 'user', 'password', 'database');
// Проверка соединения
if ($mysqli->connect_errno) {
die('Ошибка подключения: ' . $mysqli->connect_error);
}
// Подготовка запроса
$stmt = $mysqli->prepare('SELECT id, name FROM users WHERE email = ?');
// Привязка параметра (s - string)
$stmt->bind_param('s', $email);
$email = 'user@example.com';
// Выполнение
$stmt->execute();
// Получение результата
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
echo $row['id'] . ' - ' . $row['name'] . "\n";
}
// Закрытие
$stmt->close();
$mysqli->close();Php управление com (управление com-объектами в php)
1 - Иван Иванов
Php modules (установка и использование модулей php)
Типичные проблемы:
- Ошибка подключения - проверьте имя хоста, логин, пароль, порт (по умолчанию 3306). Используйте
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);для преобразования ошибок в исключения. - Неверная кодировка - после подключения выполните
$mysqli->set_charset('utf8mb4');. - Утечка памяти - всегда освобождайте результат (
$stmt->free_result()) и закрывайте запросы.
Вариант: процедурный стиль mysqli
Процедурный API использует функции с префиксом mysqli_. Он удобен для быстрых скриптов без ООП, но менее структурирован.
$link = mysqli_connect('localhost', 'user', 'password', 'database');
if (!$link) {
die('Ошибка: ' . mysqli_connect_error());
}
$query = 'SELECT * FROM products WHERE price > ?';
$stmt = mysqli_prepare($link, $query);
mysqli_stmt_bind_param($stmt, 'd', $price);
$price = 100.50;
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
while ($product = mysqli_fetch_assoc($result)) {
echo $product['name'] . ' - ' . $product['price'] . "\n";
}
mysqli_stmt_close($stmt);
mysqli_close($link);
Php mysqli extension (расширение mysqli в php)
При процедурном стиле легко забыть передать ссылку на соединение в каждую функцию. Также не забывайте проверять возвращаемые значения на false.
Вариант: выполнение простых запросов без подготовки
Если запрос не содержит пользовательских данных, можно использовать query(). Этот метод выполняет запрос напрямую и возвращает объект mysqli_result.
$mysqli = new mysqli('localhost', 'user', 'password', 'database');
$result = $mysqli->query('SELECT COUNT(*) AS cnt FROM users');
if ($result) {
$row = $result->fetch_assoc();
echo 'Всего пользователей: ' . $row['cnt'];
$result->free();
}Всего пользователей: 42
Никогда не используйте query() с подстановкой пользовательских данных - это прямой путь к SQL-инъекции. Для динамических значений применяйте подготовленные запросы.
Вариант: множественные запросы через multi_query
multi_query() позволяет выполнить несколько SQL-команд, разделённых точкой с запятой, за один вызов. Используется при импорте данных или выполнении пакета изменений.
$mysqli = new mysqli('localhost', 'user', 'password', 'database');
$sql = 'UPDATE users SET status = 1 WHERE id = 1;';
$sql .= 'UPDATE users SET status = 0 WHERE id = 2;';
if ($mysqli->multi_query($sql)) {
do {
if ($result = $mysqli->store_result()) {
$result->free();
}
} while ($mysqli->more_results() && $mysqli->next_result());
}
if ($mysqli->errno) {
echo 'Ошибка: ' . $mysqli->error;
}Опасность множественных запросов - возможность SQL-инъекции при конкатенации пользовательских данных. Также невозможно использовать подготовленные запросы с multi_query().
Вариант: транзакции в mysqli
Транзакции обеспечивают атомарность изменений. В mysqli транзакции управляются методами begin_transaction(), commit() и rollback().
$mysqli->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
try {
$mysqli->query('UPDATE accounts SET balance = balance - 100 WHERE id = 1');
$mysqli->query('UPDATE accounts SET balance = balance + 100 WHERE id = 2');
$mysqli->commit();
} catch (Exception $e) {
$mysqli->rollback();
echo 'Транзакция отменена: ' . $e->getMessage();
}Следите за поддержкой транзакций движком таблиц (InnoDB, не MyISAM). При возникновении ошибки в середине транзакции автоматического отката не происходит - необходим явный вызов rollback().
Вариант: получение всех строк результата в виде массива
Метод fetch_all() возвращает все строки результата в виде вложенного массива. Это удобно, когда нужно сразу обработать весь набор данных.
$result = $mysqli->query('SELECT id, name FROM users LIMIT 3');
$rows = $result->fetch_all(MYSQLI_ASSOC);
foreach ($rows as $row) {
echo $row['id'] . ': ' . $row['name'] . "\n";
}1: Иван 2: Мария 3: Петр
При больших объёмах данных fetch_all() может потреблять много памяти. В таких случаях лучше использовать итеративный fetch_assoc().
Расширенные примеры использования mysqli
Как работать с хранимыми процедурами и OUT-параметрами?
Хранимые процедуры могут возвращать значения через OUT-параметры. В mysqli для этого используется метод bind_param() с указанием типа 's', 'i', 'd' и передачей переменной по ссылке.
Пример процедуры:
DELIMITER //
CREATE PROCEDURE get_user_name(IN user_id INT, OUT name VARCHAR(100))
BEGIN
SELECT name INTO name FROM users WHERE id = user_id;
END //
DELIMITER ;Код PHP:
$mysqli = new mysqli('localhost', 'user', 'password', 'database');
$stmt = $mysqli->prepare('CALL get_user_name(?, ?)');
$stmt->bind_param('is', $id, $name);
$id = 1;
$stmt->execute();
// После выполнения OUT-параметр доступен
$stmt->bind_result($name);
$stmt->fetch();
echo 'Имя пользователя: ' . $name;
$stmt->close();Имя пользователя: Иван
В некоторых версиях MySQL после вызова хранимой процедуры может остаться неиспользованный результат. Вызовите $stmt->free_result() или $mysqli->next_result(), чтобы избежать ошибок при последующих запросах.
Как настроить асинхронные запросы?
Асинхронные запросы позволяют не блокировать выполнение скрипта во время ожидания ответа от БД. Используйте MYSQLI_ASYNC флаг и опрос результатов.
$mysqli = new mysqli('localhost', 'user', 'password', 'database');
$mysqli->query('SELECT SLEEP(2)', MYSQLI_ASYNC);
// Делаем другую работу
sleep(1);
echo 'Ожидание...';
// Проверяем готовность
$links = $errors = $reject = [$mysqli];
if (mysqli_poll($links, $errors, $reject, 1)) {
$result = $mysqli->reap_async_query();
if ($result) {
print_r($result->fetch_assoc());
$result->free();
}
}Ожидание... Array ( [SLEEP(2)] => 0 )
Асинхронный режим доступен не во всех сборках PHP. Убедитесь, что используется родной драйвер mysqlnd. Также требуется корректная обработка таймаутов.
Как выполнить массовую вставку с помощью подготовленных запросов?
При многократной вставке одинаковой структуры данных подготовленные запросы позволяют менять только параметры, экономя ресурсы сервера.
$data = [
['user1@example.com', 'User1'],
['user2@example.com', 'User2'],
['user3@example.com', 'User3'],
];
$stmt = $mysqli->prepare('INSERT INTO users (email, name) VALUES (?, ?)');
$stmt->bind_param('ss', $email, $name);
foreach ($data as $row) {
$email = $row[0];
$name = $row[1];
$stmt->execute();
}
echo 'Добавлено строк: ' . $stmt->affected_rows;
$stmt->close();Добавлено строк: 3
При очень большом количестве записей (сотни тысяч) лучше использовать LOAD DATA INFILE или пакетные вставки с одним запросом INSERT INTO ... VALUES (...), (...).
Как использовать mysqli::options для настройки соединения?
Перед открытием соединения можно задать опции через mysqli_options() или метод options(). Например, установить таймаут или режим авто-коммита.
$mysqli = new mysqli();
$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 10);
$mysqli->options(MYSQLI_INIT_COMMAND, 'SET NAMES utf8mb4');
$mysqli->real_connect('localhost', 'user', 'password', 'database');
// Далее обычная работаНекоторые опции (например, MYSQLI_SET_CHARSET_NAME) могут не поддерживаться старыми версиями libmysqlclient. Всегда проверяйте документацию вашего окружения.