Выполнение запросов к базам данных в PHP: PDO против MySQLi

Раздел: PHP программирование -> Базы данных в PHP

Основные подходы к выполнению запросов в PHP

Взаимодействие с базами данных - ключевая задача веб-разработки. В PHP существуют два основных расширения: MySQLi и PDO. Оба поддерживают подготовленные запросы, однако PDO считается более гибким, так как работает с разными СУБД. Далее рассмотрено основное решение с использованием PDO.

Рекомендуемый способ: PDO с подготовленными запросами

Как безопасно выполнить запрос к базе данных с помощью PDO?

Для начала необходимо установить соединение с базой данных через DSN, указав имя хоста, имя базы и кодировку. Затем создаётся объект PDO. Для выполнения запросов с параметрами следует использовать метод prepare и последующий вызов execute. Это защищает от SQL-инъекций.

$dsn = 'mysql:host=localhost;dbname=test;charset=utf8mb4';
$user = 'root';
$pass = '';
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
try {
    $pdo = new PDO($dsn, $user, $pass, $options);
    $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
    $stmt->execute(['email' => 'test@example.com']);
    $user = $stmt->fetch();
} catch (PDOException $e) {
    echo 'Ошибка: ' . $e->getMessage();
}

Php бд mysql (база данных mysql в php)

В этом примере используется именованный плейсхолдер :email. Можно также применять позиционные параметры (знаки вопроса).

Типичные проблемы:

  • Неверный DSN - ошибка «could not find driver» или «connection refused». Решение - проверить наличие драйвера pdo_mysql и правильность хоста.
  • Отсутствие обработки исключений - если не установлен режим исключений, ошибки игнорируются. Рекомендуется всегда включать ERRMODE_EXCEPTION.
  • Использование устаревшего query с прямым встраиванием переменных - приводит к SQL injection. Выход - только подготовленные запросы.

Как выполнить запрос с помощью MySQLi в процедурном стиле?

Процедурный стиль MySQLi предполагает использование функций mysqli_connect, mysqli_query, mysqli_fetch_assoc и т.д. Этот способ прост для понимания, но требует ручного освобождения ресурсов и не поддерживает исключения по умолчанию.

$link = mysqli_connect('localhost', 'root', '', 'test');
if (!$link) {
    die('Ошибка подключения: ' . mysqli_connect_error());
}
$result = mysqli_query($link, 'SELECT * FROM users WHERE id = ' . (int)$_GET['id']);
while ($row = mysqli_fetch_assoc($result)) {
    echo $row['name'];
}
mysqli_free_result($result);
mysqli_close($link);

Php база данных пользователей (база данных пользователей в php)

Распространённые ошибки:

  • SQL-инъекция при встраивании переменных без экранирования. Приведённый код использует приведение к int, но для строк требуется mysqli_real_escape_string.
  • Утечка памяти, если не вызвать mysqli_free_result.
  • Отсутствие проверки ошибок после mysqli_query - может привести к неожиданному поведению.

Как использовать объектно-ориентированный MySQLi для запросов?

Объектно-ориентированный интерфейс MySQLi предоставляет классы mysqli, mysqli_stmt и mysqli_result. Подготовленные запросы реализуются через метод prepare, что повышает безопасность.

$mysqli = new mysqli('localhost', 'root', '', 'test');
if ($mysqli->connect_error) {
    die('Ошибка: ' . $mysqli->connect_error);
}
$stmt = $mysqli->prepare('SELECT name, email FROM users WHERE status = ?');
$status = 'active';
$stmt->bind_param('s', $status);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
    echo $row['name'] . ' - ' . $row['email'];
}
$stmt->close();
$mysqli->close();

Php база данных сайт (база данных сайта на php)

Возможные трудности:

  • Необходимость указывать тип параметра в bind_param (s, i, d, b). Ошибка типа может привести к некорректной работе.
  • Неправильный порядок вызовов: сначала prepare, потом bind_param, затем execute.
  • При использовании get_result требуется драйвер mysqlnd. Без него можно использовать bind_result и fetch.

Как выполнить несколько запросов в одной транзакции?

Транзакции позволяют гарантировать, что все изменения будут применены атомарно. В PDO используется методы beginTransaction, commit и rollback.

try {
    $pdo->beginTransaction();
    $pdo->exec('UPDATE accounts SET balance = balance - 100 WHERE id = 1');
    $pdo->exec('UPDATE accounts SET balance = balance + 100 WHERE id = 2');
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollback();
    echo 'Транзакция отменена: ' . $e->getMessage();
}

Ошибки при работе с транзакциями:

  • Забыли вызвать beginTransaction - каждый запрос выполняется в авто-коммите.
  • Отсутствие обработки исключений в транзакции - при ошибке изменения могут частично сохраниться.
  • Использование exec с непроверенными данными - риск SQL injection.
- подключение базы php (подключение к базе данных в php)
- база данных таблицы php (работа с таблицами базы данных в php)
- создание базы php (создание базы данных в php)

Расширенные примеры запросов

Пример 1: Вставка записи с получением ID

Добавление нового пользователя и получение его идентификатора.

Пример
$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (:name, :email)');
$stmt->execute(['name' => 'Иван Петров', 'email' => 'ivan@example.com']);
$id = $pdo->lastInsertId();
echo 'Добавлен пользователь с ID: ' . $id;
Добавлен пользователь с ID: 42

Пример 2: Выборка с JOIN и пагинацией

Получение списка заказов с именем клиента и суммой, с постраничным выводом.

Пример
$page = 1;
$perPage = 10;
$offset = ($page - 1) * $perPage;
$stmt = $pdo->prepare('
    SELECT o.id, c.name, o.total
    FROM orders o
    JOIN customers c ON o.customer_id = c.id
    ORDER BY o.created_at DESC
    LIMIT :limit OFFSET :offset
');
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$orders = $stmt->fetchAll();
print_r($orders);
Array
(
    [0] => Array
        (
            [id] => 101
            [name] => Анна Смирнова
            [total] => 2500.00
        )
    [1] => Array
        (
            [id] => 100
            [name] => Борис Иванов
            [total] => 1200.00
        )
    ...
)

Пример 3: Использование точек сохранения в транзакции

При необходимости частичного отката можно установить точку сохранения.

Пример
$pdo->beginTransaction();
$pdo->exec('UPDATE accounts SET balance = balance - 200 WHERE id = 1');
$pdo->exec('SAVEPOINT after_debit');
$pdo->exec('UPDATE accounts SET balance = balance + 200 WHERE id = 2');
// при обнаружении ошибки откат до точки
$pdo->exec('ROLLBACK TO SAVEPOINT after_debit');
$pdo->commit(); // фиксируем только debit, credit откатился

Пример 4: Получение одного значения

Метод fetchColumn извлекает один столбец из следующей строки результата.

Пример
$stmt = $pdo->prepare('SELECT COUNT(*) FROM users WHERE status = :status');
$stmt->execute(['status' => 'active']);
$count = $stmt->fetchColumn();
echo 'Активных пользователей: ' . $count;
Активных пользователей: 152

Пример 5: Массовая вставка с prepared statement

Можно многократно выполнять один и тот же подготовленный запрос с разными данными.

Пример
$stmt = $pdo->prepare('INSERT INTO logs (message, level) VALUES (:msg, :lvl)');
$logs = [
    ['msg' => 'User login', 'lvl' => 'info'],
    ['msg' => 'Page view', 'lvl' => 'debug'],
];
foreach ($logs as $log) {
    $stmt->execute($log);
}
echo 'Вставлено ' . count($logs) . ' записей';
Вставлено 2 записей

Запросы к базе данных в PHP - comments

En
Php запросы в базу данных (php)