Current situation: PHP2Shell. Two critical PHP vulnerabilities (CVSS 9.2) allow malicious code without login. Patches since 21 October 2026.What to do now →

PHP and Databases: PDO and mysqli

PHP talks to databases through PDO or mysqli. Here you will see the differences, secure queries with prepared statements and what to do about database credentials after a server incident.

Last updated 5 min readPHP knowledge

In short: PHP accesses databases through two widely used interfaces: PDO (PHP Data Objects) works with many database systems through a uniform interface, while mysqli is tailored to MySQL and MariaDB. With both, prepared statements, that is, queries with placeholders, reliably protect against SQL injection. Since PHP 8.0 (PDO) and 8.1 (mysqli) respectively, both report errors as exceptions by default.

How PHP talks to databases

Almost every PHP application stores its content in a database: pages, orders, user accounts, settings. For this the PHP script establishes a connection, sends a query in the SQL language and processes the result. The credentials are usually in a configuration file on the same server.

This matters for management because the database usually holds the company's most valuable data. Anyone who can execute code on the web server can generally also read the configuration file and use these credentials to access the database.

PDO and mysqli compared

Feature PDO mysqli
Databases many, via drivers such as PDO_MYSQL, PDO_PGSQL, PDO_SQLITE, PDO_SQLSRV, PDO_OCI MySQL and MariaDB
Placeholders named (:name) and positional (?) positional (?)
Style object-oriented object-oriented and procedural
Errors as exceptions default since PHP 8.0.0 default since PHP 8.1.0
Shorthand for queries with parameters prepare() and execute() with an array execute_query() since PHP 8.2.0

In practice the framework or CMS often decides. Many bring their own database layer that uses PDO or mysqli internally. Anyone taking over an application can use a simple search of the source code to find out which interface is used and where queries originate:

# Where does the application open connections, where do queries originate?
grep -rn --include='*.php' -E 'new PDO|new mysqli|mysqli_connect|->query\(' /var/www/app/src | head -50

Hits with ->query( are not automatically insecure. But they deserve a second look, because values can end up directly in the SQL text there instead of being passed via placeholders. Older projects in particular mix both styles, and the check is worthwhile before any major update.

Prepared statements: the most important pattern

With a prepared statement, the query is sent to the database with placeholders and the values are passed separately. According to the PHP manual, this protects against SQL injection if all user input runs through placeholders.

<?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";
}

The same with mysqli in the short form available since PHP 8.2:

<?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";
}

Developers should know two limits. Placeholders must not be placed in quotation marks; with LIKE searches, the wildcard characters % belong in the value passed. And placeholders replace only values, not table or column names. How to solve such special cases safely is shown in the guide Preventing SQL injection in PHP.

Error handling: exceptions instead of silent errors

In the past, both interfaces worked silently by default: a failed query returned false, and anyone who did not check for it carried on working with empty data. That has changed:

  • PDO: Since PHP 8.0.0, PDO::ERRMODE_EXCEPTION is the default. Errors throw a PDOException.
  • mysqli: Since PHP 8.1.0, error reporting is set to MYSQLI_REPORT_ERROR and MYSQLI_REPORT_STRICT. Errors throw a mysqli_sql_exception; before that, reporting was switched off.

For security this means: exceptions must be caught and logged, but must never end up in the browser with SQL text, table names or credentials. How to separate this cleanly is described on the page Errors and exceptions in PHP.

Assigning credentials and privileges correctly

The database is only as well protected as its credentials. These rules have proven their worth:

  1. Separate user per application: Never use the database administrator for the website.
  2. Only the necessary privileges: A website usually needs to read, write, change and delete in its own database, but no privileges on other databases or server functions.
  3. No remote connection without reason: The database should be reachable only from the web server, not from the internet.
  4. Credentials outside the web root: Configuration and .env files must not be retrievable via URL. More on this in the article Protecting credentials and .env files.
  5. Encrypted connection with separate servers: If the database is on another machine, the connection should be encrypted so that credentials and content do not travel across the network in plain text.
  6. Separate access for maintenance: Backups, reports and administrators get their own users. That way the log shows who did what, and an account can be blocked without disturbing the website.

Databases, PHP2Shell and the worst case

PHP2Shell does not affect the database itself but the PHP interpreter: CVE-2026-109618 and CVE-2026-109619 allow the execution of malicious code without authentication. If that succeeds, the code runs with the privileges of the PHP process and can generally read the configuration file with the database credentials. Prepared statements do not help against this, because the attack does not run through an SQL query at all.

The update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 and the restart of all PHP processes are therefore mandatory. If there are indications of a successful attack, new database credentials, a check of the database for injected content and an assessment of reporting obligations under the GDPR are part of the response. Structured incident response makes sense here.

Tip

Change database passwords only after the server has been cleaned up. As long as malicious code is running, it can read out new credentials again.

How does PHP SOS help?

  • We check how your application accesses the database and whether users, privileges and network access meet the minimum necessary.
  • We verify that configuration and credential files are outside the web root and not publicly accessible.
  • After an incident we renew the credentials in the right order and check the database for injected code.
  • We document the results so that data protection officers and insurers can follow them.

Frequently asked questions

Should I use PDO or mysqli?

For new projects, PDO is usually the more flexible choice, because it works with many databases through the same interface. mysqli makes sense if you use only MySQL or MariaDB and need their special features. Both are secure if prepared statements are used consistently.

Does PDO automatically throw exceptions on errors?

Yes, since PHP 8.0.0 PDO::ERRMODE_EXCEPTION is the default. Before that it was the silent mode, in which errors could only be retrieved via errorCode() and errorInfo(). Older applications often still set the mode explicitly.

Can placeholders also replace table or column names?

No. Placeholders stand only for values. Table and column names, for example for sorting, must be checked against a fixed list of permitted names before they are inserted into the query.

Do I have to change the database password after PHP2Shell?

The update alone does not require it. But if there are indications that the server was compromised, all credentials in configuration files must be considered known. In that case you should renew them after clean-up.

Sources

Immediate contact

Every hour counts. Write to us now.

Briefly describe your system, PHP version and observation. We will get back to you, clarify the situation and work remotely until the incident is closed and documented.

Immediate help