Połączenie z bazą przez PDO

🎯 Po tej lekcji będziesz łączyć PHP z MySQL przez PDO, ustawiać tryb błędów i kodowanie utf8mb4 oraz przechwytywać wyjątki PDOException.

W poprzedniej lekcji pisałeś zapytania SQL bezpośrednio w phpMyAdmin. Teraz połączysz PHP z bazą danych przez PDO — PHP Data Objects — i zaczniesz korzystać z niej w kodzie.

Dlaczego PDO, a nie mysql_* ani mysqli?

PHP historycznie miało kilka sposobów łączenia się z MySQL:

  • mysql_* — stare funkcje (np. mysql_connect, mysql_query) usunięte z PHP 7. Nie używaj ich. Jeśli widzisz je w tutorialach, ten tutorial jest przestarzały.
  • mysqli — następca mysql_*, dostępny od PHP 5. Działa tylko z MySQL/MariaDB.
  • PDO — nowoczesne, obiektowe rozszerzenie PHP obsługujące wiele różnych baz danych (MySQL, PostgreSQL, SQLite i inne). Jeden interfejs, wiele baz. To jest właściwy wybór.
💡 Kursy oparte na mysql_* są nieaktualne: Stare samouczki często używają funkcji mysql_connect() i mysql_query(). To kod z PHP 5 — niedziałający od PHP 7. Jeśli trafiasz na taki materiał, szukaj nowszego źródła.

DSN — łańcuch połączenia

PDO identyfikuje bazę danych przez DSN (Data Source Name) — łańcuch tekstowy w określonym formacie:

mysql:host=localhost;dbname=moja_aplikacja;charset=utf8mb4

Poszczególne elementy:

| Element | Znaczenie | |---|---| | mysql: | Sterownik — używamy MySQL/MariaDB | | host=localhost | Serwer bazy danych (lub adres IP) | | dbname=moja_aplikacja | Nazwa bazy danych | | charset=utf8mb4 | Kodowanie znaków (zawsze utf8mb4) |

Dlaczego charset=utf8mb4, nie utf8?

MySQL ma dwa kodowania o mylących nazwach:

  • utf8 — historyczna 3-bajtowa implementacja. Nie obsługuje emoji (🎉) ani niektórych rzadkich znaków.
  • utf8mb4 — prawdziwe UTF-8, 4 bajty. Obsługuje cały standard Unicode.

Zawsze używaj utf8mb4. Ustawiasz je w DSN (charset=utf8mb4), a MySQL zadba o resztę: prawidłowe porównywanie i sortowanie polskich znaków.

Tworzenie połączenia

Połączenie otwierasz konstruktorem klasy PDO:

<?php
declare(strict_types=1);

$dsn = 'mysql:host=localhost;dbname=moja_aplikacja;charset=utf8mb4';

$pdo = new PDO($dsn, 'uzytkownik', 'haslo');

Trzy argumenty: DSN, nazwa użytkownika, hasło.

Niezbędna konfiguracja — atrybuty

Domyślna konfiguracja PDO jest zbyt łagodna na błędy. Zawsze ustawiaj dwa atrybuty:

<?php
declare(strict_types=1);

$dsn = 'mysql:host=localhost;dbname=moja_aplikacja;charset=utf8mb4';

$pdo = new PDO($dsn, 'uzytkownik', 'haslo', [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

Co te atrybuty robią?

  • PDO::ERRMODE_EXCEPTION — każdy błąd SQL (literówka w nazwie tabeli, brak kolumny, problem z siecią) rzuca wyjątek PDOException. Bez tego PDO cicho zwraca false i łatwo przeoczyć błąd.
  • PDO::FETCH_ASSOC — wiersze zwracane przez fetch() i fetchAll() będą tablicami asocjacyjnymi ($row['tytul']), nie numerycznymi ($row[0]). Czytelniejszy kod.

Obsługa błędów — try/catch z PDOException

Jeżeli połączenie się nie powiedzie (zła nazwa bazy, złe hasło, serwer nie odpowiada), PDO rzuci PDOException. Chwyć go przez try/catch:

<?php
declare(strict_types=1);

$dsn = 'mysql:host=localhost;dbname=moja_aplikacja;charset=utf8mb4';

try {
    $pdo = new PDO($dsn, 'uzytkownik', 'haslo', [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]);
} catch (PDOException $e) {
    // Zaloguj szczegóły błędu po STRONIE SERWERA — nie wyświetlaj ich użytkownikowi
    error_log('Błąd połączenia PDO: ' . $e->getMessage());

    // Użytkownikowi pokaż ogólny komunikat
    echo 'Przepraszamy, wystąpił problem techniczny. Spróbuj ponownie za chwilę.';
    exit();
}

// Tutaj $pdo jest gotowe do użycia
echo 'Połączono z bazą danych.';
⚠️ Nie ujawniaj komunikatów błędów PDO użytkownikowi: Wiadomość $e->getMessage() często zawiera nazwę bazy, użytkownika, tabelę lub fragment hasła. To dane wrażliwe dla atakującego. Loguj je przez error_log() lub dedykowany logger (Monolog z Modułu 7), a użytkownikowi pokazuj tylko ogólny komunikat.

Kompletny przykład — plik konfiguracyjny bazy

W prawdziwej aplikacji tworzymy jeden centralny plik z konfiguracją połączenia. Reszta kodu go dołącza:

<?php
declare(strict_types=1);

// db.php — wczytyj przez require 'db.php';

$dsn = 'mysql:host=localhost;dbname=moja_aplikacja;charset=utf8mb4';

try {
    $pdo = new PDO($dsn, 'root', '', [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]);
} catch (PDOException $e) {
    error_log('PDO: ' . $e->getMessage());
    http_response_code(503);
    exit('Błąd serwera.');
}

W kolejnych lekcjach przyjmiemy, że $pdo jest już dostępne — właśnie z takiego pliku konfiguracyjnego.

💡 Dane dostępowe poza kodem: Użytkownik, hasło i nazwa bazy nie powinny być na stałe w kodzie PHP trafiającym do repozytorium Git. W praktyce trzyma się je w zmiennych środowiskowych lub w pliku .env — z pakietem vlucas/phpdotenv (Packagist). Na razie użyjemy prostych zmiennych, ale warto to wiedzieć.

W kolejnej lekcji wykonasz pierwsze zapytania SELECT przez PDO i zobaczysz, jak iterować po wynikach.

Sprawdź się

Jakie kodowanie znaków należy podać w DSN połączenia PDO z MySQL?

Który atrybut PDO powoduje, że błędy SQL są zgłaszane jako wyjątki?

Dlaczego NIE należy wyświetlać komunikatu $e->getMessage() z PDOException bezpośrednio użytkownikowi?

Ćwiczenie

Napisz funkcję polaczDB(): PDO, która zwraca obiekt PDO połączony z bazą moja_aplikacja na localhost, z użytkownikiem root i pustym hasłem. Ustaw ERRMODE_EXCEPTION, FETCH_ASSOC i charset utf8mb4. Obsłuż błąd połączenia przez przechwycenie PDOException i wypisanie 'Błąd połączenia z bazą danych.' (bez szczegółów) — a następnie exit().

Pokaż rozwiązanie
<?php
declare(strict_types=1);

function polaczDB(): PDO
{
    $dsn = 'mysql:host=localhost;dbname=moja_aplikacja;charset=utf8mb4';

    try {
        $pdo = new PDO($dsn, 'root', '', [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $e) {
        // Loguj szczegóły po stronie serwera (nie pokazuj użytkownikowi)
        error_log('PDO connect: ' . $e->getMessage());
        echo 'Błąd połączenia z bazą danych.';
        exit();
    }

    return $pdo;
}