PHP MySQLi
Расширение PHP MySQLi: подключение к MySQL, безопасные запросы с подготовленными выражениями, обработка ошибок и сравнение MySQLi с PDO.
MySQLi («MySQL Improved») — это расширение PHP для подключения к базе данных MySQL или MariaDB, выполнения запросов и чтения результатов. В этой статье рассказывается, что представляет собой расширение, как устанавливать соединение, как безопасно выполнять запросы SELECT/INSERT/UPDATE и когда выбирать MySQLi, а не PDO.
Что такое расширение MySQLi?
Расширение MySQLi пришло на смену устаревшим (и ныне удалённым) функциям mysql_*. Оно добавляет возможности, которых в оригинальном расширении не было: подготовленные выражения, транзакции, множественные запросы и полноценную поддержку аутентификации MySQL 4.1+. Расширение поставляется в комплекте с PHP и по умолчанию включено на большинстве установок.
MySQLi предоставляет одну и ту же функциональность через два интерфейса:
- Объектно-ориентированный — вы работаете с объектом
mysqliи вызываете методы наподобие$mysqli->query(). Именно этот стиль используется в большинстве современного кода и в данной статье. - Процедурный — вы вызываете функции вроде
mysqli_connect()иmysqli_query()и передаёте соединение в качестве первого аргумента.
Оба варианта делают абсолютно одно и то же; выберите один и придерживайтесь его. Объектно-ориентированная форма короче и читается естественнее.
Подключение к базе данных
Соединение представлено объектом mysqli. Вы создаёте его, передавая хост, имя пользователя, пароль и имя базы данных конструктору, а затем проверяете успешность подключения перед выполнением каких-либо действий:
<?php
$mysqli = new mysqli("localhost", "username", "password", "my_database");
// Always check the connection before using it.
if ($mysqli->connect_error) {
die("Failed to connect to MySQL: " . $mysqli->connect_error);
}
// Recommended: use UTF-8 so accented characters and emoji are stored correctly.
$mysqli->set_charset("utf8mb4");
echo "Connected successfully";
?>Свойство connect_error содержит понятное человеку сообщение в случае неудачного подключения (или null при успехе). Вызов set_charset() сразу после подключения помогает избежать скрытых проблем с кодировкой в дальнейшем. Подробнее об открытии соединения читайте в PHP mysqli_connect() и справочнике connect().
Выполнение запроса SELECT
После подключения метод query() выполняет SQL и возвращает объект результата, по которому можно итерироваться. fetch_assoc() возвращает одну строку за раз в виде ассоциативного массива или null, когда строк больше нет:
<?php
$result = $mysqli->query("SELECT name, email FROM users");
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
echo "Name: " . $row["name"] . " - Email: " . $row["email"] . "<br>";
}
} else {
echo "No users found.";
}
$result->free(); // release the result set
$mysqli->close(); // close the connection when you are done
?>num_rows показывает количество возвращённых строк, чтобы вы могли вывести понятное сообщение, если запрос не вернул результатов. Больше примеров запросов смотрите в PHP MySQL Select Data.
Подготовленные выражения (безопасный способ работы с вводом)
Никогда не вставляйте пользовательский ввод прямо в строку запроса — именно так происходит SQL-инъекция. Подготовленное выражение передаёт SQL и данные отдельно, поэтому база данных воспринимает ввод строго как значение, а не как исполняемый SQL.
Вы записываете ?-заполнители, prepare() -ите выражение, затем привязываете реальные значения через bind_param():
<?php
$age = 18;
$stmt = $mysqli->prepare("SELECT name, email FROM users WHERE age > ?");
$stmt->bind_param("i", $age); // "i" = integer (s = string, d = double, b = blob)
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
echo "Name: " . $row["name"] . " - Email: " . $row["email"] . "<br>";
}
$stmt->close();
?>Первый аргумент bind_param() — это строка типов: один символ на каждый заполнитель — i (integer), s (string), d (double/float) или b (blob). Остальные аргументы — это значения в том же порядке.
Примечание:
get_result()требует драйвераmysqlnd, который используется по умолчанию в современных версиях PHP. Если в вашей сборке его нет, используйтеbind_result()для привязки столбцов к переменным.
Подробное руководство смотрите в PHP MySQL Prepared Statements.
Вставка и обновление данных
Тот же шаблон с подготовленными выражениями работает и для записи. После INSERT автоинкрементный идентификатор новой строки доступен через $mysqli->insert_id; после INSERT/UPDATE/DELETE свойство $mysqli->affected_rows показывает количество изменённых строк:
<?php
$name = "Ada Lovelace";
$email = "[email protected]";
$stmt = $mysqli->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->bind_param("ss", $name, $email); // two strings
$stmt->execute();
echo "Inserted user #" . $mysqli->insert_id;
$stmt->close();
?>Полную картину операций CRUD смотрите в PHP MySQL Insert Data и PHP MySQL Update Data.
Обработка ошибок
Если запрос завершается неудачей, query() возвращает false, а подробности доступны на объекте соединения. Чтение $mysqli->error после неудачного вызова позволяет узнать, что пошло не так:
<?php
if (!$mysqli->query("SELECT * FROM no_such_table")) {
echo "Query failed (" . $mysqli->errno . "): " . $mysqli->error;
}
?>В процессе разработки можно заставить MySQLi автоматически выбрасывать исключения с помощью mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) — это поведение по умолчанию начиная с PHP 8.1. Дополнительно: PHP connect_error и PHP connect_errno.
MySQLi vs. PDO — что выбрать?
Оба варианта являются безопасными и современными способами работы с базой данных. Коротко о главном:
- MySQLi работает только с MySQL/MariaDB. Он предоставляет некоторые специфичные для MySQL возможности (например, асинхронные запросы), которых нет в PDO.
- PDO (PHP Data Objects) поддерживает десятки баз данных через единый API, поэтому переход с MySQL на PostgreSQL означает лишь смену строки подключения, а не переработку запросов. Он также поддерживает именованные заполнители (
:name), которые легче читать.
Если вы точно знаете, что будете работать только с MySQL, MySQLi вполне подойдёт. Если важна переносимость, предпочтите PDO. В любом случае всегда используйте подготовленные выражения.
Резюме
- MySQLi — стандартное расширение для работы с MySQL/MariaDB в PHP, доступное как в объектно-ориентированном, так и в процедурном стиле.
- Создайте соединение с помощью
new mysqli(...), проверьтеconnect_errorи задайте кодировку. - Используйте
query()+fetch_assoc()для чтения данных и перебора результатов. - Используйте подготовленные выражения (
prepare()→bind_param()→execute()) для всего, что связано с пользовательским вводом, чтобы защититься от SQL-инъекций. - Проверяйте
$mysqli->error/$mysqli->errnoпри сбое запроса и предпочитайте PDO, если требуется переносимость между СУБД.