PHP PDO: подключение к MySQL и подготовленные запросы (понятное руководство)

PHP PDO: подключение к MySQL и подготовленные запросы (понятное руководство)

PHP PDO: подключение к MySQL и подготовленные запросы (понятное руководство)

Если вы начинаете работать с базами данных в PHP или хотите перейти на современный и безопасный способ общения с MySQL, используйте PDO. Он поддерживает подготовленные запросы (prepared statements), транзакции и единый интерфейс для разных СУБД. В статье — практический минимум, чтобы быстро и правильно стартовать с PHP PDO и подготовленными запросами.

Зачем использовать PDO

  • Безопасность: подготовленные выражения защищают от SQL-инъекций по умолчанию.
  • Универсальность: переключение между MySQL, PostgreSQL и др. почти без переписывания кода.
  • Удобство: исключения, настройки ошибок, гибкие режимы выборки.
  • Подключение к MySQL через PDO

    Настройте кодировку, ошибки и режим выборки сразу при подключении. Так вы избежите 80% проблем в будущем.

    <?php
    $dsn = 'mysql:host=127.0.0.1;dbname=app;charset=utf8mb4';
    $user = 'app_user';
    $pass = 'secret';
    $options = [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,          // ошибки как исключения
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,     // массивы без дублей индексов
        PDO::ATTR_EMULATE_PREPARES => false,                  // нативные prepared statements
    ];
    
    try {
        $pdo = new PDO($dsn, $user, $pass, $options);
    } catch (PDOException $e) {
        // В проде логируйте, а не показывайте пользователю детали
        die('DB error: ' . $e->getMessage());
    }
    

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

    Prepared statements позволяют отделить SQL от данных. Это устраняет инъекции и упрощает повторные вызовы одного и того же запроса.

    SELECT с именованными параметрами

    <?php
    $status = 'active';
    $limit = 10;
    
    $sql = 'SELECT id, email FROM users WHERE status = :status LIMIT :limit';
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':status', $status, PDO::PARAM_STR);
    $stmt->bindValue(':limit', (int)$limit, PDO::PARAM_INT); // для LIMIT/OFFSET используйте INT
    $stmt->execute();
    $users = $stmt->fetchAll();
    

    Если ваш драйвер не поддерживает биндинг для LIMIT/OFFSET с отключённой эмуляцией, безопасная альтернатива — жёсткое приведение к int и интерполяция:

    <?php
    $limit = (int)$limit;
    $sql = "SELECT id, email FROM users WHERE status = :status LIMIT $limit"; // безопасно после (int)
    $stmt = $pdo->prepare($sql);
    $stmt->execute([':status' => $status]);
    

    INSERT с позиционными параметрами

    <?php
    $email = 'new@example.com';
    $name  = 'New User';
    
    $sql = 'INSERT INTO users (email, name, created_at) VALUES (?, ?, NOW())';
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$email, $name]);
    $newId = (int)$pdo->lastInsertId();
    

    UPDATE и счётчик затронутых строк

    <?php
    $sql = 'UPDATE users SET name = :name WHERE id = :id';
    $stmt = $pdo->prepare($sql);
    $stmt->execute([':name' => 'Alice', ':id' => 5]);
    $updated = $stmt->rowCount(); // сколько строк обновлено
    

    Как PDO защищает от SQL-инъекций

    Ошибка новичка — склеивать SQL и данные.

    <?php
    // ПЛОХО: уязвимо к инъекциям
    $email = $_GET['email'];
    $q = $pdo->query("SELECT * FROM users WHERE email = '$email'");
    
    // ПРАВИЛЬНО: используем prepare + параметры
    $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
    $stmt->execute([':email' => $email]);
    $user = $stmt->fetch();
    

    Транзакции: надёжные изменения данных

    Транзакции гарантируют целостность: либо все операции проходят, либо ни одна.

    <?php
    $from = 1; $to = 2; $sum = 100;
    
    $pdo->beginTransaction();
    try {
        $dec = $pdo->prepare('UPDATE accounts SET balance = balance - :s WHERE id = :id');
        $dec->execute([':s' => $sum, ':id' => $from]);
        if ($dec->rowCount() !== 1) {
            throw new RuntimeException('Не удалось списать средства');
        }
    
        $inc = $pdo->prepare('UPDATE accounts SET balance = balance + :s WHERE id = :id');
        $inc->execute([':s' => $sum, ':id' => $to]);
    
        $pdo->commit();
    } catch (Throwable $e) {
        $pdo->rollBack();
        // логируем и пробрасываем дальше
        throw $e;
    }
    

    Динамический IN с массивом значений

    Частая задача — выбрать записи по списку id. Решение — сгенерировать плейсхолдеры под каждое значение.

    <?php
    $ids = [3, 5, 9];
    $placeholders = implode(',', array_fill(0, count($ids), '?'));
    $sql = "SELECT id, name FROM products WHERE id IN ($placeholders)";
    $stmt = $pdo->prepare($sql);
    $stmt->execute($ids);
    $products = $stmt->fetchAll();
    

    Эффективность: переиспользование подготовленного выражения

    <?php
    $userIds = [1,2,3,4,5];
    $stmt = $pdo->prepare('UPDATE users SET last_login = NOW() WHERE id = ?');
    foreach ($userIds as $id) {
        $stmt->execute([$id]);
    }
    

    Отладка запросов

    <?php
    $stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
    $stmt->bindValue(':id', 10, PDO::PARAM_INT);
    $stmt->execute();
    
    // В DEV-среде можно посмотреть, что и как было подставлено
    $stmt->debugDumpParams();
    

    Важно: debugDumpParams предназначен для отладки и не должен светиться в продакшене.

    Типичные ошибки и как их избежать

  • Забыли установить ERRMODE_EXCEPTION — исключения не летят, ошибки тихо игнорируются.
  • Склеивание SQL-строк с пользовательскими данными — используйте параметры везде, где можно.
  • Неверный тип для LIMIT/OFFSET — применяйте (int) и PDO::PARAM_INT.
  • Отключили транзакции при нескольких связанных изменениях — включайте их для целостности.
  • Неправильная кодировка — всегда charset=utf8mb4 в DSN для работы с emoji и всеми символами.
  • Мини-чеклист перед продакшеном

  • PDO::ATTR_ERRMODE = EXCEPTION
  • PDO::ATTR_EMULATE_PREPARES = false (по возможности)
  • charset=utf8mb4 в DSN
  • Все внешние данные — через подготовленные параметры
  • Транзакции вокруг связанных изменений
  • Логи ошибок — пишем в файлы/мониторинг, не показываем пользователю
  • Куда двигаться дальше

    Освоив подключение и подготовленные запросы, изучите пулы соединений, репозитории, миграции и профилирование. Если хотите системно прокачать PHP и базу данных, рекомендую практический путь с обратной связью: Пройти полный курс «PHP и MySQL с Нуля до Гуру 3.0» и закрыть пробелы быстрым темпом.

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

    Источник

    НЕТ КОММЕНТАРИЕВ

    Оставить комментарий