MySQL и PHP: пошаговое руководство по соединению и выполнению запросов

Раздел: Разработка на PHP -> Работа с базами данных

Обзор способов работы с MySQL в PHP

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

Как подключиться к MySQL с помощью PDO и выполнить запрос?

PDO (PHP Data Objects) предоставляет универсальный интерфейс для работы с базами данных. Чтобы подключиться, создается экземпляр класса PDO с DSN, именем пользователя и паролем. Рассмотрим пример.


$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$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);
    echo "Подключение успешно";
} catch (PDOException $e) {
    echo "Ошибка подключения: " . $e->getMessage();
}

Здесь задается режим ошибок исключения, что упрощает отладку. После подключения можно выполнять запросы:


$stmt = $pdo->query('SELECT * FROM users');
while ($row = $stmt->fetch()) {
    print_r($row);
}

Типичные ошибки: неправильное имя хоста или базы данных, неверные учетные данные, отсутствие расширения PDO MySQL (php_pdo_mysql). Рекомендуется проверять файл php.ini и подключение к серверу.

Как использовать расширение mysqli для подключения к MySQL?

Mysqli (MySQL Improved) доступно в процедурном и объектно-ориентированном стиле. Рассмотрим ООП вариант.


$mysqli = new mysqli('localhost', 'root', '', 'testdb');
if ($mysqli->connect_error) {
    die('Ошибка подключения: ' . $mysqli->connect_error);
}
echo "Подключение успешно";
// Выполнение запроса
$result = $mysqli->query('SELECT * FROM users');
while ($row = $result->fetch_assoc()) {
    print_r($row);
}
$mysqli->close();

Проблемы: если не вызвать close(), соединение останется открытым. Также mysqli не поддерживает PDO-стиль исключений по умолчанию, но можно включить режим исключений через mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

Как выполнить подготовленный запрос для защиты от SQL-инъекций?

Подготовленные запросы - основной способ предотвращения инъекций. В PDO это делается через метод prepare и execute.


$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (:name, :email)');
$stmt->execute([':name' => 'Иван', ':email' => 'ivan@example.com']);
echo "Запись добавлена";

В mysqli метод аналогичен:


$stmt = $mysqli->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
$stmt->bind_param('ss', $name, $email);
$name = 'Иван';
$email = 'ivan@example.com';
$stmt->execute();
echo "Запись добавлена";

Ошибки: неправильное количество плейсхолдеров, несоответствие типов при bind_param. В PDO легче с типами, так как они автоматические.

Как обработать ошибки базы данных в PHP?

Рекомендуется использовать исключения PDO. Для mysqli можно установить режим отчетов.


try {
    $pdo = new PDO(...);
    $pdo->exec('INVALID SQL');
} catch (PDOException $e) {
    error_log($e->getMessage());
    echo "Произошла ошибка базы данных";
}

Проблема: без установки режима исключений PDO по умолчанию не бросает исключения, а возвращает false. Это может привести к пропущенным ошибкам.

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

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


$pdo->beginTransaction();
try {
    $pdo->exec('UPDATE accounts SET balance = balance - 100 WHERE id = 1');
    $pdo->exec('UPDATE accounts SET balance = balance + 100 WHERE id = 2');
    $pdo->commit();
    echo "Перевод выполнен";
} catch (Exception $e) {
    $pdo->rollBack();
    echo "Ошибка, откат: " . $e->getMessage();
}

Проблемы: если в коде не перехвачены исключения, транзакция может остаться незавершенной. Рекомендуется всегда закрывать транзакцию в блоке finally.

Цели и случаи использования каждого варианта: PDO подходит для новых проектов, требующих абстракции от СУБД и поддержки разных баз. Mysqli - для существующих проектов или если нужна максимальная производительность без дополнительных слоев. Старое расширение mysql следует применять только в legacy-коде.

Расширенные примеры работы с MySQL в PHP

Пакетная вставка с использованием подготовленного запроса

Многократное выполнение одного подготовленного запроса с разными параметрами эффективнее, чем выполнение множества отдельных запросов. Пример вставки нескольких записей в таблицу логов.

Пример

$stmt = $pdo->prepare('INSERT INTO logs (message, date) VALUES (:msg, :date)');
$logs = [
    ['msg' => 'Login attempt', 'date' => '2024-01-01 10:00:00'],
    ['msg' => 'Logout', 'date' => '2024-01-01 11:00:00'],
];
foreach ($logs as $log) {
    $stmt->execute([':msg' => $log['msg'], ':date' => $log['date']]);
}
echo "Добавлено " . count($logs) . " записей";
Добавлено 2 записи

Извлечение данных в виде объектов пользовательского класса

Использование FETCH_CLASS позволяет автоматически заполнять свойства объекта данными из строки результата. Удобно для работы с ORM-подобными структурами.

Пример

class User {
    public $id;
    public $name;
}
$stmt = $pdo->query('SELECT id, name FROM users LIMIT 3');
$users = $stmt->fetchAll(PDO::FETCH_CLASS, 'User');
foreach ($users as $u) {
    echo $u->id . ': ' . $u->name . "\n";
}
1: Иван
2: Петр
3: Сергей

Использование транзакции с гарантированным закрытием

В реальных приложениях важно всегда завершать транзакцию, даже при возникновении непредвиденных ошибок. Блок finally обеспечивает откат, если commit не был вызван.

Пример

$pdo->beginTransaction();
try {
    $pdo->exec('UPDATE accounts SET balance = balance - 100 WHERE id = 1');
    if (someCondition()) {
        throw new Exception('Произошла ошибка');
    }
    $pdo->exec('UPDATE accounts SET balance = balance + 100 WHERE id = 2');
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    echo "Откат из-за: " . $e->getMessage();
} finally {
    // можно выполнить очистку
}
Откат из-за: Произошла ошибка

Получение значений одного столбца из всех строк

Для извлечения списка значений (например, всех имен) удобен режим FETCH_COLUMN. Результат - простой одномерный массив.

Пример

$stmt = $pdo->query('SELECT name FROM users WHERE active = 1');
$names = $stmt->fetchAll(PDO::FETCH_COLUMN);
print_r($names);
Array
(
    [0] => Иван
    [1] => Петр
    [2] => Сергей
)

Вызов хранимой процедуры с выходными параметрами

PDO поддерживает вызов хранимых процедур. В данном примере процедура возвращает одну строку по идентификатору.

Пример

$stmt = $pdo->prepare('CALL get_user_by_id(:id)');
$stmt->execute([':id' => 2]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($user);
Array
(
    [id] => 2
    [name] => Петр
    [email] => petr@example.com
)

Эти примеры охватывают ситуации, выходящие за рамки простого SELECT: массовая вставка, работа с объектами, безопасные транзакции, получение отдельных колонок и вызов процедур. Каждый из них демонстрирует возможности PDO для построения надежного и производительного взаимодействия с базой данных.

Работа с MySQL в PHP - comments

En
файл mysql php (php)