База данных в PHP: подключение с нуля через PDO
Пошаговое подключение к MySQL из PHP через PDO: от установки драйвера до первого безопасного запроса с параметрами. Займёт около 20 минут.

К концу этого материала у вас будет рабочий PHP-скрипт, который подключается к базе MySQL, читает и записывает данные без риска SQL-инъекций. Мы используем расширение PDO — оно входит в стандартную поставку PHP и работает не только с MySQL, но и с PostgreSQL, SQLite и другими. Расчёт на PHP 8.1 и выше, MySQL 8.x или MariaDB 10.x. Времени уйдёт минут двадцать, если база уже установлена.
Что нужно иметь до начала
Проверьте это до первой строки кода — иначе застрянете на середине.
- PHP 8.1+ с включённым расширением
pdo_mysql. Проверить: выполните в терминалеphp -m | grep pdo. В выводе должны бытьPDOиpdo_mysql. Если их нет — включите вphp.iniстрокуextension=pdo_mysqlи перезапустите PHP. - Работающий сервер MySQL или MariaDB и данные для входа: хост, порт (по умолчанию 3306), имя пользователя и пароль.
- Созданная база данных. Если её нет, создайте:
CREATE DATABASE shop CHARACTER SET utf8mb4; - Доступ к терминалу или веб-серверу, чтобы запустить скрипт.
Дальше по шагам. Каждый шаг заканчивается проверкой — не переходите к следующему, пока предыдущий не отработал.
Шаг 1. Подготовьте таблицу
Чтобы было с чем работать, создайте простую таблицу. Выполните в консоли MySQL или в phpMyAdmin:
USE shop;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Проверка: команда SHOW TABLES; должна вернуть строку users.
Шаг 2. Подключитесь к базе
Создайте файл db.php. В нём — строка подключения (DSN) и создание объекта PDO. Обратите внимание на параметр charset=utf8mb4: без него русский текст может сохраниться кракозябрами.
<?php
// db.php — PHP 8.1+, PDO для MySQL
$host = '127.0.0.1';
$port = 3306;
$dbname = 'shop';
$user = 'root';
$pass = 'YOUR_PASSWORD'; // заглушка, подставьте свой пароль
$dsn = "mysql:host=$host;port=$port;dbname=$dbname;charset=utf8mb4";
$options = [
// при ошибке кидать исключение, а не молча возвращать false
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// строки вместо ассоциативных массивов по умолчанию
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
// настоящие подготовленные запросы, не эмуляция
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (PDOException $e) {
// в проде не выводите текст ошибки пользователю — только логируйте
die('Ошибка подключения: ' . $e->getMessage());
}
Три опции в массиве важнее, чем кажется. ERRMODE_EXCEPTION заставляет PDO бросать исключение при любой ошибке — иначе вы будете ловить тихие false и гадать, что пошло не так. ATTR_EMULATE_PREPARES => false включает настоящие подготовленные запросы на стороне сервера, а не подстановку в строку внутри PHP.
Проверка: запустите php db.php в терминале. Если ничего не вывелось — подключение прошло. Если увидели «Ошибка подключения» — проверьте хост, пароль и что сервер MySQL запущен.
Шаг 3. Вставьте запись безопасно
Самая частая ошибка новичка — склеивать SQL со значениями через точку. Так делать нельзя: это открывает дверь для SQL-инъекций. Используйте плейсхолдеры.
<?php
require 'db.php';
$name = 'Иван Петров';
$email = 'ivan@example.com';
$stmt = $pdo->prepare(
'INSERT INTO users (name, email) VALUES (:name, :email)'
);
$stmt->execute([
':name' => $name,
':email' => $email,
]);
echo 'Добавлена запись с id ' . $pdo->lastInsertId();
Метод prepare() отправляет шаблон запроса на сервер, а execute() передаёт значения отдельно. Даже если в $email окажется строка с кавычками и DROP TABLE, она попадёт в базу как обычный текст, а не выполнится.
Проверка: в консоли MySQL выполните SELECT * FROM users; — должна появиться строка с Иваном и непустым id.
Правило одно и без исключений: любое значение, которое приходит извне — из формы, из URL, из другого API — попадает в запрос только через плейсхолдер. Ни разу не через конкатенацию строк.
Шаг 4. Прочитайте данные
Чтение работает так же: подготовили, выполнили, забрали результат. Для одной строки — fetch(), для всех — fetchAll().
<?php
require 'db.php';
// одна запись по email
$stmt = $pdo->prepare('SELECT id, name, email FROM users WHERE email = :email');
$stmt->execute([':email' => 'ivan@example.com']);
$user = $stmt->fetch(); // ассоциативный массив или false
if ($user) {
echo $user['name'] . ' — ' . $user['email'];
}
// все записи
$all = $pdo->query('SELECT id, name FROM users ORDER BY id DESC')->fetchAll();
foreach ($all as $row) {
echo $row['id'] . ': ' . $row['name'] . PHP_EOL;
}
Заметьте: для запроса SELECT ... WHERE email = :email мы снова используем prepare, потому что подставляем значение. А для запроса без параметров (SELECT id, name FROM users ORDER BY id) можно вызвать query() напрямую — там подставлять нечего.
Проверка: запустите скрипт. Должно вывестись имя Ивана и список всех записей.
Плейсхолдеры: именованные и позиционные
PDO поддерживает два стиля. Разница только в читаемости, работают оба одинаково.
| Стиль | Пример шаблона | Передача значений |
|---|---|---|
| Именованные | WHERE email = :email | [':email' => $email] |
| Позиционные | WHERE email = ? | [$email] |
На старте берите именованные: когда параметров пять, по именам понятнее, что куда идёт. Смешивать оба стиля в одном запросе нельзя — PDO выдаст ошибку.
Если не получилось
could not find driver— не включено расширениеpdo_mysql. Проверьтеphp -mи раскомментируйтеextension=pdo_mysqlвphp.ini, затем перезапустите PHP или веб-сервер.SQLSTATE[HY000] [2002] Connection refused— сервер MySQL не запущен или указан неверный хост/порт. Проверьте, что база работает, и попробуйте127.0.0.1вместоlocalhost: они не всегда эквивалентны из-за сокетов.SQLSTATE[HY000] [1045] Access denied— неверный логин или пароль. Проверьте$userи$pass.SQLSTATE[42S02] Base table or view not found— таблицыusersнет или вы в другой базе. Вернитесь к шагу 1.- Русский текст сохраняется как
иван— потерялиcharset=utf8mb4в DSN или таблица создана не вutf8mb4. Пересоздайте таблицу и добавьте charset в строку подключения.
Что делать дальше
Базовое подключение готово. Три следующих шага, когда освоитесь:
- Вынесите пароль из кода в переменные окружения или отдельный файл конфигурации вне корня сайта — держать пароль в
db.phpпод веб-корнем опасно. - Разберитесь с транзакциями:
$pdo->beginTransaction(),commit(),rollBack()— когда несколько запросов должны выполниться либо все, либо ни один. - Настройте логирование ошибок вместо вывода
getMessage()на экран: в проде текст ошибки пользователю показывать нельзя.
Транзакции и пул соединений — отдельные темы, здесь мы разобрали только первое рабочее подключение и безопасный запрос.
Инфографика для маркетплейса: Три главных преимущества товара короткими фразами, выноски в безопасной зоне превью, точный размер площадки.
Спросить за 60 ₽Источники: PHP Manual: PDO, PHP Manual: PDO::prepare
Частые вопросы
А если у меня PHP 7.4, всё это сработает?
Чем PDO лучше mysqli?
Скрипт подключается, но fetch() возвращает false — что не так?
Нужно ли закрывать соединение вручную?
Безопасно ли хранить пароль от базы прямо в db.php?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.