MySQL через PDO
Безопасные запросы без склейки строк
Подключение
PDO — единый интерфейс к разным базам. Подключение делается один раз и переиспользуется:
$dsn = 'mysql:host=localhost;dbname=blog;charset=utf8mb4';
$pdo = new PDO($dsn, 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
ERRMODE_EXCEPTION — главное, что нужно включить: без него ошибки базы молча возвращают false, и вы часами ищете, почему данные не сохраняются.
Почему не конкатенация
Такой код опасен: если в $id придёт 1 OR 1=1, вернутся все записи, а при ; DROP TABLE — всё хуже.
// ТАК НЕЛЬЗЯ
$pdo->query("SELECT * FROM posts WHERE id = $id");
Подготовленный запрос отделяет структуру от данных: значение приходит как значение, а не как часть SQL.
SELECT с параметрами
$stmt = $pdo->prepare("SELECT * FROM posts WHERE id = ?");
$stmt->execute([$id]);
$post = $stmt->fetch();
if (!$post) { http_response_code(404); exit; }
Именованные параметры читаются лучше, когда их несколько:
$stmt = $pdo->prepare("SELECT * FROM posts WHERE status = :status AND year = :year");
$stmt->execute(['status' => 'published', 'year' => 2026]);
Список строк
$stmt = $pdo->query("SELECT id, title FROM posts ORDER BY created_at DESC");
$posts = $stmt->fetchAll();
foreach ($posts as $post) {
echo htmlspecialchars($post['title']);
}
query() допустим только для запросов без пользовательских данных. Всё, что пришло из формы или адреса, идёт через prepare.
INSERT и обновление
$stmt = $pdo->prepare(
"INSERT INTO posts (title, body) VALUES (?, ?)"
);
$stmt->execute([$title, $body]);
$newId = $pdo->lastInsertId();
Для массовой вставки не делайте execute в цикле — оберните всё в транзакцию через beginTransaction() и commit(), иначе база получит сотни отдельных записей.
Обработка ошибок
try {
$stmt->execute([$id]);
} catch (PDOException $e) {
error_log($e->getMessage());
http_response_code(500);
echo "Ошибка сервера";
}
Текст исключения пишите в лог, а пользователю показывайте общую фразу. Подробности ошибки reveal структуру базы.
Частые ошибки
- Конкатенация переменных в SQL. SQL-инъекция в чистом виде.
- Выключенные исключения. Ошибка не видна, данные не сохраняются.
- Вывод из базы без экранирования. XSS через поле заголовка.
- Соединение в каждом файле. Сотни подключений вместо одного.
Нужен сайт, а не статья?
Спроектирую базу и напишу слой доступа без инъекций и дублирования. С 2009 года, по договору, с поддержкой после запуска.
Полезно по теме: PHP с нуля, формы на PHP, ORM в Django.