Navigation
 Startseite
 Fachbücher
 Anzeigenmarkt
 Forum
 Webmaster News
 Script Newsletter
 Kontakt
 Script Installation
 Php
 Php Tutorials
 Lernpfade
 Webhoster Vergleich
 Impressum

Community-Bereich
 kostenlos Registrieren
 Anmelden
 Benutzerliste

Script Datenbank
 Script Archiv
 Script Top 20
 Screenshots
 Testberichte

Suche
 

Unsere Php Scripts
 Counter Script
 Umfrage Script
 Bilder Upload Script
 Terminverwaltung
 Simple PHP Forum
 RSS Grabber

Tools und Generatoren
 .htpasswd Generator
 md5 Generator
 base64 Generator
 Markdown to HTML
 Colorpicker
 Unix timestamp Tool
 Unit Test Generator
 TLD Liste
 Webkatalog‑Verzeichnis

Artfiles.de
Bietet Serviceorientierte Internetdienstleistungen...
https://www.Artfiles.de
Hosterplus.de
Bekommen Sie Speicherplatz (Webspace), Domains und...
https://www.Hosterplus.de
 
 
 

PHP mysqli Prepared Statements: Bind, Execute, Get-Result

Sie befinden sich: Home > Php Tutorial > PHP mysqli Prepared Stateme...

PHP mysqli Prepared Statements: Bind, Execute, Get-Result
Eintrag am:
19.07.2026
Hits / Besucher:
23
Sprache:
  Deutsch
Tutorial Art:
eigenes
Eingetragen von:
Merkliste:
 
Beschreibung

Datenbankabfragen mit Benutzereingaben sind eines der häufigsten Einfallstore für Angriffe. Wer in PHP auf die mysqli-Erweiterung setzt, bekommt mit vorbereiteten Anweisungen ein Werkzeug, das SQL-Injection zuverlässig ausschliesst und Abfragen zugleich effizienter macht. Dieser Beitrag erklärt den kompletten Ablauf von der Vorbereitung bis zum Auslesen der Ergebnisse.

Was sind PHP mysqli Prepared Statements?

Wer in PHP gegen MySQL oder MariaDB arbeitet und nicht auf PDO setzt, landet schnell bei der mysqli-Erweiterung. Mit PHP mysqli Prepared Statements lassen sich SQL-Abfragen sicher formulieren und SQL Injection ausschließen, weil Wert und Anweisung strikt getrennt werden. Die Datenbank erhält zuerst ein Statement-Template mit Platzhaltern, dann die Werte, und kombiniert beides intern. Ein kompaktes Beispiel zeigt das Pattern in wenigen Zeilen.

Illustration zum Tutorial: PHP mysqli Prepared Statements: Bind, Execute, Get-Result

Bevor der vierstufige Lebenszyklus mit prepare, bind_param, execute und fetch im Detail folgt, ein kurzer Blick auf den doppelten Nutzen dieses Patterns.

Der Vorteil ist doppelt: zum einen kann ein Angreifer keine SQL-Befehle über Eingabefelder einschmuggeln, zum anderen kann ein einmal vorbereitetes Statement mit immer neuen Werten wiederverwendet werden. Genau das macht PHP mysqli Prepared Statements zur Standardlösung für alles, was nicht trivialer Static-Content ist.

Lebenszyklus von PHP mysqli Prepared Statements: prepare, bind_param, execute, fetch

Der typische Ablauf von PHP mysqli Prepared Statements besteht aus vier Schritten. Zuerst wird eine Vorlage mit Fragezeichen vorbereitet, dann werden die Werte gebunden, dann wird das Statement ausgeführt und schließlich werden die Ergebnisse abgeholt. Der folgende Codeabschnitt zeigt das alles in OOP-Schreibweise.

<?php

$mysqli = new mysqli('localhost', 'user', 'pass', 'shop');
$mysqli->set_charset('utf8mb4');

$stmt = $mysqli->prepare('SELECT id, name FROM kunde WHERE land = ? AND aktiv = ?');
$stmt->bind_param('si', $land, $aktiv);
$land = 'DE';
$aktiv = 1;
$stmt->execute();

$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
echo $row['id'] . ': ' . $row['name'] . "\n";
}
$stmt->close();

Wichtig: bind_param() erwartet die Variablen by reference, nicht ihre Werte. Deshalb funktioniert es nur, wenn die Variable existiert, wenn der Aufruf erfolgt. Und der Type-String 'si' muss in Länge und Reihenfolge zu den Platzhaltern passen.

SELECT mit get_result und fetch_assoc

Für SELECT-Abfragen ist get_result() der bequemste Weg. Die Methode liefert ein mysqli_result-Objekt, aus dem sich Zeile für Zeile als assoziatives Array lesen lässt. Voraussetzung ist der mysqlnd-Treiber, der in modernen PHP-Distributionen standardmäßig aktiv ist.

<?php

$stmt = $mysqli->prepare('SELECT id, name, email FROM kunde WHERE land = ?');
$land = 'DE';
$stmt->bind_param('s', $land);
$stmt->execute();
$result = $stmt->get_result();
$alle = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
print_r($alle);

fetch_all(MYSQLI_ASSOC) ist praktisch, wenn das Ergebnis komplett ins RAM passt. Bei sehr großen Result-Sets ist die Schleife mit fetch_assoc() schonender, weil pro Iteration nur eine Zeile gehalten wird. Wer mit JSON-APIs arbeitet, kann das Resultat direkt durch json_encode() schicken und an den Client zurückliefern.

INSERT, UPDATE und DELETE mit Prepared Statements

Bei schreibenden Operationen liefert das Statement-Objekt nützliche Metadaten. insert_id enthält die ID des zuletzt eingefügten Datensatzes (sofern auto_increment), affected_rows gibt die Anzahl der betroffenen Zeilen zurück.

<?php

$stmt = $mysqli->prepare('INSERT INTO kunde (name, land, aktiv) VALUES (?, ?, ?)');
$stmt->bind_param('ssi', $name, $land, $aktiv);
$name = 'Erika Musterfrau';
$land = 'DE';
$aktiv = 1;
$stmt->execute();
echo 'Neue Kunden-ID: ' . $stmt->insert_id;
$stmt->close();

Soll dieselbe Vorlage gleich mehrfach mit unterschiedlichen Werten ausgeführt werden, lohnt sich die Wiederverwendung. PHP mysqli Prepared Statements bereiten die Vorlage einmal vor, danach kostet jeder weitere Aufruf nur noch das Binden und Ausführen.

<?php

$stmt = $mysqli->prepare('INSERT INTO log (msg, level) VALUES (?, ?)');
$stmt->bind_param('si', $msg, $level);
foreach ($eintraege as [$msg, $level]) {
$stmt->execute();
}
$stmt->close();

Type-Spezifikatoren s, i, d und b in PHP mysqli Prepared Statements

Der Type-String in PHP mysqli Prepared Statements legt fest, wie jeder Wert intern interpretiert wird. Vier Codes sind möglich. Ein s steht für String, i für Integer, d für Double und b für Blob (binary data, wird per send_long_data() gesendet). Beispiele finden sich in der folgenden Übersicht.

<?php

/* SELECT mit allen vier Typen */
$stmt = $mysqli->prepare('
SELECT id FROM dokument
WHERE titel = ?
AND seitenzahl = ?
AND preis <= ?
AND inhalt LIKE ?
');
$stmt->bind_param('sids', $titel, $seitenzahl, $preis, $like);
$titel = 'Handbuch';
$seitenzahl = 120;
$preis = 19.99;
$like = '%PHP%';
$stmt->execute();

Wichtig: nur Werte können über Platzhalter im Statement gebunden werden, ein Tabellen- oder Spaltenname hingegen nicht. Wer Spaltennamen aus User-Input ableitet, muss sie etwa über eine Whitelist prüfen. Mehr dazu im Tutorial PHP SQL-Injection verhindern.

Prozedurale vs. objektorientierte API

mysqli bietet beide Programmierstile parallel an. Die OOP-Variante ist heute der Standard, in alten Codebasen findet sich aber oft noch der prozedurale Stil. Beide bilden denselben Lebenszyklus ab, nur die Schreibweise unterscheidet sich.

<?php

$mysqli = mysqli_connect('localhost', 'user', 'pass', 'shop');
$stmt = mysqli_prepare($mysqli, 'SELECT id FROM kunde WHERE name = ?');
mysqli_stmt_bind_param($stmt, 's', $name);
$name = 'Max';
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
while ($row = mysqli_fetch_assoc($result)) {
echo $row['id'] . "\n";
}
mysqli_stmt_close($stmt);

Für neuen Code ist die OOP-Variante die bessere Wahl, weil sie kürzer und besser lesbar ist. Wer ein Legacy-System pflegt, sollte sich allerdings nicht zwingen, sofort umzustellen, denn beide Stile arbeiten gegen dieselbe Datenbank-Engine und liefern identische Ergebnisse.

Dynamische IN-Listen mit str_repeat

Eine klassische Stolperfalle: SQL kennt keinen Platzhalter für eine variable Anzahl von Werten. Wer ein WHERE id IN (?, ?, ?) braucht, muss die Platzhalter dynamisch erzeugen und passend binden. Der folgende Code nutzt str_repeat() und den Spread-Operator, was den Aufruf sehr kompakt macht.

<?php

$ids = [12, 34, 56];
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$types = str_repeat('i', count($ids));

$stmt = $mysqli->prepare("SELECT id, name FROM artikel WHERE id IN ($placeholders)");
$stmt->bind_param($types, ...$ids);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
echo $row['name'] . "\n";
}
$stmt->close();

Diese Konstruktion sieht auf den ersten Blick komplex aus, ist aber sicher. Die Werte aus $ids werden weiterhin getrennt vom SQL-Template übergeben, das verhindert SQL-Injection trotz des dynamischen Anteils. Die beiden Helper str_repeat() und array_fill() sind in der PHP-Dokumentation ausführlich beschrieben und tauchen in vielen DB-Helpern für dynamische Listen wieder auf.

bind_result als Alternative zu get_result

get_result() setzt den mysqlnd-Treiber voraus. Auf den meisten heutigen PHP-Builds ist er Standard, aber es gibt noch ältere Shared-Hostings oder spezielle Distributionen, die mit dem klassischen libmysqlclient ausgeliefert werden. Dort steht get_result() nicht zur Verfügung, und die Spalten müssen einzeln per bind_result() an Variablen gebunden werden.

<?php

$stmt = $mysqli->prepare('SELECT id, name, email FROM kunde WHERE land = ?');
$land = 'DE';
$stmt->bind_param('s', $land);
$stmt->execute();

/* Jede Spalte explizit an eine Variable binden */
$stmt->bind_result($id, $name, $email);

while ($stmt->fetch()) {
echo "$id | $name | $email" . PHP_EOL;
}
$stmt->close();

Der Code ist umständlicher, weil die Reihenfolge und Anzahl der Variablen exakt zur SELECT-Liste passen muss. Dafür funktioniert er auch ohne mysqlnd. Wer eine Bibliothek schreibt, die in Legacy-Hosting laufen soll, hat mit einem method_exists($stmt, 'get_result')-Check und einem Fallback auf bind_result einen sauberen Weg.

Server-side Statement-Handles und korrektes Schliessen

Jedes vorbereitete Statement belegt auf dem MySQL-Server einen sogenannten Statement-Handle. Solange der Handle offen ist, hält der Server das Statement-Plan-Cache vor. In normalen Web-Requests ist das egal, weil der Prozess am Ende ohnehin alles aufräumt. In CLI-Skripten oder Worker-Prozessen, die zehntausende Inserts in einer Schleife absetzen und dabei pro Iteration ein neues prepare() machen, summiert sich das schnell.

<?php

/* Antipattern: pro Schleifendurchlauf neues Statement */
foreach ($eintraege as $eintrag) {
$stmt = $mysqli->prepare('INSERT INTO log (msg) VALUES (?)');
$stmt->bind_param('s', $eintrag);
$stmt->execute();
/* close() vergessen: Server-Handle bleibt offen */
}

/* Besser: einmal prepare, im Loop nur execute, am Ende close */
$stmt = $mysqli->prepare('INSERT INTO log (msg) VALUES (?)');
$stmt->bind_param('s', $msg);
foreach ($eintraege as $msg) {
$stmt->execute();
}
$stmt->close();

Ein vergessenes close() führt spätestens dann zu sichtbaren Problemen, wenn das Server-Limit max_prepared_stmt_count erreicht ist und neue Prepares mit Fehlermeldung scheitern. Wer mit PHP 8.1 oder neuer arbeitet, profitiert zusätzlich davon, dass mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) standardmäßig aktiv ist und solche Fehler als mysqli_sql_exception wirft, statt sie still zu schlucken. Migrations-Skripte, die alten mysql_query-Code auf Prepared Statements umstellen, sollten genau diese Disziplin (einmal prepare, im Loop execute, am Ende close) als Leitlinie haben.

mysqli vs PDO im Vergleich

mysqli ist auf MySQL und MariaDB festgelegt, PDO unterstützt verschiedene Datenbanken hinter einer einheitlichen API. Wer nur MySQL einsetzt, hat mit mysqli minimal mehr Kontrolle über spezifische Features wie multi_query oder asynchrone Abfragen. Wer dagegen Wert auf Wechselbarkeit, einheitliche Fehlerbehandlung und benannte Platzhalter legt, ist mit PDO besser beraten.

In neuen Projekten fällt die Wahl meist auf PDO, weil seine API konsistenter ist und der Wechsel zwischen MySQL und Postgres in Tests oder bei Migrationen leichter fällt. Bestehende mysqli-Projekte müssen aber nicht zwingend portiert werden. PHP mysqli Prepared Statements sind weiterhin sicher und schnell, solange das Pattern korrekt eingehalten wird.

flowchart TD
    A[prepare SQL mit Platzhaltern] --> B[bind_param mit Type-String]
    B --> C[execute]
    C --> D{SELECT?}
    D -->|Ja| E[get_result + fetch_assoc]
    D -->|Nein| F[affected_rows / insert_id]
    E --> G[close]
    F --> G

Fazit zu PHP mysqli Prepared Statements

PHP mysqli Prepared Statements sind das Mittel der Wahl, wenn ein Projekt auf der mysqli-Erweiterung aufbaut. Wer den vierstufigen Lebenszyklus aus prepare, bind_param, execute und fetch verinnerlicht und die Type-Spezifikatoren s, i, d, b korrekt setzt, schreibt robusten Code, der gegen SQL-Injection geschützt ist. Mit der Wiederverwendung im Bulk-Insert und der dynamischen IN-Liste decken PHP mysqli Prepared Statements auch fortgeschrittene Anwendungsfälle. Für neue Projekte mit möglicher Datenbank-Vielfalt bleibt PDO die flexiblere Alternative, für reine MySQL-Setups ist mysqli weiterhin eine schlanke und schnelle Wahl.

 

Tags:

 

 

Kommentare (0)

Noch keine Kommentare. Sei der Erste!

Melde dich an, um einen Kommentar zu schreiben.
Bücherregal mit drei Büchern: 'PHP 4 - Grundlagen und Profiwissen' von Hanser Verlag, 'Webdesign in a Nutshell' von O'Reilly Verlag, und 'Webgestaltung' von Galileo Computing.