PHP 5 - Ein praktischer Einstieg
2. Auflage

PHP 5 - Ein praktischer Einstieg, 2. Auflage

Von Ulrich Günther
2. Auflage, August 2004
O'Reilly Verlag, ISBN: 3-89721-278-1
www.oreilly.de/catalog/einphp2ger/



TOC PREV NEXT INDEX

Kapitel 6

Kapitel 6

PHP und Datenbanken

In diesem Kapitel werden wir uns relativ ausführlich mit der Verwendung von Datenbanken und ihrer Anbindung an PHP beschäftigen. Bevor wir uns in die technischen Details stürzen, sollten wir aber erst ein paar Fragen beantworten: Was ist eigentlich eine Datenbank? Was macht sie? Warum kann der Einsatz einer Datenbank in Verbindung mit PHP sinnvoll sein? Wann brauchen wir eine Datenbank?

Was ist eine Datenbank?

Im Prinzip kann eine Datenbank aus einer einfachen Datei bestehen, in der Daten abgespeichert werden. Dann müssen wir uns aber selbst darum kümmern, wie wir die entsprechenden Daten in die Datei bekommen und sie dort bei Bedarf finden, auslesen, ändern oder löschen können.

In der Praxis müssen wir uns diese Mühe nicht machen, da es eine große Anzahl von Programmen gibt, die uns diese Aufgabe abnehmen: Im Regelfall verstehen wir unter einer Datenbank deshalb einfach ein Programm, das auf einem Computer läuft. Eine solche Datenbank ermöglicht es Ihnen, nahezu beliebige Daten in strukturierter Weise abzuspeichern, auszulesen und zu ändern. So weit die theoretische Antwort. In einer normalen Datei können Sie natürlich auch Daten abspeichern, auslesen und ändern. Der Unterschied liegt in dem Wörtchen strukturiert.

In den meisten Datenbanken, so z.B auch in Microsoft Access und der hier besprochenen Open Source-Datenbank MySQL, werden die Daten in Tabellenform abgelegt. Die Tabellen und die Beziehungen zwischen den Tabellen stellen dabei die Struktur (auch Schema) der Datenbank dar. Tabellenkalkulationsprogramme wie Excel können Daten zwar auch halbwegs strukturiert abspeichern, sind aber eher für die Auswertung kleinerer Datenmengen gedacht.

Datenbanken sind dagegen hauptsächlich dahingehend optimiert, schnellen und einfachen Zugriff auf die Daten zu ermöglichen. Das können Sie im Prinzip auch selbst mit Dateien machen - dann müssen Sie aber den PHP-Code schreiben, der Ihnen die Informationen in die Dateien schreibt und dort wieder herausholt. Es ist allerdings ziemlich schwierig, das effizient zu machen - warum also das Rad neu erfinden?

Datenbanken, die ihre Daten in Tabellen speichern und Beziehungen zwischen diesen Tabellen definieren, nennt man relationale Datenbanken. Daneben gibt es noch andere Datenbankkonzepte, wie z.B. XML-Datenbanken, die ihre Daten in einer HTML-ähnlichen Tag-Struktur speichern, oder Datenbanken, die ihre Daten in Objekten organisieren. Wir beschränken uns hier auf relationale Datenbanken, da sie am weitesten verbreitet sind.

Viele Datenbanken liegen auf einem so genannten Datenbank-Server - das kann eine Maschine sein, die ausschließlich zu diesem Zweck verwendet wird, kann aber auch ein Rechner sein, auf dem neben dem Datenbank-Serverprogramm noch andere Anwendungsprogramme laufen. Auf dem Computer, auf dem dieses Buch geschrieben wurde, läuft beispielsweise nicht nur der Datenbank-Server MySQL, sondern auch noch ein Apache-Webserver sowie die übliche Horde von Webbrowsern, Office-Programmen usw. Im Prinzip sehen Datenbank-Server es aber vor, dass über ein Netzwerk auf sie zugegriffen wird. Als Datenbankbenutzer brauchen Sie deshalb in der Regel einen Login-Namen und ein Passwort.

Sobald Sie (oder ein von Ihnen beauftragtes Client-Programm, z.B. ein PHP-Skript) sich bei einem Datenbank-Server eingeloggt haben, können Sie mit ihm reden und auf die Daten in den Tabellen zugreifen. Das machen Sie im Regelfall mit einer Sprache namens SQL. SQL steht für »Structured Query Language«, was auf Deutsch »Strukturierte Abfragesprache« bedeutet und sowohl »Es-Kju-Ell« als auch aus historischen Gründen »Sie-Quell« (engl. Sequel) ausgesprochen wird. Es lohnt sich, beide Versionen im Kopf zu behalten, da Ihnen auch im professionellen Bereich Personen begegen können, die SQL zwar schon seit Jahren benutzen, aber nur einen der beiden Namen verwenden.

SQL ist eine recht lesbare und intuitive Sprache mit nur wenigen Sprachelementen, die auf Englisch basiert und deren Kern relativ leicht zu erlernen ist. Mit einem einfachen Befehl wie select * from tabelle können Sie beispielsweise alle Einträge einer Tabelle tabelle auslesen.

In diesem Kapitel werden Sie die wichtigsten Konzepte kennen lernen. So, nun wissen Sie, was eine Datenbank ist. Aber brauchen Sie eine? Wenn ja, wofür?

Wann braucht unser PHP-Skript eine Datenbankanbindung?

Eine Datenbankanbindung für Ihre Website brauchen Sie normalerweise dann, wenn Sie die vom Browser gelieferten Daten auf der Serverseite permanent speichern wollen. Bei unserer Beispielsite ist das genau dann der Fall, wenn wir die Spende auf unserem System registrieren und festhalten wollen, damit unser Schatzmeister sie bearbeiten und verbuchen kann. Das geht nicht ohne Datenbank - Webserver haben nämlich ein Gedächtnis wie ein Sieb.

Wie Sie vielleicht noch aus Kapitel 2 in Erinnerung haben, sind alle HTTP-Requests, mit denen der Browser Dokumente vom Webserver anfordert, im Prinzip voneinander unabhängige Ereignisse. Sobald die HTTP-Response an den Browser geschickt worden ist, verliert Ihr Server jede Erinnerung an die Transaktion. Das muss auch so sein - schließlich können Sie nicht von Ihrem Webserver verlangen, dass er versteht, in welchem Zusammenhang Ihre Seiten und die Daten in Ihren Formularen stehen.

Wenn Sie verschiedenen Browsern über viele HTTP-Transaktionen hinweg den Zugriff auf dieselben Daten geben wollen, muss im Regelfall eine Datenbank her. Wenn wir die Daten aber erst einmal in der Datenbank haben, wird es auch einfach, andere Skripte darauf zugreifen zu lassen. Typische Fälle, in denen Sie eine Datenbank brauchen, sind:

Online-Shopping
Beim Einkaufen online müssen Sie Wareninformationen speichern, die sich häufig ändern. Wenn Sie sie in einer Datenbank speichern, müssen Sie Ihre HTML-Dateien nicht für jedes neue Produkt oder jede Preisänderung verändern. Ein paar Bestellungen pro Tag könnten Sie noch selbst per E-Mail entgegennehmen und bearbeiten. Bei 10. 000 Bestellungen am Tag wird E-Mail ziemlich unübersichtlich. Einer guten Datenbank macht das aber wenig aus - sie ist skalierbar, d.h., sie kann mit Ihrem Online-Shop wachsen.
Auskunfts-Suchmaschinen
Wenn Sie Informationen aus größeren Datenmengen extrahieren wollen (Kataloge, Fahrpläne usw.), ist es unpraktisch, diese Daten in Ihrem PHP-Skript unterzubringen. Das Skript würde einfach zu groß und außerdem vom Server zu langsam verarbeitet. Da kommt eine Datenbank wie gerufen.

Es gibt aber auch viele Fälle, in denen Sie für Ihr PHP-Skript keine Datenbankanbindung benötigen. Hier zwei Beispiele:

Online-Berechnungsformular
Skripte, die Benutzerdaten sofort verarbeiten und dem Benutzer das Ergebnis sofort mitteilen, ohne die Daten nach Abschluss der Skriptausführung auf dem Server zu behalten, brauchen keine Datenbankanbindung. Das könnte z.B. ein Skript sein, das einen komplizierten Steuerrabatt berechnet.
Webcams
Bei vielen Webcam-Systemen werden die Bilder direkt als Bilddateien auf dem Webserver gespeichert, wobei die Aufnahmezeit in den Dateinamen mit einfließt. Wenn Sie ein Bild aufrufen wollen, das zu einer bestimmten Zeit aufgenommen wurde, kann Ihr Skript einen entsprechenden Dateinamen berechnen und Ihnen die Datei über ein dynamisch erzeugtes <img>-Tag zugänglich machen. Diese Aufgabe kann man aber im Prinzip auch über eine Datenbank lösen.

Sind Sie sich in einem bestimmten Fall nicht sicher, ob Sie eine Datenbank brauchen, können Sie sich das anhand der folgenden Fragen überlegen:

Früher oder später werden Sie aber vermutlich einem Fall begegnen, in dem es nicht ohne Datenbank geht. Deswegen lohnt es sich, Datenbanken anhand eines Beispiels zu entmystifizieren und Ihnen zu zeigen, wie Sie sie einsetzen können.

Datenbankgrundlagen für den Einsatz mit PHP

PHP-kompatible Datenbanken

Die gute Nachricht ist, dass fast alle weit verbreiteten Datenbankprogramme auch von PHP aus angesprochen werden können. Dazu zählen neben der bereits erwähnten Open Source-Datenbank MySQL die folgenden Datenbanken: dba, dBase, DBM, DB++, Frontbase, filePro, Informix, Interbase, Ingres II, LDAP, Microsoft SQL Server, mSQL, Oracle, Ovrimos, PostgreSQL, SESAM und Sybase sowie alle Datenbanken, die Sie über ODBC ansprechen können. Damit haben Sie so ziemlich alle bekannten Datenbanken abgedeckt (MS-Access-Datenbanken können Sie in Microsoft SQL Server importieren oder auch direkt über ODBC ansprechen). Bis Sie diese Zeilen lesen, sind vielleicht noch einige Datenbankprogramme dazugekommen.

Seit PHP 5 hat PHP auch eine einfache, eingebaute SQL-Datenbank namens SQLite. SQLite wird oft als Trostpflaster für all diejenigen angesehen, die der »Ehe« von PHP und MySQL nachtrauern.

PHP kann man seit PHP 5 kann nicht mehr mit einem eingebauten MySQL-Modul herunterladen - womit MySQL jetzt den gleichen Status bei PHP genießt wie alle anderen Datenbanken auch. Das bedeutet ein klein wenig mehr Arbeit bei der Installation und - auf Grund der neuen Lizenzstruktur bei MySQL AB - gegebenenfalls eine Lizenzgebühr, falls Sie MySQL 4 oder höher in einer kommerziellen Anwendung einsetzen wollen, die nicht Open Source ist. Das dürfte hauptsächlich diejenigen betreffen, die ihren PHP-Code weiterverkaufen wollen und darin MySQL verwenden.

SQLite ist kein Datenbank-Server und nicht so umfangreich wie MySQL oder einige der anderen oben angeführten Datenbanken, aber für viele kleine Anwendungen ist es eine gute Lösung.

Wenn Sie noch nicht genau wissen, welches Datenbankprogramm Sie letztendlich verwenden wollen, könnte sich ein Blick auf die dbx-Funktionen lohnen. Sie ermöglichen das Ansprechen verschiedener Datenbankprogramme durch einen gemeinsamen (wenn auch kleinen) Satz von Funktionen. Nähere Details dazu finden Sie in der PHP-Dokumentation unter »dbx-Funktionen« (Adresse siehe Anhang B).

Sofern es sich bei diesen Datenbanken um SQL-Server handelt, ist von PHP allerdings ohnehin ein relativ konsistentes Interface gegeben. Die meisten hier vorgestellten MySQL-Funktionen sind mit ähnlichem Namen und Parameterformat auch für andere Datenbankpakete vorhanden. Eine Abfrage eines MySQL-Servers führen Sie beispielsweise mit mysql_query() durch. Für einen Microsoft SQL Server heißt das äquivalente Kommando mssql_query(). Bei einem Wechsel der Datenbank müssen Sie daher allenfalls die Funktionsnamen in Ihrem Code ändern. Die Funktionsreferenz in der PHP-Dokumentation gibt Ihnen dazu detailliert Auskunft.

Die weiteren Abschnitte in diesem Kapitel erläutern deshalb nur die am häufigsten mit PHP verwendete Datenbank: MySQL.

Achtung Stolperstein! Welche MySQL-Version haben Sie?

Bevor wir mit MySQL loslegen, sollten Sie sich ansehen, mit welcher MySQL-Version Sie arbeiten werden. Die grundlegende Frage hier lautet: Ist es eine Version vor 4.1.0 oder 4.1.0 oder höher? Wenn Sie sich Ihren MySQL-Server selbst installiert und konfiguriert haben, werden Sie dieser Frage bereits begegnet sein, ebenso bei der Installation von PHP.

Die in diesem Buch besprochenen Beispiele laufen mit MySQL-Versionen vor 4.1.0.

Mit einer geringfügigen Änderung können Sie die Befehle und Skripte allerdings leicht auf MySQL 4.1.0 und höher umstellen: Ersetzen Sie einfach in Funktionen, die mit mysql_... anfangen, das mysql durch mysqli. Das sind die äquivalenten Funktionen für MySQL 4.1.0 und höher.

Diese Änderung lässt sich am besten mit der »Suchen und Ersetzen«-Funktion Ihres Texteditors durchführen.

Eine MySQL-Datenbank einrichten

Das Einrichten einer Datenbank ist immer etwas von Ihrem Datenbank-Server abhängig. Nachdem Sie MySQL installiert haben (siehe Anhang A), steht Ihnen gewissermaßen das »Betriebssystem« der Datenbank zur Verfügung.

Der nächste Schritt besteht im Anlegen einer Datenbank und darin, Ihnen Zugang zu dieser Datenbank zu geben. Wenn Sie das nicht von Ihrem Systemadministrator erledigen lassen können, folgen Sie einfach den Instruktionen in den nächsten Abschnitten. Sie beschreiben die Einrichtung einer Datenbank namens SPENDEN in MySQL.

Die grundlegende Struktur unserer Datenbank und ihre Position im MySQL-Datenbank-Server können Sie Abbildung 6-1 entnehmen. Unter einem MySQL-Server sind üblicherweise mehrere Datenbanken installiert (darunter die zur MySQL-Verwaltung verwendete Datenbank mysql und die Dummy-Datenbank test). Die jeweiligen Datenbanken stellen quasi logische Einheiten her, denen wir dann die entsprechenden Tabellen zuordnen. Wenn Sie hier eine Analogie zu Dateien und Verzeichnissen (Ordnern) sehen, dann liegen Sie damit gar nicht so falsch: MySQL speichert auf der Festplatte des Datenbank-Servers die einzelnen Datenbanken als Verzeichnisse ab, in denen sich die Dateien für die Tabellen befinden.
Abbildung 6-1 Datenbanken auf unserem MySQL-Server

Wenn Sie auf Ihrem Webserver die PHP-Anwendung phpMyAdmin zur Verwaltung Ihrer MySQL-Datenbanken installiert haben (erhältlich unter http://phpmyadmin.sourceforge.net, siehe Anhang B), können Sie viele der folgenden Schritte auch komfortabel über Ihren Webbrowser durchführen. Das Passwort für den Superuser root müssen Sie aber auf jeden Fall über die Eingabeaufforderung Ihres Betriebssystems setzen.

Das Passwort für root setzen

Wenn Ihre MySQL-Installation ganz neu ist, sollten Sie zunächst ein Superuser-Passwort setzen. Der Superuser, der in der Datenbank alles darf, wird als root bezeichnet. Unter Linux oder anderen Unix-Systemen öffnen Sie dazu ein Terminal und wechseln in das Unterverzeichnis bin des MySQL-Installationsverzeichnisses, sofern sich dieses nicht sowieso in Ihrem Pfad befindet.

Der Name bin stammt von dem Begriff »Binärdateien«. Als Binärdateien bezeichnet man Dateien, die keine Textdateien sind. In diesem Fall sind das die ausführbaren Programme, die zum MySQL-Paket gehören. Diese Konvention finden Sie auch in anderen Software-Paketen, z.B. in Apache.

Unter Windows verwenden Sie Start > Programme > Zubehör, um eine MS-DOS-artige Eingabeaufforderung (schwarzer Terminalschirm) zu öffnen. Dort wechseln Sie ebenfalls in das bin-Verzeichnis. Dann loggen Sie sich zunächst noch ohne Passwort in die Datenbank mysql ein.

mysql -u root mysql
 

Jetzt sollten Sie die Eingabeaufforderung mysql> sehen. Wenn Sie die MySQL-Version 3.22 oder höher verwenden, was bei einer neuen Installation eigentlich selbstverständlich sein sollte, können Sie ein neues root-Passwort mit

mysql> set password for root=password('mein_neues_passwort');
 

setzen. Das sollten Sie auf jeden Fall so schnell wie möglich tun, da sich sonst jeder beliebige Hacker im Internet direkt ohne Passwort in Ihren MySQL-Server einloggen kann. Loggen Sie sich mit quit aus und dann wieder ein:

mysql -u root -p
 

MySQL fragt Sie nun nach einem Passwort, und Sie geben natürlich das eben gesetzte an. Wenn Sie die Eingabeaufforderung mysql> sehen, haben Sie sich erfolgreich eingeloggt. Im Prinzip können Sie jetzt anfangen, Ihre Datenbank anzulegen. Aus Sicherheitsgründen kann es allerdings ratsam sein, den Zugriff auf die Datenbank für Ihre Anwendung nicht über root laufen zu lassen, sondern über einen speziell zu diesem Zweck eingerichteten Datenbankbenutzer.

Wenn Sie sich nicht einloggen können, könnte das daran liegen, dass der MySQL-Server nicht gestartet worden ist. Konsultieren Sie die MySQL-Dokumentation, um zu sehen, wie Sie ihn auf Ihrem System starten.
phpMyAdmin verwenden

Wenn Ihnen phpMyAdmin zur Verfügung steht, tragen Sie jetzt das neue root-Passwort in die Konfigurationsdatei config.inc.php (im phpAdmin-Verzeichnis) ein. Suchen Sie dazu die folgende Zeile in config.inc.php:

$cfgServers[1]['password']      = '';          
 

und ändern Sie sie durch Einsetzen des root-Passworts in:

$cfgServers[1]['password']      = 'mein_neues_passwort';          
 

Ebenso müssen Sie den Konfigurationsparameter $cfgPmaAbsoluteURI auf die Webadresse des Verzeichnisses setzen, in dem sich config.inc.php befindet, z.B.:

$cfgPmaAbsoluteUri = 'http://localhost/phpMyAdmin/';
 

Speichern Sie config.inc.php danach wieder ab. Wenn Sie mit Ihrem Browser dann http://localhost/phpMyAdmin/index.php öffnen, können Sie die folgenden Schritte zum großen Teil auch über komfortable Webformulare ausführen. Als Sprache geben Sie in der Auswahlliste rechts »German« an, auf diese Weise erscheinen alle Ausgaben von phpMyAdmin auch auf Deutsch.

Zusätzliche Datenbankbenutzer einrichten

Nehmen wir einmal an, dass wir einem Benutzer namens webserver Zugang zu unserer Datenbank SPENDEN geben wollen. Den Benutzer können wir einrichten, indem wir ihm ein Passwort geben. Dazu loggen Sie sich als root ein und geben ein:

mysql>grant all privileges on SPENDEN.* to 'webserver'@'localhost'
 
           identified by 'neues_webserver_passwort';
 

wenn Ihr MySQL-Datenbank-Server auf demselben Computer läuft wie Ihr Webserver. Wenn der MySQL-Datenbank-Server auf einem anderen Computer läuft als der Webserver, müssen Sie statt localhost den entsprechenden Hostnamen des Webservers angeben, z.B. hier webserver.meine-firma.de:

mysql>grant all privileges on SPENDEN.* 

           to 'webserver'@'webserver.meine-firma.de'
 
           identified by 'neues_webserver_passwort';
 

Die Syntax SPENDEN.* bedeutet übrigens: »Alle Tabellen der Datenbank SPENDEN«.

Eine Datenbank anlegen

Wollten Sie sich die Datenbank SPENDEN ansehen, würden Sie feststellen, dass sie noch gar nicht existiert. Wie sollte sie auch? Dazu müssen wir die Datenbank erst einrichten und mit den entsprechenden Tabellen versehen. Das können Sie mit SQL von Hand machen, wenn Sie wollen. Diese Methode ist aber ziemlich kompliziert und fehleranfällig.

Eine einfachere Methode ist, die dazu nötigen SQL-Statements in eine einfache Textdatei zu schreiben und dann in MySQL zu importieren. Eine solche Datei finden Sie auch im Web bei den Beispielen für dieses Kapitel. Der Inhalt dieser Datei, spendenDb.sql, besteht aus SQL-Befehlen, die unsere Datenbank und die dazugehörigen Tabellen einrichten:

Beispiel 6-1
Die Strukturinformationen unserer Datenbank in spendenDb.sql 
# spendenDb.sql
 
#
 
# SQL-Kommandodatei zum Einrichten der Datenbank SPENDEN.
 
# Achtung: Daten in einer evtl. bereits existierenden 
 
# Datenbank namens SPENDEN gehen in diesem Fall verloren.
 
drop database if exists SPENDEN;
 
create database SPENDEN;
 
use SPENDEN;
 

 
CREATE TABLE spender (
 
  spenderId varchar(13) NOT NULL,
 
  spenderName varchar(50),
 
  spenderAdresse longtext,
 
  spenderFoto varchar(50),
 
  PRIMARY KEY  (spenderId)
 
);
 

 
CREATE TABLE karte (
 
  kartenId varchar(13) NOT NULL,
 
  kartenName varchar(50),
 
  kartenTyp longtext,
 
  kartenNummer varchar(20),
 
  karteGueltigBis varchar(4),
 
  karteGueltig varchar(1),
 
  PRIMARY KEY  (kartenId)
 
);
 

 
CREATE TABLE spende (
 
  spendenId varchar(13) NOT NULL,
 
  spendenBetrag varchar(3),
 
  spendeOeffentlich varchar(1),
 
  spendenFrequenz varchar(3),
 
  spendeSpender varchar(13),
 
  spendenKarte varchar(13),
 
  spendenDatum varchar(20),
 
  spendenFormularZeit varchar(10),
 
  spendeAbgebucht varchar(1),
 
  PRIMARY KEY  (spendenId)
 
);
 

Diese Datenbank-Definition besteht aus zwei Teilen: Der erste Teil löscht eine evtl. existierende Datenbank desselben Namens, erstellt dann die (leere) Datenbank und wählt sie an:

drop database if exists SPENDEN;
 
create database SPENDEN;
 
use SPENDEN;
 

Damit können Sie ggf. Ihre Datenbank in einen nagelneuen Zustand zurückversetzen. Der zweite Teil besteht aus drei Tabellen-Definitionen. Sehen wir uns die erste dieser Definitionen einmal näher an:

Beispiel 6-2
Der Befehl zur Erzeugung der Tabelle spender
CREATE TABLE spender (
 
  spenderId varchar(13) NOT NULL,
 
  spenderName varchar(50),
 
  spenderAdresse longtext,
 
  spenderFoto varchar(50),
 
  PRIMARY KEY  (spenderId)
 
);
 

Der SQL-Befehl CREATE erzeugt hier eine (leere) Tabelle mit vier Spalten. In der Sprache der Datenbanker heißen Tabellenspalten übrigens »Felder«. Wir werden uns diesem Sprachgebrauch hier anschließen. Jedes Feld hat einen Namen, so dass unsere (leere) Tabelle in grafischer Form wie Tabelle 6-1 aussehen würde.

Tabelle 6-1
Die leere Tabelle mit vier Feldern
spenderId spenderName spenderAdresse spenderFoto
 
 
 
 

Außerdem hat jedes Feld auch einen Datentyp. Wir geben also an, welche Art von Daten wir in diesem Feld speichern wollen, d.h. in den Tabellenzellen, die zu dieser Spalte gehören.

Die Angabe des Datentyps ermöglicht der Datenbank, Speicherplatz ökonomisch einzusetzen: Wenn wir zum Beispiel nur eine ID speichern wollen, muss die Datenbank dazu nie mehr als 13 Zeichen speichern (so viele Zeichen gibt die PHP-Funktion uniqid("") aus). Wenn wir aber eine Adresse speichern wollen, können das auch erheblich mehr Zeichen sein.

Datenbank-Datentypen sind eine Wissenschaft für sich, deshalb wimmelt es in der Praxis von nicht optimalen Typzuordnungen nur so. In vielen Fällen ist Speicherplatz nicht sehr kritisch - für einen Vogelschutzverein müssen Sie auf keinen Fall zum Datenbankexperten werden. Deswegen reicht es oft auch, nur einen annähernd passenden Datentyp zu verwenden - Hauptsache, Sie können die gewünschten Daten darin unterbringen.

In unseren Definitionen verwenden wir nur zwei Datentypen: varchar(n), der eine Zeichenkette mit bis zu n Zeichen (n < 256) speichert, und longtext, der eine Zeichenkette von bis zu fast 4 GByte unterbringen kann, was für die meisten Zwecke reichen dürfte. Bei größeren Datenbanken lohnt es sich aber, passendere Datentypen zu verwenden, z.B. für Zahlen oder für BLOBs (Binary Large OBjects): binäre Objekte wie z.B. Bilddateien, denn auch so etwas kann man in einer Datenbank speichern.

Zunächst ist unsere Tabelle spender noch leer. Das soll sich aber spätestens dann ändern, wenn wir die Site öffentlich machen - dann sollen sich die Spender hier sprichwörtlich reihenweise einfinden. Und so geschieht es auch: Jede Zeile in dieser Tabelle wird einem Spender entsprechen. Alle Spenderkennnummern werden in der ersten Spalte (pardon - im Feld natürlich!) eingetragen, die Namen der Spender im zweiten Feld und so weiter.

Als letzten Teil der Tabellen-Definition definieren wir einen Primary Key, einen Primärschlüssel. Der ist für unsere Zwecke zwar im Prinzip nicht notwendig, macht der Datenbank das Leben aber etwas einfacher. Mit seiner Definition sagen wir der Datenbank, dass das entsprechende Feld (hier spenderId) als Identifikationsfeld für die Einträge der Tabelle verwendet wird. Das heißt, dass die Werte in diesem Feld jeweils nur in einem einzigen Eintrag der Tabelle vorkommen dürfen. Damit kann der Zugriff auf die Einträge beschleunigt werden. Da ein Primärschlüssel nie den Wert NULL haben darf, müssen wir dies nach der Felddeklaration explizit mit NOT NULL angeben. Die Definition der Kreditkartentabelle funktioniert im Prinzip genauso.

Es ist übrigens eine gute Praxis, bei Feldnamen immer ein Präfix zu verwenden, das auf die Tabelle verweist, in der sich das Feld befindet. Sonst könnten Sie vor lauter Feldern namens ID ganz schön ins Schwimmen kommen.

Bei der Definition der Tabelle spende gibt es zwei Besonderheiten unter den Feldern: spendeSpender und spendeKarte. Sie werden sicher ahnen, wozu wir sie benötigen: um den Spender bzw. die bei der Spende benutzte Kreditkarte anzugeben. Dazu brauchen wir aber nicht gleich sämtliche Spender- und Kartendaten in die Tabelle zu schreiben. Es reicht, wenn wir die ID des Spenders bzw. der Karte in spendeSpender und spendenKarte eintragen. Mit etwas SQL können wir dann die entsprechenden Daten aus spender und karte hinzufügen, wenn wir sie brauchen. Damit steht der Bauplan unserer Datenbank - das Schema.

Laden des Datenbank-Schemas über die Kommandozeile

Sie können die Datei in MySQL laden, indem Sie sie in das Unterverzeichnis bin des MySQL-Installationsverzeichnisses kopieren und dort den Befehl:

mysql -u webserver -p < spendenDb.sql
 

ausführen. Nachdem Sie das entsprechende Passwort eingegeben haben, wird die Datenbank SPENDEN mit allen drei Tabellen eingerichtet.

Laden des Datenbank-Schemas über phpMyAdmin

Wenn Sie phpMyAdmin verwenden, können Sie das Datenbank-Schema auch über Ihren Browser laden. Rufen Sie die Startseite von phpMyAdmin in Ihrem Browser auf. Wählen Sie dann den Eintrag »-« in der Auswahlliste links. Ihr Browser sollte daraufhin die in Abbildung 6-2 gezeigte Seite laden, über die Sie die Datenbank SPENDEN erzeugen können: einfach den Namen eingeben - fertig.
Abbildung 6-2 Erzeugen einer leeren Datenbank mit phpMyAdmin

Wählen Sie nun in phpMyAdmin die Datenbank SPENDEN über die Auswahlliste links an. Abbildung 6-3 zeigt das Dateifeld »Oder Datei:« unter dem Punkt »SQL-Befehl(e) ausführen in Datenbank SPENDEN«, mit dem Sie die Datei spendenDb.sql öffnen können. Klicken Sie auf OK. Damit werden die Tabellen Ihrer Datenbank mit den oben besprochenen Strukturen eingerichtet.
Abbildung 6-3 Laden von spendenDb.sql über phpMyAdmin

Damit wären wir beim nächsten Thema: Wie bekommen wir Daten in die neue Datenbank, und wie können wir die Daten dann wieder auslesen, auf den neusten Stand bringen und löschen? Dazu gibt es vier SQL-Befehle, die Sie kennen sollten: insert, select, update und delete. Sie werden in den folgenden Abschnitten erklärt.

Daten in eine MySQL-Datenbank schreiben: der SQL-Befehl insert

Zum Eintragen neuer Daten in ine Datenbanktabelle gibt es in SQL den Befehl insert (einfügen). Diesen Befehl können Sie auf zwei verschiedene Arten verwenden. Wenn Ihnen die Reihenfolge der Felder in der Tabelle bekannt ist und Sie alle Felder auf einmal beschreiben wollen, können Sie das über die Angabe aller Feldwerte tun. Das folgende Beispiel zeigt einen solchen Eintrag in die Spendertabelle:

insert into spender values('3ca2b43d12d2e','Fred Fisch',
 
                           '37 Wasserweg\r\nAquarium',
 
                           '3ca2b513a1a68.jpg');
 

Die zweite Variante bietet sich an, wenn Sie von der Reihenfolge der Felder unabhängig sein oder nur wenige Felder des Eintrags beschreiben wollen:

insert into spender (spenderId,spenderName,
 
                     spenderFoto,spenderAdresse)
 
            values ('3ca2b43d12d2e','Fred Fisch',
 
                    '3ca2b513a1a68.jpg', '37 Wasserweg\r\nAquarium');
 

Wenn Sie nicht alle Felder beschreiben wollen, lassen Sie sie einfach weg:

insert into spender (spenderId,spenderName,spenderAdresse)
 
            values ('3ca2b43d12d2e','Fred Fisch', '37 Wasserweg\r\nAquarium');
 

Ein solches Kommando können Sie auf drei Arten geben:

  1. Direkt über die MySQL-Kommandozeilen-Eingabe. Dazu müssen Sie sich mit Benutzernamen und Passwort in den MySQL-Server einloggen und die entsprechende Datenbank auswählen, z.B. use SPENDEN.
  2. Indem Sie die obige Zeile an spendenDb.sql anhängen und die Datenbank neu laden. Dazu müssen Sie die Rauten vor den drop- und create-Befehlen am Anfang der Datei entfernen.
  3. Mittels PHP - dazu kommen wir später noch.

Nach dem Eintrag der ersten Variante unseres Kommandos würde unsere spender-Tabelle wie in Tabelle 6-2 aussehen:

Tabelle 6-2
Die Tabelle spender nach Einfügen des ersten Datensatzes
spenderId spenderName spenderAdresse spenderFoto
3ca2b43d12d2e
Fred Fisch
37 Wasserweg
Aquarium
3ca2b513a1a68.jpg

In der Tabelle können Sie erkennen, was die Sonderzeichen \r\n in unserem insert-Befehl bedeuten: Sie stellen einen »Wagenrücklauf« (Carriage Return) und einen Zeilenvorschub (»Newline«) dar. In der Webwelt ergeben diese beiden Zeichen zusammen genommen einen Zeilenumbruch.

SQL-Befehle zum Einfügen in eine Datenbank können Sie, wie andere SQL-Befehle auch, in einer Textdatei ablegen. Die Datei spendenDaten.sql ist eine solche Datei. Sie können sie mit dem Befehl:

mysql -u root -p < spendenDaten.sql 
 

in Ihre Datenbank laden oder wie spendenDb.sql auch über phpMyAdmin installieren. In unserem Fall enthält spendenDaten.sql fünf Dummy-Einträge für unsere Datenbank SPENDEN, damit Sie ohne viel Tippaufwand in den nächsten Abschnitten weiter experimentieren können.

Damit haben wir unsere Daten in der Datenbank. Wie können wir sie wieder auslesen?

Daten aus der Datenbank lesen: der SQL-Befehl select

Zum Auslesen von Datenbankdaten steht der SQL-Befehl select zur Verfügung. Fangen wir mit ein paar einfachen Beispielen an: Nehmen wir an, dass wir alle Felder und Einträge der Tabelle spender auslesen wollen. Das machen wir so:

select * from spender;
 

In diesem Fall gibt das Zeichen * an, dass wir alle Felder der Tabelle auslesen wollen.

Benötigen wir nur bestimmte Felder, z.B. nur die Spendernamen und -adressen, lautet der Befehl folgendermaßen:

select spenderName, spenderAdresse from spender;
 

Wenn Sie die beiden obigen Beispiele zunächst einmal in MySQL ausprobieren wollen, können Sie sich wie gehabt mit Benutzernamen und Passwort einloggen und mit use SPENDEN die Datenbank SPENDEN auswählen. In MySQL bekommen Sie dann eine aus ASCII-Zeichen improvisierte Tabelle. Allerdings hat MySQL so seine liebe Not mit der Darstellung der Zeilenumbrüche. Aber keine Angst, sie werden schon richtig gespeichert. Allerdings ist diese Speicherung ein Grund mehr, mit visuellen Tools wie phpMyAdmin zu arbeiten.

Wenn wir die Namen alphabetisch sortieren wollen, nehmen wir folgenden Befehl:

select spenderName, spenderAdresse from spender order by spenderName;
 

SQL-Befehle können wir wie PHP-Anweisungen auch über mehrere Zeilen ausdehnen - das Ende des Befehls wird auch bei SQL durch ein Semikolon markiert. Wenn wir beispielsweise Namen und Adresse eines bestimmten Spenders suchen, gehen wir so vor:

select spenderName, spenderAdresse 
 
    from spender 
 
    where spenderId = '3ca2b43d12d2e';
 

An diesem Statement gibt es einige neue Punkte zu lernen: Erstens können Sie mit der where-Klausel eine Bedingung für die Auswahl setzen. Weil es in SQL keine Wertzuweisungen gibt, reicht als Vergleichsoperator auch das einfache Gleichheitszeichen aus. In diesem Fall vergleichen wir das Feld spenderId jedes Eintrags mit der Zeichenkette '3ca2b43d12d2e' und wählen diejenigen Einträge aus, in denen der Wert des Felds der Zeichenkette entspricht.

Da spenderId ein Primary Key ist, darf diese Zeichenkette nur in einem Eintrag der Tabelle vorkommen. Das Feld muss übrigens nicht in der Feldauswahl von select enthalten sein, solange es in einer Tabelle existiert, die hinter from aufgelistet ist, bekommen Sie ein Ergebnis.

Apropos Tabellen auflisten: Wir können ein select-Statement auch zum Abrufen von Informationen aus mehreren Tabellen verwenden. Nehmen wir an, dass wir den Namen des Spenders und den Spendenbetrag von allen Spenden über 50 Euro herausfinden und in absteigender Reihenfolge des Spendenbetrags sortieren wollen. Bei Spenden gleicher Spendenhöhe wollen wir die Einträge nach dem Spendernamen sortieren. Das machen wir so:

select spender.spenderName, spende.spendenBetrag
 
    from spender, spende
 
    where ((spender.spenderId = spende.spendeSpender)
 
            and (spende.spendeBetrag > 50))
 
    order by spende.spendeBetrag desc, spender.spenderName;
 

In diesem Fall haben wir es mit mehreren Tabellen zu tun. Daher geben wir die Felder unmissverständlich in der Form Tabellenname-Punkt-Feldname an, listen die Tabellen hinter from auf und sorgen in der where-Klausel dafür, dass wir die Feldwerte jeweils den Einträgen in den beiden Tabellen entnehmen, für die die ID-Nummern des Spenders übereinstimmen. Um die Default-Einstellung »aufsteigende Reihenfolge« von order by in »absteigende Reihenfolge« umzuwandeln, hängen wir dem Feld, nach dem sortiert werden soll, den Nachsatz desc an. Er leitet sich von »descending« (absteigend) ab.

Noch eine Kleinigkeit: Wenn wir den Wert eines Datenbankfelds mit einer Zahl vergleichen (wie beispielsweise spende.spendeBetrag mit der Zahl 50 im letzten select-Befehl), brauchen wir um die Zahl keine Anführungszeichen zu setzen. Bei Zeichenketten ist das anders: Wenn sie nicht von Anführungszeichen umgeben sind, werden sie von SQL als Feldnamen interpretiert, was in den meisten Fällen zu einem Fehler führt. In der Regel schaden Anführungszeichen nicht - wir werden sogar sehen, dass sie unter Sicherheitsaspekten ziemlich hilfreich sein können.

Die hier angeführten Beispiele sind nur ein kleiner Einblick in die Möglichkeiten, die Ihnen der SQL-Befehl select bietet. Mit diesen Grundkenntnissen kommen Sie aber schon relativ weit: Sie können selektierte Felder aus mehreren verwandten Tabellen auswählen und die entsprechenden Einträge filtern und sortieren. Damit können Sie die meisten Klippen umschiffen, die Ihnen beim Lesen aus Datenbanktabellen begegnen.

Daten in Datenbanktabellen löschen: der SQL-Befehl delete

Wir wissen jetzt, wie wir Daten in eine Datenbanktabelle schreiben und von dort wieder auslesen können. Aber was machen wir, wenn wir Daten in unserer Datenbank löschen wollen? Das Löschen von Einträgen aus einer Tabelle geht sehr einfach - der SQL-Befehl delete ist fast das Gegenstück zu select:

delete from spender where spenderId = '3ca2b43d12d2e';
 

Da wir nur gesamte Einträge löschen können, aber keine einzelnen Felder innerhalb eines Eintrags, folgt die from-Klausel direkt auf den delete-Befehl. Sie können auch mehrere Einträge auf einmal löschen, z.B. alle Spenden, deren Beträge unter 25 Euro liegen:

delete from spende where spendenBetrag < 25;
 

In diesem Fall lassen wir den Wert wieder ohne Anführungszeichen, weil es sich um eine Zahl handelt. Sie müssen aber aufpassen, dass Sie die where-Klausel nicht unabsichtlich weglassen: Der Befehl

delete from spende;
 

löscht alle Einträge in der Datenbanktabelle spende.

Datenbankeinträge auf den neusten Stand bringen: der SQL-Befehl update

Wenn Sie sich die letzten Abschnitte gut durchgelesen haben, könnten Sie theoretisch auch ohne den hier vorgestellten update-Befehl auskommen. Um einen Datenbankeintrag auf den neusten Stand zu bringen, könnten Sie ihn ja einfach löschen und danach wieder neu in die Datenbank schreiben.

Das ist aber nicht besonders effizient, da das Löschen und Neuschreiben eines kompletten Eintrags in einer Tabelle im Regelfall mehr Ressourcen benötigt als der Austausch von Feldwerten in einem existierenden Eintrag. Zum anderen ist es einfacher, alles in einem Befehl abwickeln zu können.

Der update-Befehl hat eine etwas andere Syntax als die bisherigen Befehle. Nehmen wir einmal an, wir wollten die Nummer und das Ablaufdatum einer Kreditkarte ändern, nicht jedoch ihren Typ oder den Namen auf der Karte. Das ginge dann so:

update karte set kartenNummer='7845-6430-1342-4921', 
 
                 karteGueltigBis='0504' 
 
             where kartenId='3ca2f5d516c33';
 

Hier geben wir nach dem Befehl update die Tabelle an, in der die Änderung erfolgen soll: card. Nach dem Schlüsselwort set folgt eine Liste der zu ändernden Felder, denen jeweils über ein Gleichheitszeichen der neue Wert zugewiesen wird. In diesem Fall müssen Sie auch aufpassen, dass Sie die where-Klausel nicht vergessen - ansonsten wird jeder Eintrag in der Tabelle »erneuert«.

SQL-Befehle kurz und knapp

Die Syntax der vier wichtigsten SQL-Befehle, die Sie kennen sollten, haben wir hier noch einmal zusammengefasst, in ihrer einfachsten generalisierten Form. Die eckigen Klammern stehen für optionale Syntaxelemente.
Daten einfügen
insert into <tabelle> [<feldliste>] values <werteliste>
Daten auslesen
select <feldliste> from <tabellenliste>
[where <bedingung>]
[order by <feldliste>]
Daten aktualisieren
update <tabelle> set <feld1>="<wert1>"
[, <feld2>="<wert2>", ...]
[where <bedingung>]
Daten löschen
delete from <tabelle> [where <bedingung>]
Das ist aber nur die Spitze des SQL-Eisbergs, obwohl Sie mit diesen Befehlen bereits die meisten einfachen Datenbankprobleme lösen können. Insbesondere der select-Befehl ist noch erheblich leistungsfähiger. Sehen Sie sich doch einmal ein paar der MySQL-Ressourcen an, die in Anhang A aufgeführt sind.

So - damit sind Sie fit für die nächste Aufgabe: den Zugriff auf Ihre MySQL-Datenbank von PHP aus.

PHP-Zugriff auf Datenbanken

Der Zugriff auf Ihre Datenbank von PHP aus geschieht in der Regel nach demselben Strickmuster, unabhängig davon, um welche SQL-Datenbank es sich handelt: Sie müssen sich mit dem Datenbank-Server verbinden und ihm einen Login-Namen und ein Passwort übergeben. Danach müssen Sie ihm sagen, mit welcher der Datenbanken auf dem Server Sie arbeiten wollen.

Sobald Sie die Datenbank angegeben haben, können Sie beliebige SQL-Befehle an den Datenbank-Server schicken. Sofern es sich um select-Befehle handelt, bekommen Sie Datensätze zurückgeliefert, die Sie in PHP verwenden können. Die wichtigsten PHP-Funktionen für MySQL1, in der Reihenfolge ihres Auftauchens in einer Datenbank-Interaktion, sind:

mysql_connect(server, benutzer, passwort)
Die Funktion mysql_connect() verbindet Ihr Skript mit dem durch server angegebenen Datenbank-Server und loggt sich als user mit password ein. mysql_connect() gibt Ihnen eine Kennung als Rückgabewert, unter der die Verbindung zur Datenbank verwaltet wird:
$dbLink = mysql_connect("pluto.solarsystem.com","spock","beamer");
 
In diesem Fall stellt $dbLink die Datenbankverbindung zum Rechner pluto dar, in den wir uns als Benutzer spock mit Passwort beamer einloggen. Falls die Verbindung wegen Netzwerk-, Server- oder Login-Problemen nicht zu Stande kommt, wird $dbLink auf false gesetzt. Damit können wir sehr einfach testen, ob wir auch tatsächlich verbunden sind.
mysql_select_db(datenbankname, dblink)
Mit dieser Funktion können wir die Datenbank auswählen. Dazu müssen wir den Namen angeben, z.B. SPENDEN. Der zweite (optionale) Parameter der Funktion ist die Kennung der Datenbankverbindung, beispielsweise:
$dbAuswahlOk = mysql_select_db("SPENDEN",$dbLink);
 
Fehlt der zweite Parameter, wird die zuletzt geöffnete Verbindung verwendet. Der Rückgabewert von mysql_select_db(), der hier in der Variablen $dbAuswahlOk gespeichert wird, ist entweder true oder false, je nachdem, ob die Auswahl geklappt hat.
mysql_query(query, dblink)
Diese Funktion ist die Brot-und-Butter-Funktion unserer Interaktionen mit der Datenbank. Der erste Parameter, query, ist ein String, der den SQL-Befehl enthält, den wir ausführen möchten. Der zweite Parameter ist wieder die optionale Kennung der Datenbankverbindung. Zurückgegeben wird eine Ergebniskennung, die wir zum Zugriff auf das Ergebnis verwenden können, beispielsweise:
$queryId = mysql_query("select * from card", $dbLink);
 
Wenn Sie hier einen expliziten String als query angeben, sollten Sie darauf achten, dass Ihnen Anführungszeichen im SQL-Befehl keine Probleme bereiten können (d.h., dass sie mit einem Backslash geschützt sein müssen).
mysql_fetch_array(queryid)
Das Ergebnis mit der Kennung queryid kann aus mehreren Datensätzen (d.h. Einträgen) bestehen. Bei jedem Aufruf von mysql_fetch_array() wird der jeweils nächste Eintrag aus dem Ergebnis ausgelesen und als assoziatives Array ausgegeben:
$eintrag = mysql_fetch_array($queryId);
 
Die Schlüssel für dieses Array sind Strings mit den Feldnamen, während die Werte der zugehörigen Array-Elemente den Feldwerten entsprechen. Wenn wir also beispielsweise einen einzigen Eintrag in unserem Ergebnis haben, der das Feld spenderName und das Feld spenderAdresse aus der Tabelle spender enthält, dann sind nach dem obigen Aufruf von mysql_fetch_array() der Spendername in $eintrag["spenderName"] und die Adresse in $eintrag["spenderAdresse"] zu finden. Jeder weitere Aufruf von mysql_fetch_array() hat einen Rückgabewert von false.

Damit können Sie sich wahrscheinlich schon in etwa denken, wie Sie in PHP an Daten aus einer Datenbank kommen. Sehen wir uns das doch anhand eines Beispiels etwas genauer an.

Beispiel 6-3
Eine einfache MySQL-Datenbankverbindung und -abfrage mit PHP 
<?php
 
    // Verbindung zum Datenbank-Server herstellen.
 
    $dbLink = mysql_connect("localhost","mein_benutzername","mein_passwort");
 
    // Datenbank auswählen.
 
    msyql_select_db("SPENDEN",$dbLink);
 
    // Abfrage starten (nach dem Namen des Spenders mit der 
 
    // angegebenen Spender-ID).
 
    $abfrageId = mysql_query("select spenderName from spender
 
                               where spenderId='3ca2b43d12d2e'");
 
    // Eintrag auslesen.
 
    $eintrag = mysql_fetch_array($abfrageId);
 
    // output the result
 
    echo "Der Spender, den Sie gesucht haben, ist ".$eintrag["spenderName"];
 
?>
 

Dieses Beispiel ist natürlich etwas primitiv, aber es verdeutlicht alle wesentlichen Elemente einer MySQL-Datenbankabfrage unter PHP.

In einer »richtigen« PHP-Datenbankanwendung lohnt es sich oft, über den Datenbankzugriff etwas nachzudenken, damit einem Code und Zugriffskomplexität nicht über den Kopf wachsen. Wie Sie gleich sehen werden, lässt sich das hervorragend in das Objektdesign einbauen, das wir im letzten Kapitel kennen gelernt haben.

Datenbankzugriff für unsere Objekte

Sie werden sich sicher daran erinnern, dass die Klassen, die wir im letzten Kapitel vorgestellt haben, noch etwas unvollständig waren. An mehreren Stellen hatten wir auf noch zu implementierenden Code hingewiesen. Diesen Code können wir jetzt Schritt für Schritt hinzufügen. Dazu können wir von einem großen Vorteil der Objektorientierung profitieren: Wenn Sie eine Klasse gebaut haben, können Sie dieser Klasse jederzeit neue Eigenschaften und Methoden hinzufügen. Solange die existierenden Eigenschaften und die existierenden Methoden in ihrer bisherigen Funktionalität erhalten bleiben, werden die Klassen auch mit Ihrem alten Code kompatibel sein.

Sie finden die neue Version von spendenKlassen.phpi im Online-Verzeichnis für dieses Kapitel. Wie Sie aber vielleicht schon im letzen Kapitel gemerkt haben, habe ich Ihnen bereits dort die jetzige Datei mit voller Datenbankfunktionalität untergeschoben.

Wenn Sie sie zusammen mit der bereits vorgestellten MySQL-Datenbank verwenden wollen und die Datenbank bereits eingerichtet haben, sollten Sie spendenKlassen.phpi auch mit der neuen Version von spendenCombo.php ausprobieren. Editieren Sie dazu die neu hinzugekommenen Verbindungsdaten (Server, Benutzername und Passwort) in datenObjekt in spendenKlassen.phpi, so dass sie Ihrer Konfiguration entsprechen (siehe folgenden Abschnitt). Sie können dann zumindest schon einmal Spenden in die Datenbank einfügen.

Da alle Objekte in spendenKlassen.phpi auf dieselbe Datenbank zurückgreifen, haben wir der Datei als Erstes eine Variable $dbLink hinzugefügt. Sie enthält die Kennung der Datenbankverbindung unseres Skripts und wird von allen Objekten des Skripts verwendet.

Neue Eigenschaften und Methoden in datenObjekt

Die erste Klasse in unserer alten Datei war die datenObjekt-Klasse. Wie Sie sich vielleicht erinnern, ist sie die Ursprungsklasse, von der wir alle unsere anderen Klassen abgeleitet haben. Der Zweck der Ursprungsklasse war, gemeinsame Eigenschaften und Methoden unterzubringen, die alle unsere Klassen brauchen würden.

In Sachen Datenbankfunktionalität unterscheiden sich unsere Klassen nicht allzu sehr. Egal, ob wir es nun mit einem Spender, einer Kreditkarte oder einer Spende zu tun haben: Wir müssen in jedem Fall die Werte von Objekteigenschaften in der Datenbank ablegen, auslesen und ändern können. Daher bietet sich die datenObjekt-Klasse als Unterbringungsort dieser Funktionalität an. Sehen wir uns also an, was es Neues gibt. Fangen wir mit den neuen Eigenschaften an:

Beispiel 6-4
Die neuen Eigenschaften der Klasse datenObjekt
var $schonInDB;
 
var $dbFzuE = array();
 
var $dbTabelle;
 
var $idFeld;
 
var $dbHostname = "localhost";
 
var $dbBenutzer = "spenderadmin";
 
var $dbPasswort = "6ge43stw";
 
var $dbDBName = "SPENDEN";
 

Einige dieser Eigenschaften erklären sich von selbst. Alle unsere Objekte müssen auf dieselbe Datenbank auf demselben Server mit demselben Login und Passwort zugreifen. Das erklärt die Eigenschaften dbHostname, dbBenutzer, dbPasswort und dbDBName.

Die Eigenschaft schonInDB verwenden wir dazu festzuhalten, ob wir die Eigenschaften eines Objekts bereits in der Datenbank gespeichert haben. Das ist dann wichtig, wenn wir uns entscheiden müssen, ob wir beim Speichern einen insert- (neuer Eintrag) oder einen update-Befehl (bestehender Eintrag) verwenden.

Als nächste Eigenschaft haben wir ein zunächst leeres (assoziatives) Array: dbFzuE. Diese etwas kryptische Abkürzung steht für »Feldname zu Eigenschaftsname« und dient dazu, beim Zugriff auf die Datenbank die Feldnamen (d.h. die Namen der Spalten in den Datenbanktabellen) den entsprechenden Eigenschaften des Objekts zuzuordnen. Die Schlüssel in dbFzuE werden dabei die Namen der Datenbankfelder sein. Der Wert, den wir einem Schlüssel in dbFzuE zuordnen, wird jeweils der Name der Objekteigenschaft sein, in der wir den Wert des entsprechenden Datenbankfelds abspeichern.

Dazu ein Beispiel: Nehmen wir an, dass wir in der Tabelle spender einen Eintrag für einen Spender namens "Fred Fisch" haben. Der Name wird in der Tabelle im Feld spenderName gespeichert. In unserer spender-Klasse haben wir vorgesehen, dass Spendernamen in Objekten dieser Klasse in der Eigenschaft name gespeichert werden sollen. Ein entsprechender Eintrag in dbFzuE würde also so aussehen:

$this->dbFzuE["spenderName"] = "name";
 

Da die entsprechenden Eigenschaften noch nicht in datenObjekt definiert werden, sondern eine Sache der abgeleiteten Klassen sind, können wir uns das Füllen der Array-Eigenschaft dbFzuE für die abgeleiteten Klassen aufsparen. Wozu wir dbFzuE brauchen, werden wir gleich noch im Detail sehen.

In dbTabelle speichern wir den Namen der Tabelle, in der sich die Daten für das entsprechende Objekt befinden. idFeld bezeichnet den Namen des Datenbankfelds, in dem sich der Primärschlüssel für die Tabelle befindet.

Die Methode dbVerbinden()

Die erste neue Methode finden wir etwas weiter unten in der Deklaration der datenObjekt-Klasse unter der Deklaration der Methode nurZiffern(). Es ist die Methode dbVerbinden():

Beispiel 6-5
Die Methode dbVerbinden()
function dbVerbinden() {
 
    global $dbLink;
 
    if ($dbLink) {
 
        return;
 
    }
 
    if (!(($dbLink =
 
        mysql_connect(
 
            $this->dbHostname,
 
            $this->dbBenutzer,
 
            $this->dbPasswort))
 
        && (mysql_select_db (
 
            $this->dbDBName,
 
            $dbLink)))) {
 
        die("Datenbankproblem:".mysql_error());
 
    }
 
}
 

Wie der Name schon nahelegt, stellt die Methode die Verbindung zum Datenbank-Server her. Da wir in unseren Klassen nur an einer Datenbank interessiert sind, übernimmt die Methode auch gleich noch die Auswahl derselben in einem Aufwasch. Falls die Verbindung allerdings bereits existiert, sorgt das erste if-Statement dafür, dass die Methode ohne weitere Umschweife mit return true beendet wird.

Der ganze Prozess des Verbindens und Datenbankauswählens ist allerdings potenziell fehleranfällig: Die Netzwerkverbindung zum Datenbank-Server könnte gestört sein, der Server ausgefallen, unser Login oder Passwort falsch sein, oder die gewünschte Datenbank könnte vielleicht nicht existieren. Um alle diese Fälle unter Kontrolle zu halten, verwenden wir ein if-Statement, das im Fehlerfall durch die Funktion die() zu einem Abbruch des Skripts führt und eine Fehlermeldung ausgibt.

Die Bedingung für das if-Statement besteht aus einem durch ! negierten Ausdruck, der wiederum aus zwei mit && verknüpften Bedingungen besteht. Die erste Bedingung ist:

$dbLink = mysql_connect(
 
                    $this->dbHostname,
 
                    $this->dbBenutzer,
 
                    $this->dbPasswort)
 

Bei dieser Bedingung handelt es sich nicht um einen Vergleich, sondern um einen ganz normalen Zuweisungsbefehl. Wenn PHP einen Zuweisungsbefehl ausführt (in diesem Fall also die Verbindung zum Datenbank-Server herzustellen und die Verbindungskennung an $dbLink zuzuweisen versucht), wird der zugewiesene Wert als Wert des gesamten Ausdrucks verwendet.

Liefert mysql_connect() also eine Kennung zurück, dann ist diese weder 0 noch ein leerer String und wird somit als true ausgewertet. Wenn nicht, wird false zurückgegeben, was dazu führt, dass der gesamte Ausdruck false wird. In diesem Fall ist bereits die erste Bedingung der UND-Verknüpfung && false, womit PHP sich die Auswertung der zweiten Bedingung sparen kann. Die Verknüpfung ist dann false und damit der negierte Gesamtausdruck true: Das Skript wird mit einer Fehlermeldung abgebrochen.

Zum Abbruch und zur Ausgabe der Fehlermeldung verwenden wir die PHP-Funktion die(). Diese Funktion führt zum sofortigen Abbruch des Skripts mit Ausgabe einer Fehlermeldung. Die auszugebende Fehlermeldung ist der einzige Parameter von die(), dem wir hier mit mysql_error() den aktuellen Fehlergrund beifügen.

Wenn der Verbindungsaufbau glückt, muss PHP auch die zweite Bedingung auswerten: die Auswahl der Datenbank. Den Rückgabewert von msyql_select_db() können wir hier direkt verwenden - er ist true, wenn die Datenbank gewählt werden konnte, false, wenn nicht.

Die Methode ladeAusDB()

Diese Methode haben wir schon im letzten Kapitel eingeführt - allerdings nur als funktionslosen Stub: Die Methode war leer und tat gar nichts. Hier füllen wir sie jetzt mit Leben:

Beispiel 6-6
Der Code von ladeAusDB()
function ladeAusDB($id) {
 
    $this->id = $id;
 
    $abfrage = "select * from ".$this->dbTabelle." ";
 
    $abfrage .= "where ".$this->idFeld." = '$this->id';";
 
    $tmp = $this->arrayAusDB($abfrage);
 
    $this->arrayZuEigenschaften($tmp);
 
}
 

Die Methode dient dazu, einen Eintrag mit bekannter ID in der Datenbanktabelle des Objekts zu finden und mit ihm die Eigenschaften des Objekts zu besetzen. Mit anderen Worten: Hier laden wir die Objektdaten aus der Datenbank. Dazu bauen wir einen String $abfrage, den wir dann an die Methode arrayAusDB() zur Durchführung der eigentlichen Datenbankabfrage übergeben (siehe unten).

Die drei dazu nötigen Parameter (die Namen der Tabelle und des ID-Felds in der Tabelle sowie die gesuchte ID) entnehmen wir dabei den jeweiligen bereits im Konstruktor gesetzten Eigenschaften. Dabei setzen wir so nebenbei auch noch die id-Eigenschaft des Objekts.

Die Methode arrayAusDB()

Wenn wir die Eigenschaften eines neuen Objekts mit Daten aus der Datenbank füllen wollen, müssen wir zwei Schritte durchführen: Zuerst müssen wir die Daten aus der Datenbank holen. Als Zweites müssen wir die Daten den entsprechenden Eigenschaften zuordnen und sie in ihnen abspeichern. Das könnten wir im Prinzip in einer einzigen Methode erledigen. Dennoch machen wir das hier in zwei Methoden. arrayAusDB() führt dabei den ersten Schritt aus, die noch zu besprechende Methode arrayZuEigenschaften() den zweiten.

Für diese Trennung der Aufgaben gibt es einen guten Grund. Stellen Sie sich vor, Sie wollen zum Abendessen Spaghetti kochen. Gut, dann stellen Sie sich jetzt vor, dass Ihr Supermarkt Spaghetti nicht nur in den üblichen 500-Gramm-Packungen verkauft, sondern auch einzeln. Bei unserer Datenbank ist das nicht anders: Sie können Einträge einzeln auslesen oder auch mehrere Einträge derselben Tabelle gemeinsam.

Dann gibt es zum Spaghettikochen im Prinzip zwei Methoden. Die erste Methode: Sie gehen 100-mal in den Laden, kaufen jeweils eine Nudel, tragen sie stolz nach Hause, kochen und essen sie. Dann gehen Sie zurück in den Laden, kaufen die nächste Nudel ... Hört sich etwas ineffizient an? Ist es natürlich auch.

Ebenso ist es ineffizient, wenn wir 100 Datenbankanfragen starten, nur um 100 Einträge aus der Datenbank zu bekommen, mit denen wir dann 100 gleichartige Objekte füllen. Selbst bei Tabellen mit nur ein paar Dutzend Einträgen kann MySQL da schon sehr schwerfällig werden.

Natürlich kochen Sie Spaghetti etwas anders: Sie gehen einmal in den Laden, kaufen eine große Packung mit dutzenden von Spaghetti und gehen wieder nach Hause zum Kochen und Essen. Erheblich effizienter, oder?

So ähnlich verhält es sich auch beim Erzeugen von mehreren Objekten aus Datenbankdaten. Es lohnt sich dabei oft nicht, für jedes einzelne Objekt separat auf die Datenbank zuzugreifen - das bindet unnötige Ressourcen und lässt sich oft nicht in der zur Verfügung stehenden Zeit abwickeln. Da wir sowohl in der Lage sein wollen, einzelne spender-, kreditKarte- und spende-Objekte zu erstellen, als auch Listen von Spendern und Spenden aufzustellen, sollten wir darauf Rücksicht nehmen.

Wenn wir nur ein einzelnes Objekt erstellen wollen, macht es Sinn, den ersten Schritt von einer Methode des Objekts durchführen zu lassen: arrayAusDB() holt die Daten aus der Datenbank und speichert sie in einem Array ab. Dieses Array können wir dann an die Methode arrayZuEigenschaften() übergeben, die den zweiten Schritt (Abspeichern der Daten in den Eigenschaften) übernimmt.

arrayAusDB() entspricht dabei dem Kaufen einer einzelnen Nudel. Manchmal sind wir eben auf Diät. arrayZuEigenschaften() geben wir eine Nudel, und die Methode kocht sie. Ob wir diese Nudel gerade aus einer Packung mit zig Spaghetti genommen oder sie tapfer einzeln aus dem Supermarkt geholt haben, ist arrayZuEigenschaften() egal.

Wenn wir mehrere Objekte brauchen, ist es deshalb effizienter, die Daten von außerhalb zur Verfügung zu stellen und den Objekten lediglich den zweiten Schritt, d.h. die Verteilung der Daten auf die Eigenschaften, zu überlassen. Der Code für den ersten Schritt sieht so aus:

Beispiel 6-7
Der Code der Methode arrayAusDB() 
function arrayAusDB($abfrage) {
 
    $this->dbVerbinden();
 
    if ($abfrageId = mysql_query($abfrage)) {
 
        $eintrag =
 
            mysql_fetch_array($abfrageId);
 
        $this->schonInDB = true;
 
        return $eintrag;
 
    }
 
    else
 
    {
 
        $this->schonInDB = false;
 
        return false;
 
    }
 
}
 

Die Methode hat einen Parameter: den Abfrage-String, den wir zum Bereitstellen der Daten in der Datenbank benötigen. Da die Methode in der Ursprungsklasse steht, die benötigten Daten aber erst in den abgeleiteten Klassen feststehen, müssen wir hier flexibel bleiben.

Die erste Zeile der Methode baut eine Datenbankverbindung auf, soweit diese noch nicht besteht. Dazu verwenden wir natürlich unsere eben eingeführte neue Methode dbVerbinden().

In dem darauf folgenden if-Statement machen wir erneut von der Tatsache Gebrauch, dass PHP Wertzuweisungsbefehle mit einem eigenen Wert versieht. Wenn die Abfrage erfolgreich war, lesen wir einen Eintrag (d.h. ein Array) aus dem Resultat und geben es als Rückgabewert der Funktion aus. Wenn die Abfrage kein Ergebnis gebracht hat, geben wir false zurück. In beiden Fällen vermerken wir das Resultat in der Eigenschaft schonInDB.

Die Methoden arrayZuEigenschaften() und garantiereEsc()

Wie schon erwähnt, ist diese Methode die zentrale Schaltstelle beim Einlesen von Array-Daten in unsere Objekteigenschaften. Die Array-Daten bestehen dabei aus einem assoziativen Array, dessen Schlüssel die Namen von Datenbankfeldern sind. Die zugehörigen Werte der Array-Elemente sind die Werte der jeweiligen Felder. Dieses Array kann z.B. von arrayAusDB() erzeugt werden.

Das Array wird der Methode durch den Parameter $eintrag übergeben. Sehen wir uns an, wie arrayZuEigenschaften() es zum Laden der zugehörigen Objekteigenschaften verwendet:

Beispiel 6-8
Der Code von arrayZuEigenschaften()
function arrayZuEigenschaften($eintrag) {
 
    foreach ($eintrag as $schluessel => $wert) {
 
        $wert = $this->garantiereEsc($wert);
 
        if ($this->dbFzuE[$schluessel] != "") {
 
            $this->{$this->dbFzuE[$schluessel]} = $wert;
 
        }
 
    }
 
}
 

Die Methode besteht im Wesentlichen aus einer foreach-Schleife in einer Konstruktion, der wir noch nicht offiziell begegnet sind2:

foreach ($eintrag as $schluessel => $wert) { ...
 

Diese Version der foreach()-Schleife ist für assoziative Arrays vorgesehen. Sie liefert bei jedem Durchlauf ein Element des angegebenen Arrays und spaltet es in je eine Variable für den Schlüssel und eine für den Wert des Elements auf. Mit anderen Worten: $schluessel enthält jetzt den Namen des Datenbankfelds und $wert den dazugehörigen Wert.

Die erste Zeile in der Schleife birgt eine kleine Überraschung - wir sorgen dafür, dass unser Wert erst einmal einer Sonderbehandlung unterzogen wird:

$wert = $this->garantiereEsc($wert);
 

Hier müssen wir nämlich ein bisschen aufpassen, dass der Wert unserer Objekteigenschaft auch genau das ist, was wir wollen. In Kapitel 4 (im Kasten »Magische Anführungsstriche (Magic Quotes)«) war uns ja schon einmal das Problem mit den Anführungszeichen in Elementen von $_POST begegnet, die von der Konfigurationsvariablen magic_quotes_gpc hinterrücks mit einem Backslash versehen wurden. Ich werde Ihnen noch in diesem Kapitel ausführlich erklären, warum das eine gute Idee ist.

Für den Moment will ich Sie aber nur daran erinnern, dass einige unserer Eigenschaftswerte aus $_POST stammen und ihre Anführungsstriche deshalb mit Backslashs versehen sind. Beim Schreiben in die Datenbank werden diese Backslashs gebraucht - wenn wir sie nicht hätten, könnte die Syntax unserer SQL-Abfrage-Strings kaputtgehen. Bei diesem Vorgang gehen die schönen Backslashs allerdings verloren - die Datenbankschnittstelle schmeißt sie raus. In der Datenbank stehen unsere Anführungszeichen nackt und bloß da, so wie der Benutzer sie eingetippt hat.

Wenn wir dann mit arrayZuEigenschaften() den Inhalt eines Datenbankfelds auslesen, kommen die Anführungszeichen auch ohne Backslash wieder aus der Datenbank - jedenfalls normalerweise. Da sie wieder in dieselbe Objekteigenschaft wandern sollen, aus der sie gekommen sind, sollten wir aus Konsistenzgründen dafür sorgen, dass sie wieder Backslashs vorangestellt bekommen.

Dazu verwenden wir der Übersichtlichkeit wegen eine kleine Methode, garantiereEsc(). Sie steht im Code vor arrayZuEigenschaften() und nimmt uns die Überlegung ab, ob »normalerweise« bei uns der Fall ist oder nicht. »Normalerweise« heißt nämlich, dass die PHP-Konfigurationsvariable magic_quotes_runtime in php.ini auf off steht und die Datenbankschnittstelle beim Lesen keine Backslashs einfügt. Wenn magic_quotes_runtime auf on steht, können wir uns das Hinzufügen von Backslashs sparen - es passiert automatisch. garantiereEsc() sieht nach, was der Status von magic_quotes_runtime ist, und reagiert entsprechend. Damit ist unser Wert immer im richtigen Format - mit Backslashs vor Anführungszeichen. garantiereEsc() sieht so aus:

Beispiel 6-9
Der Code der Methode garantiereEsc()
function garantiereEsc($wert) {
 
    if (!get_magic_quotes_runtime()) {   
 
        // Nicht geschützt, Backslashs einfügen.
 
        return addslashes($wert);
 
    }
 
    else
 
    {
 
        return $wert;
 
    }
 
}
 

Als nächste Aktion in der Schleife in arrayZuEigenschaften() überprüfen wir für jedes Element, ob für $schluessel (also den Feldnamen) auch eine entsprechende Eigenschaft des Objekts vorliegt, in der der Wert dieses Felds gespeichert werden soll. Dazu befragen wir unsere Objekteigenschaft dbFzuE, die uns in den abgeleiteten Klassen den zu einem Feldnamen gehörenden Eigenschaftsnamen zurückliefern soll. Da PHP bei unbekannten Schlüsseln einfach einen leeren String als Wert zurückliefert, können wir so Datenbankfelder überspringen, die für unser Objekt nicht relevant sind.

Ist eine entsprechende Eigenschaft vorhanden, wird sie in der folgenden Zeile gesetzt:

$this->{$this->dbFzuE[$schluessel]} = $wert;
 

Diese Zeile ist etwas kompliziert und verdient nähere Betrachtung. Um sie zu verstehen, müssen wir wissen, dass PHP die Möglichkeit gibt, auch die Namen von Variablen durch Variablen vorzugeben. Ein Beispiel:

$variablenName = "Fred";
 
$$variablenName = "Fisch";
 
echo $fred;  // Gibt "Fisch" aus.
 

Ähnliches gilt für die Vorgabe von Eigenschaftsnamen. Um klarzustellen, wo der variable Teil des Variablennamens beginnt und aufhört, kann man die Variable, die den Namen enthält, auch in geschweifte Klammern setzen: ${$variablenName}.

Das gleiche Prinzip gilt auch für Objekteigenschaften: In unserem Fall gibt $this->dbFzuE[$schluessel] den Namen der Objekteigenschaft vor, womit $this->{$this->dbFzuE[$schluessel]} die Eigenschaft dieses Namens zugänglich macht. Ihr weisen wir dann $wert zu.

Auf die oben angegebene Weise kopieren wir die Werte aller Datenbankfelder, für die in der Eigenschaft dbFzuE eine entsprechende Objekteigenschaft angegeben ist, in eben diese Objekteigenschaft. Da wir dbFzuE in jeder abgeleiteten Klasse mit anderen Feld/Eigenschaft-Paaren füllen können, kann sich unsere Methode allen abgeleiteten Klassen anpassen.

Damit haben wir das Werkzeug, das wir zum Lesen von Objektdaten aus unserer Datenbank brauchen. Die drei verbleibenden Methoden in datenObjekt dienen dem Einfügen, Ändern und Löschen von Einträgen.

Die Methode dbEinfuegen()

Um einen Eintrag in die Datenbank einzufügen, verwenden wir die Methode dbEinfuegen():

Beispiel 6-10
Der Code der Methode dbEinfuegen() 
function dbEinfuegen() {
 
    $this->dbVerbinden();
 
    $insertAbfrage = "insert into $this->dbTabelle ";
 
    $felder = "(";
 
    $werte = "(";
 
    $merker = false;
 
    foreach ($this->dbFzuE as $feld => $eigenschaftsName) {
 
        if ($merker) {
 
            $felder .= ",";
 
            $werte .= ",";
 
        }
 
        else
 
        {
 
            $merker = true;
 
        }
 
        $felder .= $feld;
 
        $werte .= "'".$this->{$eigenschaftsName}."'";
 
    }
 
    $felder .= ")";
 
    $werte .= ");";
 
    $insertAbfrage .= $felder." values ".$werte;
 
    mysql_query($insertAbfrage)
 
        or die("Query<p>
 
            <b>$insertAbfrage</b><p>
 
                konnte nicht ausgef&uuml;hrt werden: ".mysql_error());
 
}
 

Wie auch schon in arrayFromDB() wird am Anfang der Methode dafür gesorgt, dass eine Verbindung zur Datenbank hergestellt wird, falls noch keine besteht.

Danach bauen wir den SQL-Kommando-String zum Einfügen der Daten. Wenn Sie sich noch an die Syntax des insert-Befehls erinnern, dann wissen Sie, dass Sie ihn in zwei Varianten ausgeben können: unter Angabe der Feldnamen und Feldwerte oder ohne Angabe der Feldnamen, nur mit Feldwerten in der Feldreihenfolge der Tabelle. Da wir es hier mit verschiedenen Tabellen zu tun haben, empfiehlt sich die erste Version mit Angabe der Feldnamen.

Betrachten wir noch einmal unser Beispiel für einen Insert in die Tabelle spender:

insert into spender (spenderId,spenderName,
 
                     spenderFoto,spenderAdresse)
 
            values ('3ca2b43d12d2e','Fred Fisch',
 
                    '3ca2b513a1a68.jpg', '37 Wasserweg\r\nAquarium');
 

Damit besteht unser Kommando-String aus vier Teilen: dem insert-Befehl mit Angabe der betroffenen Tabelle (insert into spender), einer Liste von Feldnamen, dem String " values " und einer Liste mit Werten, deren Reihenfolge der der Feldnamen entspricht.

Der erste und dritte Teil des Kommando-Strings, insert into spender und values, sind einfach zu erstellen. Wir fangen mit dem ersten Teil an: Er wird in der Variablen $iquery gespeichert, an die wir nachher die anderen drei Teile anhängen. Der zweite und vierte Teil sind etwas komplizierter, da die einzufügenden Felder von der betroffenen abgeleiteten Klasse und die Werte vom jeweiligen Objekt abhängen.

Da die beiden Listen im zweiten und vierten Teil des Kommando-Strings dieselbe Reihenfolge aufweisen müssen, liegt es nahe, sie gleichzeitig zu konstruieren. Das machen wir, indem wir jede der beiden Listen in einer Variablen konstruieren: $felder wird zum Bau der Feldnamenliste verwendet, $werte für die Werteliste. Weil beide Listen von Klammern umschlossen sein müssen, geben wir beiden Variablen schon einmal den String "(" als Startwert mit auf den Weg.

Jetzt müssen wir nur noch die Feldnamen und Werte auflisten. Hier machen wir uns wieder die Array-Eigenschaft dbFzuE zu Nutze. Wie schon in arrayZuEigenschaften() haben wir mit dbFzuE die Möglichkeit, Feldnamen und zugehörige Eigenschaftsnamen zu verbinden. Über eine foreach-Schleife verarbeiten wir in jedem Durchlauf ein Feldname/Eigenschaft-Paar aus dbFzuE und hängen es an die Listen an.

Zum Anhängen an die Listen muss den Feldnamen bzw. Werten ab dem zweiten Listeneintrag ein Komma vorangestellt werden, da die Listenelemente durch Kommata getrennt werden. Das machen wir über ein if-Statement, das eine anfänglich auf false gesetzte Variable $merker im ersten Schleifendurchlauf der foreach-Schleife auf true setzt. In allen nachfolgenden Durchläufen sorgt das if-Statement dafür, dass den Listen-Strings Kommata hinzugefügt werden.

Danach fügen wir die Feldnamen an:

$felder .= $feld;
 

Das Anhängen der Werte ist etwas komplizierter:

$werte .= "'".$this->{$eigenschaftsName}."'";
 

Wir verwenden hier wieder die im letzten Abschnitt vorgestellte Technik, mit der wir den Namen der Eigenschaft durch eine Variable setzen können. Da wir hier durchgehend String-Werte einsetzen, müssen die Werte jeweils von Anführungszeichen umgeben sein. Das können doppelte Anführungszeichen sein oder auch einfache wie in diesem Fall.

Nach dem Ende der foreach-Schleife schließen wir die beiden Listen mit einer runden Klammer ab und vollenden den Kommando-String:

$insertAbfrage .= $felder." values ".$werte;
 

Danach führen wir das SQL-Kommando aus:

mysql_query($insertAbfrage)
 
    or die("Query<p>
 
        <b>$insertAbfrage</b><p>
 
            konnte nicht ausgef&uuml;hrt werden: ".mysql_error());
 

Jetzt werden Sie sich vielleicht etwas wundern: Was hat hier der ODER-Operator or zu suchen? Ganz einfach: Bei der Auswertung des Operators versucht PHP zunächst, die mysql_query-Funktion auszuführen. Wenn der Befehl erfolgreich ist, gibt das Kommando einen Wert zurück, der als true interpretiert wird. In diesem Fall spart sich PHP die Auswertung des Ausdrucks nach dem or-Operator, weil der wahre Rückgabewert der mysql_query-Funktion bereits dafür sorgt, dass der Gesamtausdruck wahr ist. Geht bei dem mysql_query-Kommando etwas schief, wird false zurückgegeben, und PHP muss sich die Mühe machen, auch den Ausdruck nach dem or-Operator zu interpretieren. Der wiederum bricht das Skript mit einer Fehlermeldung ab.

Damit wäre dbEinfuegen() auch schon entmystifiziert, bis auf eine kleine, aber wichtige Angelegenheit: Die Methode hat das Potenzial, Ihnen schwere Sicherheitsprobleme zu bereiten.

Strings und Datenbanksicherheit

Wenn Sie das plötzliche Auftauchen dieses Themas etwas überrascht, dann ist das gut so - es wird hoffentlich Ihr Auge für Sicherheitsprobleme schärfen. Denn jetzt ist der Zeitpunkt gekommen, an dem ich Ihnen erklären will, warum magic_quotes_gpc auf on stehen sollte.

Eigentlich sieht unser Code in dbEinfuegen() doch ziemlich harmlos aus, oder? Ist er aber nicht, jedenfalls dann nicht, wenn magic_quotes_gpc in Ihrer PHP-Installation auf off steht.

Wie kommt das? Nun, das Problem steckt in der folgenden Zeile:

$werte .= "'".$this->{$eigenschaftsName}."'";
 

Hier müssen wir unseren String in Anführungszeichen schreiben. Das ist völlig in Ordnung, solange unser Eigenschaftswert in $this->{$eigenschaftsName} keine einfachen Anführungszeichen enthält, denen kein Backslash vorangestellt ist. Dazu wird normalerweise magic_quotes_gpc auf on gesetzt. Wenn das nicht der Fall ist, führt ein Anführungszeichen in $this->{$eigenschaftsName} dazu, dass der Wert-String frühzeitig beendet wird.

Die restlichen Zeichen in $this->{$eigenschaftsName} werden dann Ihrer SQL-Syntax zugerechnet. Wenn $this->{$eigenschaftsName} einen lasch überprüften Eingabewert des Browsers enthält, ermöglichen Sie einem Hacker damit, SQL-Code seiner Wahl auf Ihrer Datenbank auszuführen.

Wie bitte? Okay, nehmen wir einmal an, dass $this->{$eigenschaftsName} den folgenden String enthält:

foo'); delete from spender; insert into spender values ('0','0','0','0
 

Wenn $eigenschaftsName den Wert adresse hat und das Objekt ein spender ist, ist $this->{$eigenschaftsName} die Eigenschaft adresse dieses Objekts. $insertAbfrage könnte dann (mit Zeilenumbrüchen zur Übersicht) so aussehen:

insert into spender (spenderId,spenderName,
 
                     spenderFoto,spenderAdresse)
 
            values ('3ca2b43d12d2e','Fred Fisch',
 
                    '3ca2b513a1a68.jpg','foo'); 
 
delete from spender; 
 
insert into spender values ('0','0','0','0');
 

Damit besteht unser Datenbankkommando aus drei aufeinander folgenden Befehlen. Der erste Befehl lädt zwar nur Schrott in die Datenbank, ist aber noch recht harmlos. Der zweite Befehl hat es aber in sich - er löscht sämtliche Daten in Ihrer Datenbanktabelle spender. Der dritte Befehl sorgt dafür, dass das Skript nicht mit einem Datenbankfehler abbricht.3

Nebem dem Löschen der Tabelle spender können Sie natürlich noch eine ganze Menge anderes Unheil anrichten: Sie können andere Tabellen oder sogar ganze Datenbanken löschen, sich möglicherweise ein Zugangspasswort für den Datenbank-Server einrichten und so weiter. Kleines Anführungszeichen - große Wirkung. Das ist einer der Gründe, warum es sich empfiehlt, Eingangsdaten gut zu überprüfen.

Wenn - wovon wir hier ausgehen - Ihre Installation magic_quotes_gpc on hat, funktioniert dieser Hack nicht: In diesem Fall werden evtl. in der Adresse enthaltene Anführungszeichen nicht als Teil der SQL-Syntax interpretiert, sondern als Teil des Strings selbst. Die Abfrage sieht dann so aus:

insert into spender (spenderId,spenderName,
 
                     spenderFoto,spenderAdresse)
 
            values ('3ca2b43d12d2e','Fred Fisch',
 
                    '3ca2b513a1a68.jpg','foo\'); 
 
delete from spender; 
 
insert into spender values (\'0\',\'0\',\'0\',\'0');
 

was völlig harmlos ist. In diesem Fall wird nur eine etwas komische Adresse in die Datenbank geschrieben. Aber mit Spaßvögeln, die Ihnen Quatsch auf die Website schreiben, müssen Sie im Internet immer mal rechnen.

Wir werden diesem Problem in Verkleidung noch ein paarmal begegnen. Wenn Sie unbedingt mit magic_quotes_gpc off fahren müssen, können Sie auch einfach den Code aus dem Kasten »Magische Anführungsstriche (Magic Quotes)« an den Anfang von spendenKlassen.phpi kopieren. Hier ist er zur Erinnerung noch einmal aufgeführt:

Beispiel 6-11
Code zum konditionalen Einfügen von Backslash-Escapes
if (!get_magic_quotes_gpc()) { // kann im Skript nur abgefragt werden
 
    foreach ($_POST as $key => $value) {$_POST[$key] = addslashes($value);}
 
    foreach ($_GET as $key => $value) {$_GET[$key] = addslashes($value);}
 
    foreach ($_COOKIE as $key => $value) {$_COOKIE[$key] = addslashes($value);}
 
}
 
set_magic_quotes_runtime(1); // Kann auch im Skript gesetzt werden!
 

Die Methode dbAendern()

Die Methode dbAendern() verwenden wir dazu, unsere Objektdaten in der Datenbank mit einem update-Befehl in SQL auf den neusten Stand zu bringen. Dabei gehen wir analog zu dbEinfuegen() vor. Der Unterschied besteht darin, dass wir in einem update-Befehl keine getrennten Listen für Feldnamen und Feldwerte haben. Stattdessen schreiben wir sie in eine gemeinsame Liste als durch Kommata getrennte Name/Wert-Paare, die wir in der Variablen $felder abspeichern. Ein Name/Wert-Paar wird jeweils durch ein Gleichheitszeichen zwischen dem Namen und dem Wert gebildet:

Beispiel 6-12
Der Code der Methode dbAendern() 
function dbAendern() {
 
    $this->dbVerbinden();
 
    $updateAbfrage = "update ".$this->dbTabelle." set ";
 
    $felder = "";
 
    $merker = false;
 
    foreach ($this->dbFzuE as $feld => $eigenschaftsName) {
 
        if ($merker) {
 
            $felder .= ",";
 
        }
 
        else
 
        {
 
            $merker = true;
 
        }
 
        $felder .= $feld;
 
        $felder .= "='".$this->{$eigenschaftsName}."'";
 
    }
 
    $updateAbfrage .= $felder." where ";
 
    $updateAbfrage .= $this->idFeld."='".$this->id."';";
 
    mysql_query($updateAbfrage)
 
        or die("Query<p>
 
            <b>$updateAbfrage</b><p>
 
                konnte nicht ausgef&uuml;hrt werden: ".mysql_error());
 
}
 

Als einzige »Neuheit« kommt die Update-Bedingung in der where-Klausel des SQL-Befehls hinzu. Hier verlangen wir, dass der Wert des ID-Felds des zu ändernden Eintrags mit der ID des Objekts übereinstimmen muss. Nur Einträge, bei denen diese Bedingung erfüllt ist, werden auch auf den neusten Stand gebracht.

Für dbAendern() gelten sowohl in den Name/Feld-Paaren wie auch in der where-Klausel dieselben Sicherheitsbedenken wie für dbEinfuegen(). Der zusätzliche Codeschnipsel, den ich Ihnen eben schon bei dbEinfuegen() ans Herz gelegt habe, sorgt auch hier dafür, dass Sie die Methode ebenfalls bei magic_quotes_gpc off sicher betreiben können.

Die Methode speichern()

Diese Methode ist eine einfache »Sorglos-Schnittstelle« zu den Methoden dbEinfuegen() und dbAendern(), je nachdem, ob das Objekt schon in der Datenbank existiert oder nicht:

Beispiel 6-13
Die »Bequemlichkeitsmethode« speichern() 
function speichern() {
 
    if ($this->schonInDB) {
 
        $this->dbAendern();
 
    }
 
    else
 
    {
 
        $this->dbEinfuegen();
 
    }
 
}
 

Die Methode dbLoeschen()

Beim Löschen von Datenbankeinträgen müssen wir das zu löschende Objekt mit seiner ID-Kennung in der Datenbank identifizieren. Das tun wir wie gehabt mit den Eigenschaften dbTabelle, idFeld, und id. Ansonsten birgt diese Methode keine Überraschungen:

Beispiel 6-14
Der Code der Methode dbLoeschen()
function dbLoeschen() {
 
    if ($this->id != "") {
 
        $this->dbVerbinden();
 
        mysql_query("delete from ".$this->dbTabelle .
 
                    " where ".$this->idFeld." ='$this->id';")
 
        or die("Dieser Eintrag kann nicht geloescht werden");
 
    }
 
}
 

Die Datenbank-Implementierung in den abgeleiteten Klassen

In datenObjekt haben wir schon gute Vorarbeit geleistet, die sich jetzt auszahlt. Die wichtigsten Änderungen an den bestehenden Klassen sind ebenfalls Dinge, die wir hinzufügen. Damit ändern wir die bestehende Funktionalität nicht. In keiner der abgeleiteten Klassen müssen wir neue Eigenschaften deklarieren - mit den bereits deklarierten Eigenschaften kommen wir völlig aus.

Die meisten Änderungen finden wir im Konstruktor der jeweiligen Klasse, wo wir jetzt den Namen der Datenbanktabelle, das ID-Feld und das Feld-zu-Eigenschaften-Array angeben müssen. In der Klasse spender kommen z.B. die folgenden Zeilen nach dem Aufruf von $this->datenObjekt() hinzu:

Beispiel 6-15
Die neuen Zeilen im Konstruktor der Klasse spender
$this->dbFzuE = array('spenderId' => 'id',
 
           'spenderName' => 'name',
 
           'spenderAdresse' => 'adresse',
 
           'spenderFoto' => 'fotoDatei');
 
$this->dbTabelle = "spender";
 
$this->idFeld = "spenderId";
 

Das Füllen des assoziativen Arrays dbFzuE geschieht hier auf eine Weise, die wir noch nicht kennen: mit dem array()-Konstruktor und dem »Zuweisungspfeil« =>. Damit wird elementweise jeweils einem Schlüssel (links von =>) ein Wert (rechts von =>) zugeordnet. In unserem Beispiel sind die Schlüssel die Feldnamen der Tabelle spender und die Werte die zugehörigen Eigenschaftsnamen der Klasse spender. Alternativ dazu könnten wir hier natürlich auch den klassischen Code für assoziative Arrays schreiben:

$this->dbFtoP["spenderId"] ='id';
 
$this->dbFtoP["spenderName"] ='name';
 
$this->dbFtoP["spenderAdresse"] ='adresse';
 
$this->dbFtoP["spenderFoto"] ='fotoDatei';
 

In den Konstruktoren der Klassen kreditKarte und spende kommen ebenfalls die entsprechendenden Zeilen neu hinzu:

Beispiel 6-16
Die neuen Zeilen im Konstruktor von kreditKarte
$this->dbFzuE = array('kartenId' => 'id',
 
           'kartenName' => 'name',
 
           'kartenTyp' => 'typ',
 
           'kartenNummer' => 'nummer',
 
           'karteGueltigBis' => 'laeuftAb',
 
           'karteGueltig' => 'gueltig');
 
$this->dbTabelle = "karte";
 
$this->idFeld = "kartenId";
 

Beispiel 6-17
Die neuen Zeilen im Konstruktor von spende
$this->dbFzuE = array('spendenId' => 'id',
 
           'spendenBetrag' => 'betrag',
 
           'spendeOeffentlich' => 'oeffentlich',
 
           'spendenFrequenz' => 'frequenz',
 
           'spendeSpender' => 'spenderId',
 
           'spendenKarte' => 'kreditKartenId',
 
           'spendenDatum' => 'datum',
 
           'spendenFormularZeit' =>
 
           'formularAusgabeZeit',
 
           'spendeAbgebucht' => 'abgebucht');
 
$this->dbTabelle = "spende";
 
$this->idFeld = "spendenId";
 

Ansonsten ändert sich an den Konstruktoren nichts.

Die Methode ladeAusDB()

In der Klasse spende sieht das alles etwas komplizierter aus: Dort haben wir nicht nur zwei neue Eigenschaften, spenderId und kreditKartenId, sondern auch eine etwas kompliziertere Abfrage in ladeAusDB():

Beispiel 6-18
Die Methode ladeAusDB() der Klasse spende 
function ladeAusDB($spendenId) {
 
    $this->id = $spendenId;
 
    $abfrage = "select * ";
 
    $abfrage .= "from spender, karte, spende ";
 
    $abfrage .= "where ((spendenId = '$this->id') ";
 
    $abfrage .= "and (spendeSpender = spenderId) ";
 
    $abfrage .= "and (spendenKarte = kartenId));";
 
    $tmp = $this->arrayAusDB($abfrage);
 
    $this->arrayZuEigenschaften($tmp);
 
    if (sizeof($tmp) > 0) {
 
        $spender = $this->spender;
 
        $spender->schonInDB = true;
 
        $kreditKarte = $this->kreditKarte;  
 
        $kreditKarte->schonInDB = true;
 
    }
 
}
 

Zum einen fällt uns auf, dass der Abfrage-String komplizierter geworden ist. Wir wollen alle Felder (*) aus allen drei Datenbanktabellen (spender, karte, spende). In der where-Klausel legen wir fest, dass wir nur solche Einträge wollen, deren spendenId unserer gesuchten ID entspricht und bei denen die Einträge der Tabellen spender und karte dem Spender und der Kreditkarte entsprechen, die mit dieser Spende zusammenhängen. Wenn unsere Datenbank nicht korrupt ist, sollte das nur ein einziger Eintrag sein.

Zum anderen laden wir jetzt die Daten für den Spender und die Kreditkarte gleich mit. Diese Daten werden dann in der Spezialversion von arrayZuEigenschaften() der Klasse spende dazu verwendet, in $this->spender und $this->kreditKarte ein entsprechendes Spender- bzw. Kreditkartenobjekt zu speichern. Wenn wir die Daten dazu in der Datenbank gefunden haben (d.h. die Größe des Arrays $tmp größer als null ist), markieren wir, dass sich die Einträge für diese Objekte schon in der Datenbank befinden. Damit vermeiden wir, dass beim Abspeichern der Objekte ein doppelter Eintrag in die Datenbank geschrieben wird.

Sie werden sich vielleicht fragen, warum wir hier in unserem if-Statement nicht einfach

$this->spender->schonInDB = true;
 
$this->kreditKarte->schonInDB = true;
 

verwendet haben. Das müsste doch auch gehen! Die Antwort: Als dieses Buch geschrieben wurde, ließen sich Eigenschaften von Objekten, die von _ _get() zurückgeliefert wurden, noch nicht direkt beschreiben - man musste ihnen wie oben zuerst eine eigene Objektvariable spendieren. Probieren Sie's aber ruhig selbst einmal aus - vielleicht ist dieses kleine »Feature« von PHP 5 ja beseitigt worden, bis Sie dieses Buch lesen!

Methode arrayZuEigenschaften() in der Klasse spende

Das temporäre Array $tmp, das wir aus der in ladeAusDB() konstruierten Anfrage erhalten, enthält erheblich mehr Einträge, als dbFtoP Elemente hat. Das hat auch einen Grund: Wir verwenden es gleich zur Erstellung der Spender- und Kreditkarten-Objekte, auf die die Eigenschaften spender und kreditKarte verweisen, und zwar in der Methode arrayZuEigenschaften(), für die wir in der abgeleiteten Klasse eine Spezialversion implementiert haben. Diese Technik - Methoden von Ursprungsklassen in abgeleiteten Klassen durch eine Spezialversion zu ersetzen - nennt man »Überladen«:

Beispiel 6-19
Die Methode arrayZuEigenschaften() der Klasse spende
// Diese Methode "überlädt" die Methode mit dem gleichen
 
// Namen, die wir von der Ursprungsklasse geerbt haben.
 
function arrayZuEigenschaften($dbArray) {
 
    $this->spender = new spender();
 
    $this->spender->arrayZuEigenschaften($dbArray);
 
    $this->kreditKarte = new kreditKarte();
 
    $this->kreditKarte->arrayZuEigenschaften($dbArray);
 
    parent::arrayZuEigenschaften($dbArray);
 
    $spender = $this->spender;
 
    $spender->konsistent = true;
 
    $kreditKarte = $this->kreditKarte;
 
    $kreditKarte->konsistent = true;
 
}
 

Hier überladen wir arrayZuEigenschaften(), damit wir zusätzliche Funktionalität implementieren können. Diese besteht aus dem Erzeugen eines spender- und eines kreditKarte-Objekts, deren jeweiligen arrayZuEigenschaften()-Methoden wir das Datenbank-Array übergeben.

Den ursprünglichen Job von arrayZuEigenschaften() - das Auffüllen der Eigenschaften des spende-Objekts - müssen wir natürlich auch noch erledigen. In PHP können wir dazu die existierende Funktionalität der Methode der Ursprungsklasse weiterverwenden - wir müssen dem Aufruf der Methode nur ein parent:: voranstellen. Erinnern Sie sich? Das haben wir schon bei _ _set() und _ _get() gesehen.

Der verbleibende Code in arrayZuEigenschaften() bescheinigt den hier neu erzeugten Objekten für den Spender und die Kreditkarte, dass ihre Daten konsistent sind. Das ist hier erforderlich, weil wir sie nur indirekt aus der Datenbank laden.

Damit haben wir auch schon alle Neuigkeiten besprochen. Unter anderem können wir jetzt unsere Daten in spendenCombo.php in der Datenbank abspeichern. Das geht sehr einfach - sehen Sie sich das in spendenCombo.php einfach einmal an -, den entsprechenden Code finden Sie direkt vor der Ausgabe der Bestätigung an den Benutzer:

$spender->speichern();
 
$karte->speichern();
 
$spende->speichern();
 

Was wir sonst noch mit unseren brandneuen Klassen machen können, erfahren Sie im nächsten Kapitel. Die neuen Eigenschaften sowie die neuen bzw. geänderten Methoden unserer Klassen sind in Tabelle 6-3 noch einmal für einen besseren Überblick zusammengefasst:

Tabelle 6-3
Datenbankeigenschaften und -methoden der Klassen in spendenKlassen.phpi (Forts.)
Klasse Eigenschaft/Methode Anmerkungen
datenObjekt
schonInDB
Zeigt an, ob sich für das Objekt bereits ein Eintrag in der Datenbank befindet.
 
dbFzuE
Array-Eigenschaft. Gibt für jedes Datenbankfeld, das für unser Objekt relevant ist, den Namen der zugehörigen Objekteigenschaft an. Wird abhängig von den jeweiligen Eigenschaften einer von datenObjekt abgeleiteten Klasse in deren Konstruktor gesetzt.
 
dbTabelle
Gibt den Namen der Tabelle an, in der die Daten für diese Objektklasse gespeichert werden. Ihr Wert hängt von der jeweiligen abgeleiteten Klasse ab und wird in deren Konstruktor gesetzt.
 
idFeld
Gibt den Namen des Datenbankfelds an, in dem die Kennzeichnung (ID) des Objekts abgespeichert ist.
 
dbHostname
Gibt den Namen oder die IP-Nummer des Datenbank-Servers an. Default: localhost.
 
dbBenutzer
Gibt den Benutzernamen für den Datenbank-Server an.
 
dbPasswort
Gibt das Passwort für den Datenbank-Server an.
 
dbDBName
Gibt den Namen der Datenbank auf dem Datenbank-Server an.
 
dbVerbinden()
Stellt die Verbindung zur Datenbank her, falls sie nicht schon besteht.
 
arrayAusDB(abfrage)
Holt ein assoziatives Array aus der Datenbank, dessen Schlüssel die in der übergebenen Anfrage angeforderten Felder sind.
 
arrayZuEigenschaften(eintrag)
Kopiert die Elemente des Arrays eintrag, die in der Eigenschaft dbFzuE aufgeführte Schlüssel (Feldnamen) haben, in die in dbFzuE angegebenen Eigenschaften.
 
dbEinfuegen()
Methode zum Einfügen der in dbFzuE aufgeführten Eigenschaften in die entsprechenden Felder der Datenbank.
 
dbAendern()
Methode zum Ändern der bereits in der Datenbank gespeicherten Daten.
 
dbLoeschen()
Löscht das Objekt aus der Datenbank.
 
speichern()
Bequemlichkeitsmethode, die entweder dbEinfuegen() oder dbAendern() aufruft, abhängig davon, ob das Objekt schon in der Datenbank vorhanden ist.
spender
spender()
Der Konstruktor legt jetzt auch die Datenbanktabelle, das ID-Feld und das Zuordnungs-Array dbFtoP fest.
kreditKarte
kreditKarte()
Der Konstruktor legt jetzt auch die Datenbanktabelle, das ID-Feld und das Zuordnungs-Array dbFzuE fest.
spende
spende()
Der Konstruktor legt jetzt auch die Datenbanktabelle, das ID-Feld und das Zuordnungs-Array dbFzuE fest.
 
ladeAusDB()
Diese Methode lädt die Eigenschaften des Spendenobjekts aus der Datenbank, indem sie die Datenbankabfrage an arrayAusDB() übergibt. Das zurückgelieferte Array gibt sie an die Methode arrayZuEigenschaften() weiter zum Kopieren der Werte in die Objekteigenschaften. Am Ende der Methode werden auch noch die »nebenher« erzeugten Spender- und Kreditkartenobjekte als in der Datenbank gespeichert markiert.
 
arrayZuEigenschaften()
Diese Methode ist eine überladene Version derselben Methode der Ursprungsklasse datenObjekt. Sie erzeugt zusätzlich die Spender- und Kreditkartenobjekte für diese Spende.
 
speichern()
Die Methode speichern() ist ebenfalls eine überladene Version derselben Methode in datenObjekt. Sie sorgt vor dem Speichern noch dafür, dass die Referenz-Eigenschaften für den Spender und die Kreditkarte gesetzt werden.

Probieren Sie's selbst!

Eine Information, die wir bisher in unserer Klasse nicht abgespeichert haben, ist die E-Mail-Adresse des Spenders. Wenn Sie möchten, können Sie das nachholen. Hier sind die Schritte, die Sie ausführen müssen:

Die Lösung finden Sie unter spendenFormMitEmail.php, spendenKlassenErweitert.phpi, spendenDbErweitert.sql und spendenComboErweitert.php in den Online-Beispielen zu diesem Kapitel.

1Verwenden Sie MySQL 4.1.0 oder höher, müssen Sie die hier aufgeführten PHP-Funktionen von mysql_... in mysqli_... umbenennen, da sich seit dieser MySQL-Version die Schnittstelle zu anderen Anwendungen (wie z.B. PHP) geändert hat.
2Vielleicht haben Sie diese Form von foreach aber auch schon in Kapitel 4 im Kasten »Magische Anführungsstriche (Magic Quotes)« erspäht, in dem wir sie zum Einfügen von Backslashs in $_POST usw. verwendet haben.
3Im Gegensatz zu anderen Datenbanken funktioniert dieser spezielle Angriff zum Glück (noch) nicht bei MySQL, da seine Anwendungsschnittstelle bisher keine durch Semikolon getrennten Mehrfachabfragen annimmt und PHP dies (noch) nicht kompensiert. Auch unter MySQL lassen sich aber viele Abfragen durch das Einfügen von externen SQL-Codeschnipseln zweckentfremden.

TOC PREV NEXT INDEX

Copyright © 2004 by O'Reilly Verlag GmbH & Co.KG