# PHP i bazy danych: PDO i mysqli

> PHP i bazy danych wyjaśnione: PDO i mysqli w porównaniu, prepared statements, obsługa błędów od PHP 8 i bezpieczne dane dostępowe w eksploatacji.

Źródło: https://phpsos.de/pl/php/php-und-datenbanken/  
Stan: 25.10.2026  
Dostawca: PHP SOS, Pomoc awaryjna PHP i bezpieczeństwo serwerów. Natychmiastowa pomoc przez WhatsApp: https://wa.me/491736494914?text=Dzie%C5%84%20dobry%2C%20PHP%20SOS%2C%20potrzebujemy%20pomocy%20z%20naszym%20serwerem%20PHP.%20Firma%3A%20%E2%80%A6%20Strona%20internetowa%3A%20%E2%80%A6

**Krótko:** PHP korzysta z baz danych przez dwa rozpowszechnione interfejsy: PDO (PHP Data Objects) pracuje z wieloma systemami baz danych przez jednolity interfejs, mysqli jest dopasowane do MySQL i MariaDB. W obu przypadkach prepared statements, czyli zapytania z symbolami zastępczymi, niezawodnie chronią przed SQL injection. Od PHP 8.0 (PDO) i odpowiednio 8.1 (mysqli) oba domyślnie zgłaszają błędy jako wyjątki.

## Jak PHP komunikuje się z bazami danych

Prawie każda aplikacja PHP zapisuje swoje treści w bazie danych: strony, zamówienia, konta użytkowników, ustawienia. Skrypt PHP nawiązuje w tym celu połączenie, wysyła zapytanie w języku SQL i przetwarza wynik. Dane dostępowe znajdują się zwykle w pliku konfiguracyjnym na tym samym serwerze.

Dla zarządu jest to ważne, ponieważ baza danych zawiera zwykle najcenniejsze dane firmy. Kto może wykonać kod na serwerze WWW, może z reguły także odczytać plik konfiguracyjny i tymi danymi dostępowymi uzyskać dostęp do bazy danych.

## PDO i mysqli w porównaniu

| Cecha | PDO | mysqli |
|---|---|---|
| Bazy danych | wiele, przez sterowniki takie jak `PDO_MYSQL`, `PDO_PGSQL`, `PDO_SQLITE`, `PDO_SQLSRV`, `PDO_OCI` | MySQL i MariaDB |
| Symbole zastępcze | nazwane (`:name`) i pozycyjne (`?`) | pozycyjne (`?`) |
| Styl | obiektowy | obiektowy i proceduralny |
| Błędy jako wyjątki | domyślnie od PHP 8.0.0 | domyślnie od PHP 8.1.0 |
| Skrócona forma zapytań z parametrami | `prepare()` i `execute()` z tablicą | `execute_query()` od PHP 8.2.0 |

W praktyce często decyduje framework lub CMS. Wiele z nich ma własną warstwę bazodanową, która wewnętrznie używa PDO lub mysqli. Kto przejmuje aplikację, może prostym wyszukiwaniem w kodzie źródłowym ustalić, jakiego interfejsu używa się i gdzie powstają zapytania:

```bash
# Gdzie aplikacja nawiązuje połączenia, gdzie powstają zapytania?
grep -rn --include='*.php' -E 'new PDO|new mysqli|mysqli_connect|->query\(' /var/www/app/src | head -50
```

Miejsca z `->query(` nie są automatycznie niebezpieczne. Zasługują jednak na drugie spojrzenie, ponieważ wartości mogą tam trafić bezpośrednio do tekstu SQL zamiast być przekazane przez symbole zastępcze. Zwłaszcza starsze projekty mieszają oba style, a kontrola opłaca się przed każdą większą aktualizacją.

## Prepared statements: najważniejszy wzorzec

W prepared statement zapytanie z symbolami zastępczymi jest wysyłane do bazy danych, a wartości są przekazywane osobno. Według podręcznika PHP chroni to przed SQL injection, jeśli wszystkie dane od użytkownika przechodzą przez symbole zastępcze.

```php
<?php
declare(strict_types=1);

$pdo = new PDO(
    'mysql:host=localhost;dbname=shop;charset=utf8mb4',
    $dbUser,
    $dbPass,
    [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);

$stmt = $pdo->prepare('SELECT id, name FROM kunden WHERE plz = :plz AND aktiv = :aktiv');
$stmt->execute(['plz' => $plz, 'aktiv' => 1]);

foreach ($stmt as $zeile) {
    echo htmlspecialchars($zeile['name'], ENT_QUOTES, 'UTF-8'), "\n";
}
```

To samo z mysqli w skróconej formie, która istnieje od PHP 8.2:

```php
<?php
$db = new mysqli('localhost', $dbUser, $dbPass, 'shop');
$ergebnis = $db->execute_query('SELECT id, name FROM kunden WHERE plz = ?', [$plz]);

foreach ($ergebnis as $zeile) {
    echo htmlspecialchars($zeile['name'], ENT_QUOTES, 'UTF-8'), "\n";
}
```

Programiści powinni znać dwie granice. Symbole zastępcze nie mogą stać w cudzysłowach; przy wyszukiwaniach `LIKE` znaki wieloznaczne `%` należą do przekazywanej wartości. I symbole zastępcze zastępują tylko wartości, a nie nazwy tabel czy kolumn. Jak bezpiecznie rozwiązywać takie przypadki szczególne, pokazuje poradnik [Zapobieganie SQL injection w PHP](https://phpsos.de/pl/ratgeber/sql-injection-php/).

## Obsługa błędów: wyjątki zamiast cichych błędów

Dawniej oba interfejsy domyślnie pracowały cicho: nieudane zapytanie zwracało `false`, a kto tego nie sprawdził, pracował dalej z pustymi danymi. To się zmieniło:

- **PDO:** Od PHP 8.0.0 `PDO::ERRMODE_EXCEPTION` jest ustawieniem domyślnym. Błędy zgłaszają `PDOException`.
- **mysqli:** Od PHP 8.1.0 raportowanie błędów jest ustawione na `MYSQLI_REPORT_ERROR` i `MYSQLI_REPORT_STRICT`. Błędy zgłaszają `mysqli_sql_exception`; wcześniej raportowanie było wyłączone.

Dla bezpieczeństwa oznacza to: wyjątki trzeba przechwytywać i zapisywać w dzienniku, ale nigdy nie mogą trafić do przeglądarki z tekstem SQL, nazwami tabel lub danymi dostępowymi. Jak to czysto rozdzielić, opisuje strona [Błędy i wyjątki w PHP](https://phpsos.de/pl/php/fehler-und-exceptions/).

## Prawidłowe nadawanie danych dostępowych i uprawnień

Baza danych jest chroniona tak dobrze, jak jej dane dostępowe. Sprawdziły się te zasady:

1. **Osobny użytkownik dla każdej aplikacji:** Nigdy nie używać administratora bazy danych dla strony.
2. **Tylko potrzebne uprawnienia:** Strona potrzebuje zwykle odczytu, zapisu, zmiany i usuwania we własnej bazie, ale nie uprawnień do innych baz ani funkcji serwera.
3. **Bez połączenia zdalnego bez powodu:** Baza danych powinna być osiągalna tylko z serwera WWW, a nie z internetu.
4. **Dane dostępowe poza katalogiem głównym WWW:** Pliki konfiguracyjne i `.env` nie mogą być dostępne przez URL. Więcej w artykule [Ochrona danych dostępowych i plików .env](https://phpsos.de/pl/blog/env-dateien-schuetzen/).
5. **Szyfrowane połączenie przy rozdzielonych serwerach:** Jeśli baza danych leży na innym komputerze, połączenie powinno być szyfrowane, aby dane dostępowe i treści nie szły przez sieć jawnym tekstem.
6. **Własne dostępy do konserwacji:** Kopie zapasowe, analizy i administratorzy dostają własnych użytkowników. Dzięki temu w dzienniku można prześledzić, kto co zrobił, a dostęp można zablokować, nie zakłócając działania strony.

## Bazy danych, PHP2Shell i przypadek poważny

PHP2Shell nie dotyczy samej bazy danych, lecz interpretera PHP: CVE-2026-109618 i CVE-2026-109619 umożliwiają wykonanie złośliwego kodu bez logowania. Jeśli się to uda, kod działa z uprawnieniami procesu PHP i może zazwyczaj odczytać plik konfiguracyjny z danymi dostępowymi do bazy. Prepared statements tu nie pomagają, ponieważ atak w ogóle nie przechodzi przez zapytanie SQL.

Obowiązkowa jest więc aktualizacja do PHP 8.5.12, 8.4.27, 8.3.36 lub 8.2.35 i restart wszystkich procesów PHP. Jeśli są wskazówki na udany atak, należą do tego nowe dane dostępowe do bazy, sprawdzenie bazy pod kątem wstrzykniętych treści i ocena obowiązku zgłoszenia na podstawie RODO. Sensowne jest tu uporządkowane [reagowanie na incydenty (Incident Response)](https://phpsos.de/pl/leistungen/incident-response/).

> **Wskazówka:**
> Hasła do bazy danych proszę zmieniać dopiero po oczyszczeniu serwera. Dopóki działa złośliwy kod, może on odczytać także nowe dane dostępowe.

## Jak konkretnie pomaga PHP SOS?

- Sprawdzamy, jak Państwa aplikacja korzysta z bazy danych oraz czy użytkownicy, uprawnienia i dostęp sieciowy odpowiadają minimum.
- Kontrolujemy, czy pliki konfiguracyjne i z danymi dostępowymi leżą poza katalogiem głównym WWW i nie są publicznie dostępne.
- Po incydencie odnawiamy dane dostępowe we właściwej kolejności i sprawdzamy bazę danych pod kątem wstrzykniętego kodu.
- Dokumentujemy wyniki tak, aby mogli je prześledzić inspektorzy ochrony danych i ubezpieczyciele.


## Częste pytania

**Czy używać PDO, czy mysqli?**  
W nowych projektach PDO jest zwykle bardziej elastycznym wyborem, ponieważ pracuje z wieloma bazami danych przez ten sam interfejs. mysqli ma sens, jeśli używają Państwo wyłącznie MySQL lub MariaDB i potrzebują ich funkcji specjalnych. Oba są bezpieczne, jeśli konsekwentnie stosuje się prepared statements.

**Czy PDO przy błędach automatycznie zgłasza wyjątki?**  
Tak, od PHP 8.0.0 PDO::ERRMODE_EXCEPTION jest ustawieniem domyślnym. Wcześniej był to tryb cichy, w którym błędy dało się odczytać tylko przez errorCode() i errorInfo(). Starsze aplikacje często nadal ustawiają ten tryb wprost.

**Czy symbole zastępcze mogą zastępować także nazwy tabel lub kolumn?**  
Nie. Symbole zastępcze oznaczają tylko wartości. Nazwy tabel i kolumn, na przykład do sortowania, trzeba sprawdzić względem stałej listy dozwolonych nazw, zanim zostaną wstawione do zapytania.

**Czy po PHP2Shell muszę zmienić hasło do bazy danych?**  
Sama aktualizacja tego nie wymaga. Jeśli jednak są wskazówki, że serwer był skompromitowany, wszystkie dane dostępowe w plikach konfiguracyjnych należy uznać za znane. Wtedy powinni je Państwo odnowić po oczyszczeniu.

## Źródła

- [Podręcznik PHP: PDO Prepared Statements](https://www.php.net/manual/en/pdo.prepared-statements.php)
- [Podręcznik PHP: obsługa błędów PDO](https://www.php.net/manual/en/pdo.error-handling.php)
- [Podręcznik PHP: mysqli::execute_query](https://www.php.net/manual/en/mysqli.execute-query.php)
- [Podręcznik PHP: mysqli_driver::$report_mode](https://www.php.net/manual/en/mysqli-driver.report-mode.php)
