Урок 17

PDO — подключаем PHP к базе данных

30 мин

Что такое PDO?

PDO (PHP Data Objects) — это встроенный в PHP способ работы с базами данных. Он:

  • Работает с разными базами (MySQL, PostgreSQL, SQLite...)
  • Защищает от SQL-инъекций
  • Удобен в использовании

Создаём файл подключения

Обновите файл public/includes/config.php:

<?php
declare(strict_types=1);

/**
 * Конфигурация сайта
 */

// Настройки сайта
$site_name = "Мой блог";
$site_description = "Блог о веб-разработке";
$current_year = date("Y");

// Загружаем переменные окружения из .env файла
// В PHP 8.1+ можно использовать parse_ini_file, но для простоты используем простой парсер
function loadEnv(string $filePath): void {
    if (!file_exists($filePath)) {
        return;
    }
    
    $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($lines as $line) {
        // Пропускаем комментарии
        if (strpos(trim($line), "#") === 0) {
            continue;
        }
        
        // Разделяем на ключ и значение
        if (strpos($line, "=") !== false) {
            [$key, $value] = explode("=", $line, 2);
            $key = trim($key);
            $value = trim($value);
            
            // Убираем кавычки если есть
            if ((strpos($value, "\"") === 0 && substr($value, -1) === "\"") ||
                (strpos($value, "'") === 0 && substr($value, -1) === "'")) {
                $value = substr($value, 1, -1);
            }
            
            // Устанавливаем переменную окружения, если её ещё нет
            if (!isset($_ENV[$key])) {
                $_ENV[$key] = $value;
            }
        }
    }
}

// Загружаем .env файл (находится на уровень выше public/)
loadEnv(__DIR__ . "/../../.env");

// Настройки базы данных из переменных окружения
$db_host = $_ENV["MYSQL_HOST"] ?? "mysql";       // Имя контейнера из docker-compose
$db_name = $_ENV["MYSQL_DATABASE"] ?? "blog";     // Имя базы данных
$db_user = $_ENV["MYSQL_USER"] ?? "blog_user";   // Пользователь
$db_pass = $_ENV["MYSQL_PASSWORD"] ?? "blog_pass"; // Пароль

// Подключение к базе данных
try {
    $pdo = new PDO(
        "mysql:host=$db_host;dbname=$db_name;charset=utf8mb4",
        $db_user,
        $db_pass,
        [
            // Выбрасывать исключения при ошибках
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            // Получать данные как ассоциативные массивы
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            // Отключить эмуляцию подготовленных запросов (безопаснее)
            PDO::ATTR_EMULATE_PREPARES => false,
        ]
    );
} catch (PDOException $e) {
    // Если подключение не удалось
    die("Ошибка подключения к базе данных: " . $e->getMessage());
}

// Подключаем функции-помощники
require_once __DIR__ . "/helpers.php";
?>
Зачем использовать .env файл?
- Безопасность: Пароли не хранятся в коде, не попадут в Git
- Гибкость: Разные настройки для разработки и продакшна
- Стандарт: В 2026 это стандартная практика для всех PHP проектов

Разбор подключения

DSN (Data Source Name)

"mysql:host=$db_host;dbname=$db_name;charset=utf8mb4"
  • mysql: — тип базы данных
  • host=mysql — адрес сервера (имя контейнера Docker)
  • dbname=blog — имя базы данных
  • charset=utf8mb4 — кодировка (поддержка эмодзи)

try-catch

try {
    // Пытаемся подключиться
} catch (PDOException $e) {
    // Если ошибка — обрабатываем её
}

Если подключение не удастся (неправильный пароль, сервер недоступен), код в catch покажет ошибку вместо "белого экрана".

Проверяем подключение

Создайте файл public/test_db.php:

<?php
require_once "includes/config.php";

echo "<h1>Тест подключения к базе данных</h1>";

// Проверяем подключение
$stmt = $pdo->query("SELECT VERSION() as version");
$result = $stmt->fetch();

echo "<p>Версия MySQL: " . $result["version"] . "</p>";

// Получаем пользователей
$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll();

echo "<h2>Пользователи:</h2>";
echo "<ul>";
foreach ($users as $user) {
    echo "<li>" . htmlspecialchars($user["username"]) . " — " . htmlspecialchars($user["email"]) . "</li>";
}
echo "</ul>";

// Получаем посты
$stmt = $pdo->query("SELECT * FROM posts ORDER BY created_at DESC");
$posts = $stmt->fetchAll();

echo "<h2>Посты:</h2>";
foreach ($posts as $post) {
    echo "<article style='background:#f5f5f5; padding:15px; margin:10px 0;'>";
    echo "<h3>" . htmlspecialchars($post["title"]) . "</h3>";
    echo "<p>" . htmlspecialchars($post["content"]) . "</p>";
    echo "</article>";
}
?>

Откройте http://localhost:8080/test_db.php. Если видите данные — подключение работает!

Подготовленные запросы — защита от SQL-инъекций

Что такое SQL-инъекция?

Представьте форму входа. Пользователь вводит логин, а мы подставляем его в запрос:

// ПЛОХО! Никогда так не делайте!
$username = $_POST["username"];
$sql = "SELECT * FROM users WHERE username = '$username'";
$pdo->query($sql);

Если злоумышленник введёт: ' OR '1'='1, запрос станет:

SELECT * FROM users WHERE username = '' OR '1'='1'

Это вернёт ВСЕХ пользователей, потому что '1'='1' всегда истина!

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

// ХОРОШО!
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();

Или с именованными параметрами:

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute(["username" => $username]);
$user = $stmt->fetch();

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

Основные методы PDO

Метод Что делает
$pdo->query($sql) Выполнить простой запрос без параметров
$pdo->prepare($sql) Подготовить запрос с параметрами
$stmt->execute($params) Выполнить подготовленный запрос
$stmt->fetch() Получить одну запись
$stmt->fetchAll() Получить все записи
$pdo->lastInsertId() ID последней добавленной записи
$stmt->rowCount() Количество затронутых строк

Примеры запросов

Получить одну запись

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

if ($user) {
    echo "Пользователь: " . $user["username"];
} else {
    echo "Пользователь не найден";
}

Получить все записи

$stmt = $pdo->query("SELECT * FROM posts ORDER BY created_at DESC");
$posts = $stmt->fetchAll();

foreach ($posts as $post) {
    echo $post["title"] . "<br>";
}

Добавить запись

$stmt = $pdo->prepare("INSERT INTO posts (user_id, title, content) VALUES (?, ?, ?)");
$stmt->execute([1, "Новый пост", "Содержимое поста"]);

$newId = $pdo->lastInsertId();
echo "Создан пост с ID: " . $newId;

Обновить запись

$stmt = $pdo->prepare("UPDATE posts SET title = ? WHERE id = ?");
$stmt->execute(["Обновлённый заголовок", 1]);

echo "Обновлено строк: " . $stmt->rowCount();

Удалить запись

$stmt = $pdo->prepare("DELETE FROM posts WHERE id = ?");
$stmt->execute([5]);

echo "Удалено строк: " . $stmt->rowCount();
Теперь вы умеете:
✅ Подключаться к MySQL из PHP
✅ Использовать PDO безопасно
✅ Выполнять SELECT, INSERT, UPDATE, DELETE

В следующем уроке создадим функции для работы с данными блога!

Мы используем файлы cookie для улучшения работы сайта и персонализации контента. Продолжая использовать сайт, вы соглашаетесь с использованием cookies в соответствии с нашей Политикой конфиденциальности.