PDO и MySQLi: сравнительный обзор технологий PHP
PDO и MySQLi: два способа работы с базами данных в PHP
В PHP существуют два основных расширения для взаимодействия с MySQL: PDO (PHP Data Objects) и MySQLi (MySQL Improved). Оба поддерживают подготовленные выражения и транзакции, но имеют различия в синтаксисе, производительности и переносимости. Ниже рассмотрены основные варианты использования.
Как организовать универсальный доступ к базам данных с помощью PDO?
PDO (PHP Data Objects) - основное решение
PDO предоставляет единый интерфейс для различных СУБД. Для MySQL необходим драйвер pdo_mysql. Подключение с настройками ошибок и режима выборки:
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$user = 'root';
$password = '';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
try {
$pdo = new PDO($dsn, $user, $password, $options);
echo 'Подключение успешно';
} catch (PDOException $e) {
echo 'Ошибка подключения: ' . $e->getMessage();
}Php pdo mysqli (pdo и mysqli в php)
Подготовленные выражения с именованными параметрами защищают от SQL-инъекций:
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute([':email' => 'user@example.com']);
$user = $stmt->fetch();
print_r($user);Типичные ошибки
- Ошибка "could not find driver" - отсутствует расширение pdo_mysql. Решение: установить php-pdo-mysql.
- Исключение Connection refused при неверных параметрах подключения. Проверка хоста и порта MySQL.
- Ошибка синтаксиса при указании параметров (лишнее двоеточие в execute). Следует передавать массив без двоеточия для именованных, или с двоеточием как ключи.
Как выполнить работу с MySQL через MySQLi в объектном стиле?
MySQLi (объектный интерфейс)
$mysqli = new mysqli('localhost', 'root', '', 'testdb');
if ($mysqli->connect_error) {
die('Ошибка подключения: ' . $mysqli->connect_error);
}
$result = $mysqli->query('SELECT * FROM users');
while ($row = $result->fetch_assoc()) {
echo $row['name'] . '<br>';
}
$mysqli->close();Подготовленные выражения с позиционными параметрами:
$stmt = $mysqli->prepare('SELECT * FROM users WHERE email = ?');
$stmt->bind_param('s', $email);
$email = 'user@example.com';
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
print_r($user);Типичные ошибки
- Ошибка "Class 'mysqli' not found" при отсутствии расширения mysqli.
- Предупреждение о неверном типе данных в bind_param (например, передача строки вместо целого).
- Забытая проверка connect_error может привести к фатальной ошибке при неудачном подключении.
Как использовать процедурный стиль MySQLi?
MySQLi процедурный стиль
$link = mysqli_connect('localhost', 'root', '', 'testdb');
if (!$link) { die('Ошибка: ' . mysqli_connect_error()); }
mysqli_set_charset($link, 'utf8mb4');
$res = mysqli_query($link, 'SELECT id, name FROM users');
while ($row = mysqli_fetch_assoc($res)) {
echo $row['id'] . ' - ' . $row['name'] . '<br>';
}
mysqli_close($link);Подготовленные выражения в процедурном стиле:
$stmt = mysqli_prepare($link, 'SELECT * FROM users WHERE email = ?');
mysqli_stmt_bind_param($stmt, 's', $email);
$email = 'user@example.com';
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$user = mysqli_fetch_assoc($result);
print_r($user);Типичные ошибки
- Использование устаревших функций mysql_* (удалены в PHP 7). Следует применять mysqli_*.
- Путаница между mysqli_query (буферизованный запрос) и mysqli_real_query (небуферизованный).
- Отсутствие вызова mysqli_stmt_close может привести к утечке памяти.
Расширенные примеры использования PDO и MySQLi
Транзакции в PDO
$pdo->beginTransaction();
try {
$pdo->exec('INSERT INTO accounts (user, balance) VALUES ('Alice', 1000)');
$pdo->exec('UPDATE accounts SET balance = balance - 100 WHERE user = 'Bob'');
$pdo->commit();
echo 'Транзакция выполнена успешно';
} catch (Exception $e) {
$pdo->rollBack();
echo 'Ошибка: ' . $e->getMessage();
}Транзакция выполнена успешно (если нет ошибок)
Массовая вставка с PDO
$data = [['Alice', 20], ['Bob', 30], ['Carol', 25]];
$stmt = $pdo->prepare('INSERT INTO users (name, age) VALUES (:name, :age)');
foreach ($data as $row) {
$stmt->execute([':name' => $row[0], ':age' => $row[1]]);
}
echo 'Вставлено строк: ' . count($data);Вставлено строк: 3
Fetch-режимы PDO (FETCH_CLASS)
class User {
public $id;
public $name;
}
$stmt = $pdo->query('SELECT id, name FROM users');
$users = $stmt->fetchAll(PDO::FETCH_CLASS, 'User');
foreach ($users as $u) {
echo $u->id . ' ' . $u->name . '<br>';
}1 Alice 2 Bob
Хранимая процедура с OUT параметрами в MySQLi
$mysqli->query('CREATE PROCEDURE GetUserCount(OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM users; END');
$stmt = $mysqli->prepare('CALL GetUserCount(?)');
$stmt->bind_param('i', $cnt);
$stmt->execute();
$stmt->bind_result($cnt);
$stmt->fetch();
echo 'Количество пользователей: ' . $cnt;Количество пользователей: 10
Работа с BLOB в PDO
$binaryData = file_get_contents('image.png');
$stmt = $pdo->prepare('INSERT INTO files (name, data) VALUES (:name, :data)');
$stmt->bindParam(':name', $fileName);
$stmt->bindParam(':data', $binaryData, PDO::PARAM_LOB);
$fileName = 'image.png';
$stmt->execute();(запись выполнена, результат не выводится)
Небуферизованный запрос в MySQLi для больших данных
$mysqli->real_query('SELECT * FROM large_table');
$result = $mysqli->use_result();
while ($row = $result->fetch_assoc()) {
// обработка без загрузки всего результата в память
}
$result->free();(последовательная обработка)
Каждый пример иллюстрирует конкретный сценарий. Выбор между PDO и MySQLi зависит от требований к переносимости и производительности.