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 зависит от требований к переносимости и производительности.

PDO и mysqli в PHP - comments

En
Php pdo mysqli (php)