# PHP en databases: PDO en mysqli

> PHP en databases uitgelegd: PDO en mysqli vergeleken, prepared statements, foutafhandeling sinds PHP 8 en veilige toegangsgegevens in bedrijf.

Bron: https://phpsos.de/nl/php/php-und-datenbanken/  
Stand: 25-10-2026  
Aanbieder: PHP SOS, PHP-noodhulp en serverbeveiliging. Directe hulp via WhatsApp: https://wa.me/491736494914?text=Hallo%20PHP%20SOS%2C%20wij%20hebben%20hulp%20nodig%20met%20onze%20PHP-server.%20Bedrijf%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**Kort gezegd:** PHP benadert databases via twee wijdverbreide interfaces: PDO (PHP Data Objects) werkt met veel databasesystemen via een uniforme interface, mysqli is toegesneden op MySQL en MariaDB. Met beide beschermen prepared statements, dus query's met plaatsaanduidingen, betrouwbaar tegen SQL-injectie. Sinds PHP 8.0 (PDO) respectievelijk 8.1 (mysqli) melden beide fouten standaard als exception.

## Hoe PHP met databases praat

Vrijwel elke PHP-toepassing slaat haar inhoud op in een database: pagina's, bestellingen, gebruikersaccounts, instellingen. Het PHP-script bouwt daarvoor een verbinding op, stuurt een query in de taal SQL en verwerkt het resultaat. De toegangsgegevens staan meestal in een configuratiebestand op dezelfde server.

Voor de directie is dat belangrijk, omdat de database meestal de waardevolste gegevens van het bedrijf bevat. Wie code op de webserver kan uitvoeren, kan in de regel ook het configuratiebestand lezen en met deze toegangsgegevens bij de database komen.

## PDO en mysqli vergeleken

| Kenmerk | PDO | mysqli |
|---|---|---|
| Databases | veel, via stuurprogramma's als `PDO_MYSQL`, `PDO_PGSQL`, `PDO_SQLITE`, `PDO_SQLSRV`, `PDO_OCI` | MySQL en MariaDB |
| Plaatsaanduidingen | benoemd (`:name`) en positioneel (`?`) | positioneel (`?`) |
| Stijl | objectgeoriënteerd | objectgeoriënteerd en procedureel |
| Fouten als exception | standaard sinds PHP 8.0.0 | standaard sinds PHP 8.1.0 |
| Verkorte vorm voor query's met parameters | `prepare()` en `execute()` met array | `execute_query()` sinds PHP 8.2.0 |

In de praktijk beslist vaak het framework of CMS. Veel ervan brengen een eigen databaselaag mee, die intern PDO of mysqli gebruikt. Wie een toepassing overneemt, kan met een eenvoudige zoekopdracht in de broncode vaststellen welke interface wordt gebruikt en waar query's ontstaan:

```bash
# Waar bouwt de toepassing verbindingen op, waar ontstaan query's?
grep -rn --include='*.php' -E 'new PDO|new mysqli|mysqli_connect|->query\(' /var/www/app/src | head -50
```

Vindplaatsen met `->query(` zijn niet automatisch onveilig. Ze verdienen echter een tweede blik, omdat daar waarden rechtstreeks in de SQL-tekst kunnen belanden in plaats van via plaatsaanduidingen te worden doorgegeven. Juist oudere projecten mengen beide stijlen, en de controle loont vóór elke grotere update.

## Prepared statements: het belangrijkste patroon

Bij een prepared statement wordt de query met plaatsaanduidingen naar de database gestuurd en worden de waarden afzonderlijk doorgegeven. Volgens de PHP-handleiding beschermt dat tegen SQL-injectie, als alle gebruikersinvoer via plaatsaanduidingen loopt.

```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";
}
```

Hetzelfde met mysqli in de verkorte vorm die er sinds PHP 8.2 is:

```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";
}
```

Twee grenzen moeten ontwikkelaars kennen. Plaatsaanduidingen mogen niet tussen aanhalingstekens staan; bij `LIKE`-zoekopdrachten horen de jokertekens `%` in de doorgegeven waarde. En plaatsaanduidingen vervangen alleen waarden, geen tabel- of kolomnamen. Hoe men zulke bijzondere gevallen veilig oplost, toont de gids [SQL-injectie in PHP voorkomen](https://phpsos.de/nl/ratgeber/sql-injection-php/).

## Foutafhandeling: exceptions in plaats van stille fouten

Vroeger werkten beide interfaces standaard stil: een mislukte query leverde `false` op, en wie dat niet controleerde, werkte met lege gegevens verder. Dat is veranderd:

- **PDO:** Sinds PHP 8.0.0 is `PDO::ERRMODE_EXCEPTION` de standaard. Fouten gooien een `PDOException`.
- **mysqli:** Sinds PHP 8.1.0 staat de foutrapportage op `MYSQLI_REPORT_ERROR` en `MYSQLI_REPORT_STRICT`. Fouten gooien een `mysqli_sql_exception`; daarvoor was de rapportage uitgeschakeld.

Voor de beveiliging betekent dit: exceptions moeten worden afgevangen en vastgelegd, maar mogen nooit met SQL-tekst, tabelnamen of toegangsgegevens in de browser belanden. Hoe men dat netjes scheidt, beschrijft de pagina [Fouten en exceptions in PHP](https://phpsos.de/nl/php/fehler-und-exceptions/).

## Toegangsgegevens en rechten juist toewijzen

De database is maar zo goed beschermd als haar toegangsgegevens. Deze regels hebben zich bewezen:

1. **Eigen gebruiker per toepassing:** Gebruik nooit de beheerder van de database voor de website.
2. **Alleen noodzakelijke rechten:** Een website heeft meestal lezen, schrijven, wijzigen en verwijderen in haar eigen database nodig, maar geen rechten op andere databases of serverfuncties.
3. **Geen verbinding op afstand zonder reden:** De database moet alleen vanaf de webserver bereikbaar zijn, niet vanaf het internet.
4. **Toegangsgegevens buiten de webroot:** Configuratie- en `.env`-bestanden mogen niet via URL opvraagbaar zijn. Meer daarover in het artikel [Toegangsgegevens en .env-bestanden beschermen](https://phpsos.de/nl/blog/env-dateien-schuetzen/).
5. **Versleutelde verbinding bij gescheiden servers:** Staat de database op een andere machine, dan moet de verbinding versleuteld lopen, zodat toegangsgegevens en inhoud niet in klare tekst over het netwerk gaan.
6. **Eigen toegangen voor onderhoud:** Back-up, analyses en beheerders krijgen eigen gebruikers. Zo is in het log na te gaan wie wat heeft gedaan, en kan een toegang worden geblokkeerd zonder de website te verstoren.

## Databases, PHP2Shell en de noodsituatie

PHP2Shell raakt niet de database zelf, maar de PHP-interpreter: CVE-2026-109618 en CVE-2026-109619 maken het uitvoeren van schadelijke code zonder aanmelding mogelijk. Lukt dat, dan draait de code met de rechten van het PHP-proces en kan daarmee in de regel het configuratiebestand met de databasetoegangsgegevens lezen. Prepared statements helpen daartegen niet, omdat de aanval helemaal niet via een SQL-query loopt.

Verplicht is daarom de update naar PHP 8.5.12, 8.4.27, 8.3.36 of 8.2.35 en de herstart van alle PHP-processen. Zijn er aanwijzingen voor een geslaagde aanval, dan horen nieuwe databasetoegangsgegevens, een controle van de database op ingebrachte inhoud en een beoordeling van de meldplicht op grond van de AVG erbij. Een gestructureerde [incident response](https://phpsos.de/nl/leistungen/incident-response/) is hier zinvol.

> **Tip:**
> Wijzig databasewachtwoorden pas na de opschoning van de server. Zolang schadelijke code draait, kan die ook nieuwe toegangsgegevens weer uitlezen.

## Hoe helpt PHP SOS concreet?

- Wij controleren hoe uw toepassing de database benadert en of gebruikers, rechten en netwerktoegang het minimum benaderen.
- Wij controleren of configuratie- en toegangsbestanden buiten de webroot staan en niet openbaar bereikbaar zijn.
- Na een incident vernieuwen wij de toegangsgegevens in de juiste volgorde en controleren de database op ingebrachte code.
- Wij documenteren de resultaten zo dat functionarissen voor gegevensbescherming en verzekeraars ze kunnen nalopen.


## Veelgestelde vragen

**Moet ik PDO of mysqli gebruiken?**  
Voor nieuwe projecten is PDO meestal de flexibelere keuze, omdat het met veel databases via dezelfde interface werkt. mysqli is zinvol als u uitsluitend MySQL of MariaDB gebruikt en hun speciale functies nodig hebt. Beide zijn veilig als prepared statements consequent worden gebruikt.

**Gooit PDO bij fouten automatisch exceptions?**  
Ja, sinds PHP 8.0.0 is PDO::ERRMODE_EXCEPTION de standaard. Daarvoor gold de stille modus, waarin fouten alleen via errorCode() en errorInfo() op te vragen waren. Oudere toepassingen stellen de modus vaak nog uitdrukkelijk in.

**Kunnen plaatsaanduidingen ook tabel- of kolomnamen vervangen?**  
Nee. Plaatsaanduidingen staan alleen voor waarden. Tabel- en kolomnamen, bijvoorbeeld voor een sortering, moeten tegen een vaste lijst van toegestane namen worden gecontroleerd voordat ze in de query worden gezet.

**Moet ik na PHP2Shell het databasewachtwoord wijzigen?**  
De update alleen vereist dat niet. Zijn er echter aanwijzingen dat de server gecompromitteerd was, dan gelden alle toegangsgegevens in configuratiebestanden als bekend. Dan moet u ze na de opschoning vernieuwen.

## Bronnen

- [PHP-handleiding: PDO prepared statements](https://www.php.net/manual/en/pdo.prepared-statements.php)
- [PHP-handleiding: PDO-foutafhandeling](https://www.php.net/manual/en/pdo.error-handling.php)
- [PHP-handleiding: mysqli::execute_query](https://www.php.net/manual/en/mysqli.execute-query.php)
- [PHP-handleiding: mysqli_driver::$report_mode](https://www.php.net/manual/en/mysqli-driver.report-mode.php)
