„Call to undefined function mysql_connect()“ – Ursache und Lösung
Nach einem PHP-Update meldet die Website „Call to undefined function mysql_connect()“? So stellen Sie alten Code auf mysqli oder PDO um – sicher, mit Prepared Statements.
Kurz gesagt: Die Funktion mysql_connect() und alle anderen mysql_*-Funktionen wurden mit PHP 7.0 entfernt. Läuft Ihr Server mit PHP 7 oder 8, gibt es sie schlicht nicht mehr – daher der Fehler. Zurück zu PHP 5 ist keine Lösung. Der Code muss auf mysqli oder PDO umgestellt werden, am besten gleich mit Prepared Statements.
Meistens kommt der Fehler ohne Vorwarnung: Der Hoster hat die PHP-Version umgestellt, und eine Website oder ein internes Tool, das jahrelang lief, zeigt nur noch eine weiße Seite oder einen „500 Internal Server Error“. Im Fehlerprotokoll steht dann:
PHP Fatal error: Uncaught Error: Call to undefined function mysql_connect()
in /var/www/html/includes/db.php:5
Das betrifft fast immer Code, der vor vielen Jahren geschrieben wurde: selbst programmierte Kundenportale, ältere Shop-Systeme, Buchungsformulare oder Verwaltungstools.
Warum gibt es mysql_connect() nicht mehr?
Die alte MySQL-Erweiterung stammt aus der Anfangszeit von PHP. Sie unterstützt keine Prepared Statements, keine objektorientierte Nutzung und viele neuere MySQL-Funktionen nicht. Seit PHP 5.5 war sie als veraltet markiert, mit PHP 7.0 wurde sie ganz entfernt. Seitdem gibt es zwei Nachfolger, die beide fest zu PHP gehören:
- mysqli – speziell für MySQL und MariaDB, nutzbar prozedural oder objektorientiert.
- PDO – eine einheitliche Schnittstelle für verschiedene Datenbanken, mit klarer Fehlerbehandlung.
Beide funktionieren gut. Für kleine Umstellungen nehme ich oft mysqli, weil die Struktur dem alten Code ähnlicher ist. Für alles, was länger gepflegt werden soll, bevorzuge ich PDO.
Vorher: typischer alter Code
$link = mysql_connect('localhost', 'benutzer', 'passwort');
mysql_select_db('shop', $link);
$id = $_GET['id'];
$result = mysql_query("SELECT name, preis FROM produkte WHERE id = $id");
while ($row = mysql_fetch_assoc($result)) {
echo $row['name'];
}
Dieser Code hat zwei Probleme. Das erste ist offensichtlich: Er läuft unter PHP 7 und 8 nicht mehr. Das zweite ist gefährlicher und war schon immer da – dazu gleich mehr.
Nachher: mit mysqli und Prepared Statement
$db = new mysqli('localhost', 'benutzer', 'passwort', 'shop');
$db->set_charset('utf8mb4');
$id = (int) ($_GET['id'] ?? 0);
$stmt = $db->prepare('SELECT name, preis FROM produkte WHERE id = ?');
$stmt->bind_param('i', $id);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
echo htmlspecialchars($row['name']);
}
Das Fragezeichen ist ein Platzhalter. Der Wert wird getrennt von der SQL-Abfrage an die Datenbank geschickt und kann deren Aufbau nicht verändern. Das 'i' in bind_param steht für Integer, 's' wäre ein Text. Für get_result() muss der Treiber mysqlnd installiert sein, was bei üblichen Hosting-Paketen der Fall ist.
Nachher: mit PDO
$pdo = new PDO(
'mysql:host=localhost;dbname=shop;charset=utf8mb4',
'benutzer',
'passwort',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
$stmt = $pdo->prepare('SELECT name, preis FROM produkte WHERE id = :id');
$stmt->execute(['id' => (int) ($_GET['id'] ?? 0)]);
foreach ($stmt as $row) {
echo htmlspecialchars($row['name']);
}
Benannte Platzhalter wie :id machen längere Abfragen gut lesbar. Durch ERRMODE_EXCEPTION bricht der Code bei einem Datenbankfehler mit einer klaren Meldung ab, statt still weiterzulaufen.
Die wichtigsten Entsprechungen
mysql_query()→$db->query()bzw.$db->prepare()/$pdo->prepare()mysql_fetch_assoc()→$result->fetch_assoc()/$stmt->fetch()mysql_num_rows()→$result->num_rows(bei PDO besser perCOUNT(*)abfragen)mysql_insert_id()→$db->insert_id/$pdo->lastInsertId()mysql_real_escape_string()→ entfällt, stattdessen Prepared Statementsmysql_error()→ Exceptions bzw.$db->error
Das eigentliche Risiko: SQL-Injection im alten Code
Zurück zum alten Beispiel. Die Zeile WHERE id = $id übernimmt den Wert aus der Adresszeile ungeprüft in die Abfrage. Ruft jemand die Seite mit ?id=1 OR 1=1 auf, liefert die Datenbank alle Datensätze. Mit etwas mehr Aufwand lassen sich so auch andere Tabellen auslesen – etwa Kundendaten oder Passwort-Hashes.
Viele alte Projekte nutzen mysql_real_escape_string() und gelten deshalb als „abgesichert“. Das stimmt nur teilweise. Die Funktion schützt nur Werte, die in Anführungszeichen stehen. Bei Zahlen ohne Anführungszeichen, wie im Beispiel oben, hilft sie gar nicht. Und es reicht eine einzige vergessene Stelle.
Deshalb rate ich davon ab, bei der Umstellung nur die Funktionsnamen auszutauschen. Wenn Sie den Code ohnehin anfassen müssen, setzen Sie gleich Prepared Statements ein. Der Mehraufwand ist gering, der Gewinn an Sicherheit groß.
Ein Punkt, an dem Prepared Statements nicht helfen: Tabellen- und Spaltennamen, zum Beispiel in ORDER BY. Die lassen sich nicht als Platzhalter übergeben. Hier prüfen Sie den Wert gegen eine feste Liste erlaubter Spalten.
Typische Fehler bei der Umstellung
- Suchen und Ersetzen von mysql_ zu mysqli_: Das klappt nicht. Bei vielen mysqli-Funktionen steht die Verbindung als erster Parameter, also
mysqli_query($link, $sql)stattmysql_query($sql, $link). Außerdem nutzt mysqli keine „zuletzt geöffnete Verbindung“ mehr automatisch. - Zeichensatz vergessen: Ohne
set_charset('utf8mb4')bzw.charset=utf8mb4erscheinen Umlaute plötzlich als Fragezeichen oder kaputte Zeichen. - Verbindung in Funktionen: Alter Code verlässt sich oft darauf, dass
mysql_query()ohne Verbindungsparameter funktioniert. Mit mysqli und PDO muss die Verbindung in jede Funktion übergeben werden. - Kompatibilitäts-Schichten als Dauerlösung: Es gibt Bibliotheken, die die alten
mysql_*-Funktionen mit mysqli nachbauen. Als Notlösung für ein paar Tage kann das helfen. Die Sicherheitslücken bleiben damit aber genau so, wie sie waren. - Nur die Startseite testen: Datenbankabfragen verteilen sich oft über Dutzende Dateien. Testen Sie Formulare, Login, Suche, Export und Admin-Bereich.
So gehen Sie vor
- Sichern Sie Dateien und Datenbank vollständig.
- Suchen Sie alle Stellen:
grep -rn "mysql_" --include=*.php .zeigt jede betroffene Zeile. - Legen Sie eine Testkopie mit der neuen PHP-Version an.
- Bauen Sie eine zentrale Datenbankverbindung (eine Datei, ein Objekt) und stellen Sie die Abfragen Datei für Datei um.
- Prüfen Sie das Fehlerprotokoll nach jedem Schritt und testen Sie alle Funktionen.
- Erst dann spielen Sie die Änderungen live ein.
Wann Sie Hilfe brauchen
Ein Skript mit fünf Abfragen stellen Sie mit dieser Anleitung an einem Nachmittag um. Bei einer gewachsenen Anwendung mit Hunderten Abfragen, verstreuter Logik und ohne Dokumentation sieht das anders aus. Dazu kommt: Wo noch mysql_* steckt, finden sich oft weitere Funktionen, die in neueren PHP-Versionen fehlen, etwa each() oder create_function(), die mit PHP 8.0 entfernt wurden.
In solchen Fällen schaue ich mir den Code zuerst an und sage Ihnen, ob sich eine Umstellung lohnt oder ob Teile neu gebaut werden sollten. Wie ich dabei vorgehe, steht unter Altsystem modernisieren. Geht es vor allem darum, die Anwendung auf eine aktuelle PHP-Version zu bringen, finden Sie Ablauf und Preise unter PHP-Update (ab 690 €).