13. MySQL-Datenbank Zugriff mit PHP

Du hast in diesem Tutorial bereits gelernt, wie Du mit Arrays komplexere Daten für die Dauer des Skriptablaufs speichern kannst. Auch weißt Du, wie Du Daten in einer Datei speichern kannst.

Doch beide Lösungen reichen häufig nicht aus. Insbesondere dann, wenn es um große Datenmengen geht, die direkt angesprochen werden sollen. Etwa dann wenn Nutzerdaten inklusive Name, E-Mail und letztem Login-Datum gespeichert werden sollen.

In diesem letzten und dreizehnten Teil des PHP Tutorials zeige ich Dir, wie Du mit Hilfe von PDO auf MySQL-Datenbanken zugreifen kannst.

Der PDO-Treiber für PHP

Jetzt habe ich gleich zwei unbekannte Begriffe eingeführt, nicht nur MySQL, sondern auch noch PDO. Deswegen möchte ich Dir kurz erklären, was es damit auf sich hat.

MySQL kannst Du dir wie eine Software vorstellen, die auf deinem Server läuft. Diese nimmt Befehle entgegen und verarbeitet diese intern in selbstorganisierten Dateien. MySQL ist daher die Bezeichnung für eine Datenbank-Form.

Auf diese möchten wir mit PHP zugreifen. Bis vor Kurzem gab es dazu eine Lösung, die mit Hilfe von PHP-Funktionen wie mysql_connect() funktionierte. Doch diese wurde als veraltet markiert und sollte nicht mehr verwendet werden.

Viele der Webserver, die MySQL unterstützen, verfügen auch über ein installiertes PDO. Eine Alternative zu PDO ist MySQLi, mit welchem wir uns in dem Artikel jedoch nicht beschäftigen wollen.

Denn der Vorteil von PDO, quasi einer Klasse von Befehlen für PHP, um mit Datenbanken zu interagieren ist, dass Du auch leicht auf andere Datenbanken zugreifen kannst. Solltest Du also irgendwann eine PostgreSQL-Datenbank verwenden wollen, sind nur geringfügige Änderungen notwendig.

Ein weiterer Vorteil: Mit PDO und den sogenannten Prepared Statements können SQL Injections wirkungsvoll verhindert werden. Bei einer sogenannten SQL Injection gelingt es dem Besucher ungeprüften Code in die Datenbank einzuschleusen, sodass er die Datenbank womöglich sogar aus der Ferne übernehmen kann. Der Alptraum eines jeden Webmasters.

Bei PDO werden im Folgenden statt Benutzereingaben ein ? einsetzen. Erst anschließend übergeben wir als Array alle Parameter. PDO weiß so, dass diese keine MySQL-Befehle beinhalten dürfen und kann außerdem dafür sorgen, dass gleiche Befehle mit unterschiedlichen Parametern schneller ausgeführt werden.

Verbindung zur MySQL Datenbank

Bevor wir mit einer MySQL-Datenbank arbeiten können, müssen wir uns zunächst mit ihr verbinden. Darauf brauchen wir die Zugangsdaten.

Wenn Dein Webhoster MySQL unterstützt, wird er Dir entweder die Datenbank-Daten bei deiner Registrierung mitgeteilt haben oder Dir ein Admin-Interface bereitstellen, wo Du neue Datenbanken anlegen kannst. Du benötigst:

  • Host (in der Regel localhost)
  • ggf. einen extra Port
  • Name der Datenbank
  • Name des Datenbank-Nutzers
  • Passwort des Datenbank-Nutzers

Um Fehler bei der Verbindung abzufangen, verwenden wir einen try-catch-Block in PHP. Der Code im try-Block wird solange ausgeführt, bis ein Fehler auftritt, dieser wird dann abgefangen und der Code aus dem catch-Block ausgeführt.

So stellen wir mit PDO eine Verbindung zur MySQL-Datenbank her (port-Angabe ggf. weglassen, wenn nicht benötigt):

<?php
  try {
    $sqlDB = new PDO("mysql:host=LOCALHOST;port=MYSQL_PORT;dbname=DB_NAME", DB_USER, DB_PASSWORD array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
  }
	
  catch (PDOException $db_error) {
    die("<h2>Es konnte keine Verbindung zur Datenbank hergestellt werden.</h2><p>".$dbError -> getMessage()."</p>");
  }
?>

Dabei solltest Du die angesprochenen Angaben so anpassen, dass Du dich mit deiner Datenbank verbinden kannst. Führst Du das Skript auf, sollte keine Fehlermeldung erscheinen, dann war die Verbindung mit der Datenbank erfolgreich.

Bei diesem Code sind noch zwei Dinge nennenswert. Zum einen stellen wir eine Verbindung mit dem Zeichensatz UTF-8 her. Das funktioniert nur dann reibungslos, wenn deine PHP-Dateien mit dem Zeichensatz UTF-8 arbeiten und als UTF-8-Datei gespeichert worden. UTF-8 hat den Vorteil, dass Zeichen aus den unterschiedlichsten Sprachräumen leicht wiedergegeben werden können.

Zum zweiten spielt die lokale Variable $sqlDB eine wichtige Rolle. Diese werden wir in Zukunft benötigen, um auf die MySQL-Datenbank zugreifen zu können, weil ihr das PDO-Objekt zugewiesen wurde.

Da diese Variable lokal ist, sollte sie an Klassen oder Funktionen übergeben werden, wenn diese mit der Datenbank arbeiten sollen.

Die PDO-Syntax

Nehmen wir einfach an, Du hast bereits einige Tabellen (jede Datenbank kann mehrere Tabellen enthalten), zum Beispiel mit PHPMyAdmin, erstellt und möchtest nun einen Datensatz einfügen.

Da jede Tabelle über verschiedene Spalten verfügt und Datensätze, die für jede Spalte einen eigenen Inhalt bereit halten, kannst Du, wenn Du die Spaltennamen kennst, ganz einfach einen neuen Datensatz erstellen:

$query = $sqlDB -> prepare('INSERT INTO tabelle1 (nutzername, email) VALUES (?, ?)'); // Query vorbereiten
$query -> execute(array('Peter', $_POST['email'])); // Query ausführen

Mit prepare bereiten wir zunächst den SQL-Befehl vor. Dabei verwenden wir für alle Daten-Angaben ein ? Später übergeben wir via execute in der gleichen Reihenfolge die Argumente als Array. Hast Du also 6 Fragezeichen im prepared Statement verwendet, so muss das Array, was du übergibst, ebenfalls 6 Element beinhalten.

Möchtest Du mehrmals den gleichen Befehl ausführen, aber unterschiedliche Daten übergeben, muss nur das execute-Statement neu ausgeführt werden:

$query = $sqlDB -> prepare('INSERT INTO tabelle1 (nutzername, email) VALUES (?, ?)'); // Query vorbereiten
$query -> execute(array('Peter', $_POST['email'])); // Query ausführen, 1. Datensatz
$query -> execute(array('Ben', 'bens-email@www-coding.de')); // Query ausführen, 2. Datensatz

echo $sqlDB -> lastInsertID(); // wurde 'AUTO_INCREMENT' verwendet, wird die zuletzt eingefügte ID ausgegeben (Könnte später für Dich hilfreich sein)

Auf die gleiche Art und Weise kannst Du auch den MySQL-Befehl zum Löschen (DELETE) ausführen. Eine Übersicht der wichtigsten MySQL-Befehle folgt am Ende dieses Artikels.

Zunächst möchte ich Dir noch zeigen, wie Du Daten aus der MySQL-Datenbank auslesen kannst. Dafür verwenden wir den SELECT-Befehl. Außerdem kommt eine PDO-Zeile dazu, denn wir wollen ja auch das Ergebnis der Abfrage speichern.

$getData = $sqlDB -> prepare('SELECT nutzername, email FROM tabelle1 WHERE nutzername = ? LIMIT 1'); // Query vorbereiten
$getData -> execute(array('Ben')); // Query ausführen, 1. Datensatz
$getData = $getData -> fetch();

Existiert ein solcher Datensatz, wäre $getData jetzt ein Array mit mindestens diesen Elementen:

$getData['nutzername'] => 'Ben'
$getData['email'] => 'bens-email@www-coding.de'

Dementsprechend kannst Du mit diesen Daten weiterarbeiten. Wenn Du von vornherein weißt, dass mehrere Datensätze zutreffen können, kannst Du eine while-Schleife verwenden, um alle Einträge abzufragen:

$getData = $sqlDB -> prepare('SELECT nutzername, email FROM tabelle1'); // Query vorbereiten
$getData -> execute(); // Query ausführen, 1. Datensatz
while ($user = $getData -> fetch()) {
  echo $user['nutzername']." ist der Nutzername und die E-Mail: ".$user['email'];
}

Alternativ kannst Du mit fetchAll auch alle Datensätze in ein Array übertragen. Dabei solltest Du jedoch aufpassen, dass das Array bei riesigen Datenmengen dementsprechend groß werden kann. PHP hat schließlich nur eine begrenze Menge an temporärem Speicher, der verwendet werden kann. So sieht das Ganze mit fetchAll aus:

$getData = $sqlDB -> prepare('SELECT nutzername, email FROM tabelle1'); // Query vorbereiten
$getData -> execute(); // Query ausführen, 1. Datensatz
$getData = $getData -> fetchAll(); // Alle Ergebnisse in $getData speichern


// $getData kann nun wie ein normales Array ausgelesen werden
foreach ($getData AS $user) {
  echo $user['nutzername']." ist der Nutzername und die E-Mail: ".$user['email'];
}

Damit weißt du bereits, wie du auf die wichtigsten Befehle von PDO zugreifen kannst. Gewöhnt man sich erst einmal daran, ist das gar nicht mehr so schwer. Einzig bei den möglichen MySQL-Befehlen kannst Du noch einiges lernen.

Doch eines fehlt noch. Mit PDO können auch die zutreffenden Datensätze gezählt werden. Dafür verwenden wir das COUNT in einem MySQL SELECT-Statement und lesen die Spalte mit PDO aus:

$count = $sqlDB -> prepare('SELECT COUNT(benutzerID) FROM tabelle1'); // Query vorbereiten
$count -> execute(); // Query ausführen, 1. Datensatz $getData = $count = $count -> fetchColumn(); // Anzahl der Ergebnisse in $count speichern

echo $count; // Anzahl der zutreffenden Datensätze

Wichtig dabei ist, dass bei COUNT(spalte), spalte auch tatsächlich existiert. Es kann auch einfach COUNT(*) verwendet werden (* = alle Spalten).

Die wichtigsten MySQL-Befehle

Nun weißt Du bereits, wie Du MySQL-Befehle mit PDO ausführen kannst. Daher folgen nun einfach noch ein paar Beispiele, um Dir zeigen, was MySQL alles kann.

An dieser Stelle möchte ich auch auf das MySQL Online Manual verweisen. Dort findest Du zu jedem MySQL-Befehl die Angabe aller möglichen Parameter.

1. Beispiel: SELECT

SELECT * FROM tabelle WHERE spalte1 = ? AND spalte 2 = ? OR spalte 1 = ? AND spalte2 = ? ORDER BY spalte1 DESC LIMIT 0, 10

Wir lesen alle (*) Spalten von der Tabelle tabelle aus. Dabei müssen die Bedingungen des WHERE-Statements zutreffen. Wir sortieren alle Ergebnisse nach der Werten für spalte1 und zwar absteigend (DESC). Das Gegenteil wäre ASC für eine auftsteigende Sortierung. Mit LIMIT geben wir an, dass er 0 Datensätze überspringen soll, und von da an 10 Datensätze auslesen soll.

2. Beispiel SELECT mit einfachem JOIN

Wir können auch zwei MySQL-Datenbanken virtuell miteinander verknüpfen. Das ist zum Beispiel hilfreich, wenn wir eine Tabelle für die Nutzerdaten und eine für die Newsletter-Empfänger angelegt haben.

Newsletter-Tabelle: id, email, status, userID

Nutzer-Tabelle: id (entspricht userID der Nutzer-Tabelle), username, password

Es muss bei beiden ein sich überschneidendes Feld geben, dann können wir so tun, als handele es sich um eine Tabelle. Dafür muss allerdings vor jedem Spaltennamen die Tabelle spezifiziert werden:

SELECT newsletter.email, users.username FROM newsletter, users WHERE newsletter.userID = users.id AND newsletter.status = ?

Wir lesen nun die Spalten email (aus der Tabelle newsletter) und username (Nutzer-Tabelle) aus, bei denen status (Newsletter-Tabelle) ? ist, also dem ersten übergebenen Argument entspricht.

Natürlich können auch hier ORDER, LIMIT & Co. verwendet werden.

3. Beispiel: Datensatz löschen

Mit DELETE können Datensätze gelöscht werden. Nimmst Du keine Eingrenzung durch WHERE vor, oder gibst kein LIMIT der zu löschenden Datensätze an, werden alle Datensätze aus der Tabelle gelöscht!

DELETE FROM newsletter WHERE status = ? LIMIT 10

In diesem Fall löschen wir maximal 10 Datensätze aus der Tabelle newsletter. Der Wert von status muss dem ersten Parameter entsprechen.

4. Beispiel: Datensatz einfügen

Datensätze können mittels INSERT eingefügt werden:

INSERT INTO newsletter (email, status) VALUES (?, ?)

Bitte beachte, dass Du hier kein * verwenden kannst! Du musst alle Spalten einzeln angeben. Außerdem müssen die Parameter nicht nur der Anzahl der Fragezeichen-Platzhalter, sondern auch der Anzahl angegebener Spalten (in diesem Fall email und status) entsprechen.

5. Negativ-Beispiel: MySQL-Injection!

Als letztes möchte ich Dir noch zeigen, wie Dein Statement ohne prepared Statements ausssehen könnte, wenn Du also nicht diese ganzen Fragezeichen für Daten verwenden würdest.

Das eigentliche Statement könnte lauten:

SELECT username, password FROM users WHERE username = '".$_GET['user']."' AND password = '".$_GET['password']."'

Da die Werte aus GET, und damit Benutzer-Eingaben einfach so übernommen wurden, kann dieser häufig beliebige Daten aus deiner Datenbank auslesen, indem er zum Beispiel UNION SELECT oder das MySQL Kommentar-Zeichen verwenden:

SELECT username, password FROM users WHERE username = '' UNION SELECT 1, concat_ws(0x3a,table_schema,table_name,column_name) FROM information_schema.columns LIMIT 180,1 -- ' AND password = 'password'

Was genau dieser Befehl machst, musst Du derzeit noch nicht verstehen. Nur soviel: Unter Umständen kann damit nicht nur deine gesamte Datenbank-Struktur, sondern auch alle Daten aus deiner Datenbank (E-Mail-Adressen, Passwörter & Co.) ausgelesen werden.

Prüfe also immer deine Benutzer-Eingaben, ehe Du mit ihnen weiterarbeitest oder sie ausgibst (siehe auch: Cross-Site-Scripting (XSS)).

Abschluss des Tutorials

Mit diesen Worten will ich Dich nun Deinem eigenen Coding-Schicksal überlassen. Mit diesen 13 Artikeln des PHP Tutorials hast du das Wichtigste über PHP lernen können. Doch ganz wichtig: Das Lernen hört nie auf.

Wenn Dir deine Lösung etwas umständlich vorkommt, googel lieber noch einmal oder frage in einem Forum nach, ob es dafür eine leichtere, sinnvollere oder schnellere Lösung gibt. So verbesserst Du Stück für Stück deine Kenntnisse.

Häufig hilft es auch fremde PHP-Skripte zu „lesen“ und erstmal auf die eigenen Bedürfnisse anzupassen. Dadurch kann man PHP meistens noch besser verstehen, als wenn man selbst unbedarft drauf los codet.

Daher möchte ich Dir meine PHP-Skripte ans Herz legen. Darunter befindet sich unter anderem ein Captcha, ein Kontaktformular und die Erklärung, wie Du deinen eigenen RSS-Feed erstellen kannst.

In diesem Sinne verabschiede ich mich im Rahmen des PHP Tutorials und würde mich freuen Dich erneut auf WWW Coding begrüßen zu dürfen.