🧱 Суть

PDO (PHP Data Objects) – это встроенный в PHP интерфейс для работы с базами данных.

Он позволяет:

  • подключаться к базе данных
  • выполнять SQL-запросы
  • получать результаты
  • отправлять параметры безопасным способом

Схема работы:

PHP-приложение
        |
        | PDO

   База данных
        |

 MySQL / PostgreSQL / SQLite

PDO работает через драйверы, поэтому один и тот же код можно использовать с разными СУБД.

Пример:

new PDO(
    "mysql:host=localhost;dbname=test",
    "root",
    "password"
);

📦 Проблема

1. Нельзя вставлять данные пользователя напрямую в SQL

Плохо:

$id = $_GET['id'];
 
$sql = "
SELECT *
FROM users
WHERE id = $id
";

Если пользователь отправит:

?id=1 OR 1=1

Получится:

SELECT *
FROM users
WHERE id = 1 OR 1=1

Это SQL-инъекция.

2. SQL-запросы могут содержать повторяющийся код

Без отдельного слоя работы с БД появляются:

  • дублирование подключения
  • повторение запросов
  • сложное изменение базы данных

3. Нужно правильно обрабатывать ошибки

Ошибки базы данных могут содержать:

  • структуру таблиц
  • SQL-запросы
  • внутреннюю информацию
    Их нельзя просто показывать пользователю.

✅ Решение

Использовать:

  • PDO для подключения
  • подготовленные запросы (prepare)
  • параметры (bind)
  • обработку исключений

💻 Код

Подключение к базе данных

Создание подключения:

$pdo = new PDO(
    'mysql:host=localhost;dbname=test;charset=utf8mb4',
    'root',
    'password'
);

Параметры:

ЧастьЗначение
mysqlдрайвер БД
hostсервер базы
dbnameимя базы
charsetкодировка

Настройка PDO

Обычно сразу включают режим исключений:

$pdo = new PDO(
    'mysql:host=localhost;dbname=test;charset=utf8mb4',
    'root',
    'password',
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    ]
);

Теперь ошибки будут выбрасываться как:

PDOException

SELECT-запросы

Пример:

SELECT *
FROM users;

В PHP:

$stmt = $pdo->query(
    'SELECT * FROM users'
);
 
$users = $stmt->fetchAll();

Результат:

[
    [
        'id' => 1,
        'name' => 'Alex'
    ]
]

Получение одной записи

$stmt = $pdo->query(
    'SELECT * FROM users LIMIT 1'
);
 
$user = $stmt->fetch();

Типы получения данных

Ассоциативный массив

$user = $stmt->fetch(
    PDO::FETCH_ASSOC
);

Результат:

[
    'id' => 1,
    'name' => 'Alex'
]

Объект

$user = $stmt->fetch(
    PDO::FETCH_OBJ
);

Результат:

$user->name;

Подготовленные запросы

Главное преимущество PDO.

Вместо:

$sql = "
SELECT *
FROM users
WHERE id = $id
";

Используем:

$stmt = $pdo->prepare(
    'SELECT * FROM users WHERE id = ?'
);
 
$stmt->execute([$id]);

Теперь значение передаётся отдельно от SQL.

Именованные параметры

Более читаемый вариант:

$stmt = $pdo->prepare(
    '
    SELECT *
    FROM users
    WHERE email = :email
    '
);
 
$stmt->execute([
    'email' => $email
]);

Плюсы:

  • безопаснее
  • легче читать
  • меньше ошибок

INSERT

Добавление записи:

INSERT INTO users(name)
VALUES ('Alex');

Через PDO:

$stmt = $pdo->prepare(
    '
    INSERT INTO users(name)
    VALUES (:name)
    '
);
 
$stmt->execute([
    'name' => 'Alex'
]);

Получение ID новой записи

После вставки:

$id = $pdo->lastInsertId();

UPDATE

Изменение данных:

$stmt = $pdo->prepare(
    '
    UPDATE users
    SET name = :name
    WHERE id = :id
    '
);
 
$stmt->execute([
    'name' => 'John',
    'id' => 1
]);

DELETE

Удаление:

$stmt = $pdo->prepare(
    '
    DELETE FROM users
    WHERE id = :id
    '
);
 
$stmt->execute([
    'id' => 1
]);

Транзакции

Транзакция позволяет выполнить несколько операций как одно действие.

Перевод денег:

Списать деньги
      +
Добавить деньги

Если одна операция упала:

Откатить всё

Начало:

$pdo->beginTransaction();

Подтверждение:

$pdo->commit();

Откат:

$pdo->rollBack();

Пример:

try {
 
    $pdo->beginTransaction();
 
    // SQL 1
    // SQL 2
 
    $pdo->commit();
 
} catch (Exception $e) {
 
    $pdo->rollBack();
 
}

Обработка ошибок

Через исключения:

try {
 
    $pdo->query(
        'INVALID SQL'
    );
 
} catch (PDOException $e) {
 
    echo 'Database error';
 
}

В продакшене нельзя:

echo $e->getMessage();

Потому что пользователь может увидеть:

Table users doesn't exist

Лучше:

log_error($e);
echo 'Something went wrong';

Организация работы с БД

В небольших проектах:

src/
 ├─ database/
 │   └─ connection.php
 │
 ├─ models/
 │   └─ User.php
 │
 └─ services/

Пример отдельного подключения:

function getConnection(): PDO
{
    return new PDO(
        'mysql:host=localhost;dbname=test',
        'root',
        'password'
    );
}

Подготовленные запросы vs ORM

PDO:

$stmt = $pdo->prepare(
    'SELECT * FROM users'
);

ORM:

User::all();

PDO:
✅ полный контроль
✅ быстрее понять SQL
✅ меньше магии

ORM:
✅ меньше повторяющегося кода
✅ удобнее в больших проектах

Частые ошибки

Склеивать SQL строки

Плохо:

$sql =
"SELECT * FROM users WHERE id="
. $id;

Проблема:

  • SQL-инъекции

Не использовать prepare

Плохо:

$query(
    "DELETE FROM users WHERE id=$id"
);

Хранить пароль как обычный текст

Плохо:

$password = $_POST['password'];

Лучше:

password_hash(
    $password,
    PASSWORD_DEFAULT
);

Создавать новое подключение в каждом запросе

Плохо:

new PDO(...);
new PDO(...);
new PDO(...);

Лучше:

  • одно подключение
  • передача зависимости

🎯 Вывод

  • PDO – стандартный способ работы PHP с базами данных
  • он поддерживает разные СУБД через драйверы
  • всегда нужно использовать подготовленные запросы
  • prepare() защищает от SQL-инъекций
  • execute() передаёт параметры отдельно от SQL
  • fetch() получает одну запись
  • fetchAll() получает список
  • транзакции нужны для связанных операций
  • ошибки БД нужно обрабатывать через исключения
  • PDO – основа перед использованием ORM

🔗 Связано