PHP и SQL: руководство по работе с базами данных
Основы работы PHP с SQL
Как обеспечить безопасное и гибкое взаимодействие PHP с SQL?
Наиболее эффективное решение - использование расширения PDO (PHP Data Objects). PDO предоставляет единый интерфейс для работы с разными СУБД, поддерживает подготовленные запросы и защищает от SQL-инъекций. Ниже приведен пример подключения к MySQL и выполнения запроса.
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8';
$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);
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => 'user@example.com']);
$user = $stmt->fetch();
} catch (PDOException $e) {
die('Ошибка подключения: ' . $e->getMessage());
}В коде создается DSN с указанием драйвера, хоста и базы данных. Параметры запроса передаются отдельно, что исключает возможность внедрения вредоносного кода. Обработка исключений помогает своевременно диагностировать проблемы.
Типичная ошибка: забыть указать кодировку в DSN, что приводит к ошибкам с кириллицей. Решение: добавить charset=utf8.
Как подключиться к MySQL через расширение mysqli?
Расширение mysqli предназначено исключительно для MySQL. Оно также поддерживает подготовленные запросы, но синтаксис отличается от PDO.
$mysqli = new mysqli('localhost', 'root', '', 'test');
if ($mysqli->connect_error) {
die('Ошибка подключения: ' . $mysqli->connect_error);
}
$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();В этом варианте используется объектно-ориентированный интерфейс. Подготовленный запрос требует явного связывания параметров с помощью bind_param. Ошибки подключения проверяются через свойство connect_error.
Частая проблема: путаница между процедурным и объектным стилем mysqli. Например, mysqli_connect возвращает ресурс, а не объект. Рекомендуется использовать единый стиль. Также при использовании get_result() может потребоваться установка драйвера mysqlnd.
Как абстрагироваться от конкретной СУБД с помощью ORM?
ORM (Object-Relational Mapping) упрощает работу, позволяя оперировать объектами вместо SQL. Пример с популярной библиотекой Eloquent (часть Laravel).
use illuminate\Database\Capsule\Manager as Capsule;
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'test',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
]);
$capsule->bootEloquent();
$users = User::where('email', 'user@example.com')->get();Eloquent автоматически экранирует параметры и поддерживает миграции. Однако для небольших проектов ORM может быть избыточным и добавлять лишние накладные расходы.
Ошибка: неправильная настройка соединения или отсутствие автозагрузки классов. Решение: использовать Composer и правильно подключать автозагрузчик.
Расширенные примеры взаимодействия PHP с SQL
Транзакции в PDO
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare('UPDATE accounts SET balance = balance - :amount WHERE id = :from');
$stmt->execute(['amount' => 100, 'from' => 1]);
$stmt = $pdo->prepare('UPDATE accounts SET balance = balance + :amount WHERE id = :to');
$stmt->execute(['amount' => 100, 'to' => 2]);
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
echo 'Ошибка: ' . $e->getMessage();
}Результат: баланс счета 1 уменьшен на 100, счета 2 увеличен на 100. При ошибке все изменения откатываются.
Работа с SQLite и PostgreSQL через PDO
// SQLite
$dsn = 'sqlite:/path/to/database.db';
$pdo = new PDO($dsn);
$pdo->exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)');
// PostgreSQL
$dsn = 'pgsql:host=localhost;dbname=test';
$pdo = new PDO($dsn, 'user', 'pass');
$stmt = $pdo->query('SELECT NOW()');
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo $row['now'];Результат: для SQLite создается таблица, для PostgreSQL выводится текущее время сервера.
Пагинация с использованием PDO
$page = 1;
$perPage = 10;
$offset = ($page - 1) * $perPage;
$stmt = $pdo->prepare('SELECT * FROM articles LIMIT :limit OFFSET :offset');
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$articles = $stmt->fetchAll();Результат: массив из 10 записей первой страницы.
Разные режимы выборки fetch mode
$stmt = $pdo->query('SELECT id, name FROM users');
// Ассоциативный массив
$assoc = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Объект stdClass
$objects = $stmt->fetchAll(PDO::FETCH_OBJ);
// Один столбец в массив
$names = $stmt->fetchAll(PDO::FETCH_COLUMN, 1);
// Пара ключ-значение
$pairs = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);Результат: различные форматы данных для удобной обработки.