# PHP i baze podataka: PDO i mysqli

> PHP i baze podataka objašnjeni: usporedba PDO-a i mysqli-ja, prepared statements, obrada pogrešaka od PHP-a 8 i sigurni pristupni podaci u radu.

Izvor: https://phpsos.de/hr/php/php-und-datenbanken/  
Stanje: 25.10.2026  
Pružatelj: PHP SOS, PHP hitna pomoć i sigurnost poslužitelja. Hitna pomoć putem WhatsAppa: https://wa.me/491736494914?text=Pozdrav%20PHP%20SOS%2C%20trebamo%20pomo%C4%87%20s%20na%C5%A1im%20PHP%20poslu%C5%BEiteljem.%20Tvrtka%3A%20%E2%80%A6%20Web-stranica%3A%20%E2%80%A6

**Ukratko:** PHP pristupa bazama podataka putem dvaju raširenih sučelja: PDO (PHP Data Objects) radi s mnogim sustavima baza podataka putem jedinstvenog sučelja, a mysqli je skrojen za MySQL i MariaDB. S oba pouzdano štite od SQL injectiona prepared statements, dakle upiti s rezerviranim mjestima. Od PHP-a 8.0 (PDO) odnosno 8.1 (mysqli) oba pogreške prema zadanim postavkama prijavljuju kao iznimku.

## Kako PHP razgovara s bazama podataka

Gotovo svaka PHP aplikacija sprema svoj sadržaj u bazu podataka: stranice, narudžbe, korisničke račune, postavke. PHP skripta za to uspostavlja vezu, šalje upit u jeziku SQL i obrađuje rezultat. Pristupni podaci najčešće stoje u konfiguracijskoj datoteci na istom poslužitelju.

Za upravu je to važno jer baza podataka najčešće sadrži najvrjednije podatke tvrtke. Tko može izvršavati kod na web-poslužitelju, u pravilu može pročitati i konfiguracijsku datoteku te tim pristupnim podacima pristupiti bazi.

## PDO i mysqli u usporedbi

| Obilježje | PDO | mysqli |
|---|---|---|
| Baze podataka | mnoge, putem upravljačkih programa poput `PDO_MYSQL`, `PDO_PGSQL`, `PDO_SQLITE`, `PDO_SQLSRV`, `PDO_OCI` | MySQL i MariaDB |
| Rezervirana mjesta | imenovana (`:name`) i pozicijska (`?`) | pozicijska (`?`) |
| Stil | objektno orijentiran | objektno orijentiran i proceduralni |
| Pogreške kao iznimke | zadano od PHP-a 8.0.0 | zadano od PHP-a 8.1.0 |
| Skraćeni oblik za upite s parametrima | `prepare()` i `execute()` s poljem | `execute_query()` od PHP-a 8.2.0 |

U praksi često odlučuje framework ili CMS. Mnogi donose vlastiti sloj baze podataka koji iznutra koristi PDO ili mysqli. Tko preuzima aplikaciju, jednostavnim pretraživanjem izvornog koda može utvrditi koje se sučelje koristi i gdje nastaju upiti:

```bash
# Gdje aplikacija uspostavlja veze, gdje nastaju upiti?
grep -rn --include='*.php' -E 'new PDO|new mysqli|mysqli_connect|->query\(' /var/www/app/src | head -50
```

Pogodci s `->query(` nisu automatski nesigurni. No zaslužuju drugi pogled jer tamo vrijednosti mogu izravno dospjeti u SQL tekst umjesto da se predaju putem rezerviranih mjesta. Osobito stariji projekti miješaju oba stila, a provjera se isplati prije svakog većeg ažuriranja.

## Prepared statements: najvažniji uzorak

Kod prepared statementa upit se bazi šalje s rezerviranim mjestima, a vrijednosti se predaju odvojeno. Prema PHP priručniku to štiti od SQL injectiona ako svi korisnički unosi idu putem rezerviranih mjesta.

```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";
}
```

Isto s mysqli-jem u skraćenom obliku koji postoji od PHP-a 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";
}
```

Razvijatelji bi trebali poznavati dvije granice. Rezervirana mjesta ne smiju stajati u navodnicima; kod `LIKE` pretraga znakovi zamjene `%` pripadaju u predanu vrijednost. I rezervirana mjesta zamjenjuju samo vrijednosti, a ne nazive tablica ili stupaca. Kako se takvi posebni slučajevi sigurno rješavaju, pokazuje vodič [Sprječavanje SQL injectiona u PHP-u](https://phpsos.de/hr/ratgeber/sql-injection-php/).

## Obrada pogrešaka: iznimke umjesto tihih pogrešaka

Prije su oba sučelja prema zadanim postavkama radila tiho: neuspjeli upit vraćao je `false`, a tko to nije provjerio, nastavljao je raditi s praznim podacima. To se promijenilo:

- **PDO:** Od PHP-a 8.0.0 `PDO::ERRMODE_EXCEPTION` je zadano. Pogreške bacaju `PDOException`.
- **mysqli:** Od PHP-a 8.1.0 prijavljivanje pogrešaka postavljeno je na `MYSQLI_REPORT_ERROR` i `MYSQLI_REPORT_STRICT`. Pogreške bacaju `mysqli_sql_exception`; prije je prijavljivanje bilo isključeno.

Za sigurnost to znači: iznimke se moraju hvatati i bilježiti, ali nikad ne smiju završiti u pregledniku sa SQL tekstom, nazivima tablica ili pristupnim podacima. Kako to uredno razdvojiti, opisuje stranica [Pogreške i iznimke u PHP-u](https://phpsos.de/hr/php/fehler-und-exceptions/).

## Pravilno dodijeliti pristupne podatke i prava

Baza podataka zaštićena je samo koliko i njezini pristupni podaci. Ova su se pravila pokazala dobrima:

1. **Vlastiti korisnik po aplikaciji:** Za web-stranicu nikad ne koristiti administratora baze podataka.
2. **Samo potrebna prava:** Web-stranica najčešće treba čitanje, pisanje, izmjenu i brisanje u vlastitoj bazi, ali ne prava na druge baze podataka ili funkcije poslužitelja.
3. **Nema udaljene veze bez razloga:** Baza podataka trebala bi biti dostupna samo s web-poslužitelja, a ne s interneta.
4. **Pristupni podaci izvan webroota:** Konfiguracijske i `.env` datoteke ne smiju se moći dohvatiti putem URL-a. Više u članku [Zaštita pristupnih podataka i .env datoteka](https://phpsos.de/hr/blog/env-dateien-schuetzen/).
5. **Šifrirana veza kod odvojenih poslužitelja:** Ako je baza podataka na drugom računalu, veza bi trebala biti šifrirana kako pristupni podaci i sadržaj ne bi putovali mrežom u čistom tekstu.
6. **Vlastiti pristupi za održavanje:** Sigurnosne kopije, analize i administratori dobivaju vlastite korisnike. Tako se u logu može pratiti tko je što učinio, a pristup se može blokirati bez ometanja web-stranice.

## Baze podataka, PHP2Shell i ozbiljan slučaj

PHP2Shell ne pogađa samu bazu podataka, nego PHP interpreter: CVE-2026-109618 i CVE-2026-109619 omogućuju izvršavanje zlonamjernog koda bez prijave. Ako uspije, kod radi s pravima PHP procesa i tako u pravilu može pročitati konfiguracijsku datoteku s pristupnim podacima baze. Prepared statements tu ne pomažu jer napad uopće ne ide putem SQL upita.

Obvezno je zato ažuriranje na PHP 8.5.12, 8.4.27, 8.3.36 ili 8.2.35 i ponovno pokretanje svih PHP procesa. Ako postoje naznake uspješnog napada, tome pripadaju novi pristupni podaci baze, provjera baze na ubačene sadržaje i procjena obveze prijave prema GDPR-u. Ovdje je smisleno strukturirano [Incident Response](https://phpsos.de/hr/leistungen/incident-response/).

> **Savjet:**
> Lozinke baze podataka mijenjajte tek nakon čišćenja poslužitelja. Dok zlonamjerni kod radi, može ponovno pročitati i nove pristupne podatke.

## Kako PHP SOS konkretno pomaže?

- Provjeravamo kako Vaša aplikacija pristupa bazi podataka te odgovaraju li korisnici, prava i mrežni pristup minimumu.
- Kontroliramo nalaze li se konfiguracijske i pristupne datoteke izvan webroota i nisu li javno dostupne.
- Nakon incidenta obnavljamo pristupne podatke ispravnim redoslijedom i provjeravamo bazu na ubačeni kod.
- Rezultate dokumentiramo tako da ih službenici za zaštitu podataka i osiguravatelji mogu pratiti.


## Česta pitanja

**Trebam li koristiti PDO ili mysqli?**  
Za nove projekte PDO je najčešće fleksibilniji izbor jer s mnogim bazama podataka radi putem istog sučelja. mysqli je smislen ako koristite isključivo MySQL ili MariaDB i trebate njihove posebne funkcije. Oba su sigurna ako se prepared statements dosljedno koriste.

**Baca li PDO kod pogrešaka automatski iznimke?**  
Da, od PHP-a 8.0.0 PDO::ERRMODE_EXCEPTION je zadani način. Prije je to bio tihi način u kojem su se pogreške mogle dohvatiti samo putem errorCode() i errorInfo(). Starije aplikacije često taj način još izričito postavljaju.

**Mogu li rezervirana mjesta zamijeniti i nazive tablica ili stupaca?**  
Ne. Rezervirana mjesta stoje samo za vrijednosti. Nazivi tablica i stupaca, na primjer za sortiranje, moraju se prije umetanja u upit provjeriti prema čvrstom popisu dopuštenih naziva.

**Moram li nakon PHP2Shella promijeniti lozinku baze podataka?**  
Samo ažuriranje to ne zahtijeva. Ali ako postoje naznake da je poslužitelj bio kompromitiran, svi pristupni podaci u konfiguracijskim datotekama smatraju se poznatima. Tada ih nakon čišćenja treba obnoviti.

## Izvori

- [PHP priručnik: PDO Prepared Statements](https://www.php.net/manual/en/pdo.prepared-statements.php)
- [PHP priručnik: PDO obrada pogrešaka](https://www.php.net/manual/en/pdo.error-handling.php)
- [PHP priručnik: mysqli::execute_query](https://www.php.net/manual/en/mysqli.execute-query.php)
- [PHP priručnik: mysqli_driver::$report_mode](https://www.php.net/manual/en/mysqli-driver.report-mode.php)
