MySQLi
Кроме PDO, в PHP есть второе расширение для баз данных — MySQLi. Оно работает только с MySQL и MariaDB, зато знакомо почти каждому: на нём написано огромное количество существующих сайтов. Понимать оба способа нужно, чтобы читать чужой код.
Примерам этого урока нужен сервер MySQL, поэтому здесь они не запускаются.
Старые функции mysql_
В старых учебниках встречаются mysql_connect() и mysql_query(). Этих функций больше нет: их удалили из PHP в версии 7. Их современная замена — MySQLi, буква «i» означает «improved», улучшенный.
Подключение
Объект mysqli получает адрес сервера, имя пользователя, пароль и название базы:
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$db = new mysqli('localhost', 'root', 'secret', 'shop');
$db->set_charset('utf8mb4');Первая строка включает исключения при ошибках. Кодировка utf8mb4 нужна, чтобы правильно хранить кириллицу и эмодзи.
Простой запрос
Метод query() выполняет запрос без параметров. Строки результата читают методом fetch_assoc():
<?php
$db = new mysqli('localhost', 'root', 'secret', 'shop');
$result = $db->query('SELECT id, title FROM products ORDER BY id');
while ($row = $result->fetch_assoc()) {
echo $row['id'], ': ', $row['title'], "\n";
}fetch_all(MYSQLI_ASSOC) возвращает сразу все строки массивом.
Подготовленные запросы
Данные от пользователя передают только через подготовленный запрос. В MySQLi у значений указывают типы: s — строка, i — целое число, d — дробное:
<?php
$db = new mysqli('localhost', 'root', 'secret', 'shop');
$statement = $db->prepare('SELECT title FROM products WHERE price < ? AND category = ?');
$statement->bind_param('is', $maxPrice, $category);
$maxPrice = 300;
$category = 'drinks';
$statement->execute();
$rows = $statement->get_result()->fetch_all(MYSQLI_ASSOC);Начиная с PHP 8.1 значения можно передать прямо в execute(), как в PDO: $statement->execute([300, 'drinks']).
Добавление записи
<?php
$db = new mysqli('localhost', 'root', 'secret', 'shop');
$statement = $db->prepare('INSERT INTO products (title, price) VALUES (?, ?)');
$statement->execute(['Чай', 250]);
echo $db->insert_id; // идентификатор новой строки
echo $db->affected_rows; // число затронутых строкПроцедурный стиль
У каждого метода MySQLi есть двойник-функция. Этот стиль чаще всего встречается в старом коде:
<?php
$db = mysqli_connect('localhost', 'root', 'secret', 'shop');
$result = mysqli_query($db, 'SELECT title FROM products');
while ($row = mysqli_fetch_assoc($result)) {
echo $row['title'], "\n";
}
mysqli_close($db);Оба стиля делают одно и то же; в новом коде используют объектный.
MySQLi или PDO
| MySQLi | PDO | |
|---|---|---|
| Базы данных | Только MySQL и MariaDB | MySQL, PostgreSQL, SQLite и другие |
Именованные параметры :name | Нет | Да |
| Процедурный стиль | Да | Нет |
| Возможности, специфичные для MySQL | Больше | Меньше |
Для нового проекта чаще выбирают PDO: код не привязан к одной базе, а параметры удобнее. MySQLi уместен, если проект уже написан на нём или нужны особые возможности MySQL.
Частая ошибка
Самая опасная привычка из старого кода — вставлять переменные прямо в текст запроса. Это открывает дорогу SQL-инъекции:
<?php
$db = new mysqli('localhost', 'root', 'secret', 'shop');
$name = $_GET['name'] ?? '';
// Опасно: $db->query("SELECT * FROM users WHERE name = '$name'");
$statement = $db->prepare('SELECT * FROM users WHERE name = ?');
$statement->execute([$name]);Функция mysqli_real_escape_string(), которой раньше «обезвреживали» значения, защищает не во всех случаях. Используйте подготовленные запросы.