Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Geleitwort
Vorwort
1 PEAR – Einführung
2 Authentication
3 Caching
4 Date and Time
5 File Formats
6 HTTP
7 Internationalization
8 Mail
9 Networking
10 PHP
11 Text
12 Web Services
13 Benchmarking
14 Configuration
15 Database
16 File System
17 HTML
18 Images
19 Logging
20 Math
21 Numbers
22 Tools and Utilities
23 XML
24 Selbst Pakete erstellen
25 PECL
Index
Ihre Meinung?

Spacer
 <<   zurück
PHP PEAR von Carsten Möhrke
Anwendung und Entwicklung – Erweiterungen für PHP schreiben
Buch: PHP PEAR

PHP PEAR
798 S., 39,90 Euro
Rheinwerk Computing
ISBN 3-89842-580-0
gp 15 Database
  gp 15.1 DB
    gp 15.1.1 prepare(  ) und execute(  )
    gp 15.1.2 Informationen über Tabellen und Datenbank
    gp 15.1.3 Quoting/Escaping
    gp 15.1.4 Transaktionsorientierung
  gp 15.2 DB_NestedSet
  gp 15.3 DB_QueryTool
  gp 15.4 DB_Table
    gp 15.4.1 Einfügen von Werten
    gp 15.4.2 Nutzung von Formularen
    gp 15.4.3 Auslesen von Werten
    gp 15.4.4 Manipulation von Daten

15 Database

Die Kategorie »Database« umfasst einige Pakete, die Ihnen die Arbeit mit Datenbanken erleichtern. Neben Abstraktionslayern sind hier auch Pakete zu finden, die Ihnen den Umgang mit SQL erleichtern.

In dieser Kategorie sind drei große Abstraktionspakete zu finden: DB, MDB und MDB2. Die Entscheidung, welches Paket ich hier aufnehme, war nicht ganz leicht. Da alle, zumindest in Teilen, sehr ähnliche Dinge leisten und auch die API in weiten Teilen kompatibel ist, wollte ich nicht alle Pakete aufnehmen. Ich habe mich für DB entschieden, weil es die höchste Verbreitung hat und in vielen Paketen Verwendung findet. Allerdings möchte ich Ihnen noch den Tipp geben, MDB2 im Auge zu behalten. Auch wenn das Paket noch nicht ganz fertig ist, wird es zukünftig viele interessante Funktionalitäten bieten.


Rheinwerk Computing

15.1 DB  downtop


Besprochene Version: 1.7.4 Lizenz: PHP-Lizenz 3.0
Klassendatei(en): DB.php

PEAR::DB ist sicherlich eines der bekanntesten PEAR-Pakete. Dieser Datenbankabstraktionslayer gibt Ihnen die Möglichkeit, Ihre Anwendungen unabhängig von einer bestimmten Datenbank zu entwickeln. Nutzen Sie in einer Anwendung Funktionen wie mysql_connect(), so müssten Sie Ihre ganze Anwendung umschreiben, um eine andere Datenbank nutzen zu können. Nutzen Sie hingegen PEAR::DB, ist das nicht nötig, weil Sie einfach eine andere Datenbank eintragen können, die genutzt werden soll.

Bevor ich zur Kommunikation mit der Datenbank komme, noch ein Wort zur Fehlerbehandlung. Die Methoden liefern im Falle eines Problems alle ein PEAR_Error-Objekt bzw. ein DB_Error-Objekt zurück. Allerdings ist es nicht in allen Fällen notwendig, das Script sofort zu beenden. Die Fehlermeldungen lassen eine gute Differenzierung zu, so dass Sie exakt unterscheiden können, ob es ein kritischer Fehler ist oder einer, der durch die Applikation behandelt werden kann. In Tabelle 15.1 finden Sie eine Liste mit den wichtigsten Fehlerkonstanten. Eine komplette Liste finden Sie in der Datei DB.php.


Tabelle 15.1 Die wichtigsten Fehler, die PEAR::DB kennt
Konstante Bedeutung
DB_OK Kein Fehler
DB_ERROR Unbekannter Fehler
DB_ERROR_SYNTAX Syntax-Fehler in einem SQL-Befehl
DB_ERROR_CONSTRAINT Verstoß gegen eine Constraint, weil z. B. ein Wert in eine Spalte, die als Primärschlüssel definiert war, doppelt eingefügt werden sollte.
DB_ERROR_NOT_FOUND Ein Bezeichner in einer Abfrage konnte nicht gefunden werden.
DB_ERROR_ALREADY_EXISTS Versuch, ein Datenbank-Objekt anzulegen, die es bereits gibt.
DB_ERROR_UNSUPPORTED Gewünschte Funktionalität wird vom nicht Treiber unterstützt.
DB_ERROR_MISMATCH Die Anzahl von Parametern und Platzhaltern stimmt nicht überein.
DB_ERROR_INVALID Ein übergebener Wert entspricht nicht dem erforderlichen Datentyp.
DB_ERROR_NOT_CAPABLE Die gewünschte Funktionalität wird nicht von der Datenbank unterstützt.
DB_ERROR_TRUNCATED Ein Wert war zu lang, so dass er gekürzt wurde.
DB_ERROR_INVALID_NUMBER Eine übergebene Zahl entsprach nicht dem erwarteten Datentyp.
DB_ERROR_INVALID_DATE Ein übergebenes Datum hatte nicht das korrekte Format.
DB_ERROR_CANNOT_CREATE Ein Objekt kann nicht angelegt werden, weil der User nicht die benötigten Rechte hat o. Ä.
DB_ERROR_NOSUCHTABLE Die Tabelle existiert nicht.
DB_ERROR_VALUE_COUNT_ON_ROW Die Anzahl der Spalten stimmt nicht mit der Anzahl der Werte überein.

Wenn ein Fehler festgestellt wird, können Sie den Fehler-Code mit getCode() auslesen.

if (true === PEAR::isError($db)) 
{ 
   switch ($db->getCode()) 
   { 
      case DB_ERROR_TRUNCATED:  // Verarbeitung des Fehlers 
      // Weitere Cases 
   } 
}

Wie gesagt sind nicht alle Codes in Tabelle 15.1 zu finden. Die meisten der anderen Codes erfordern allerdings wahrscheinlich einen Abbruch, weil die Datenbank nicht gefunden wurde oder Ähnliches.

Die Informationen, die PEAR::DB für den Verbindungsaufbau benötigt, werden in Form eines DSNs übergeben. Die Abkürzung DSN steht für »Data Source Name«. Ein DSN kann unterschiedlich aufgebaut sein. Er könnte beispielsweise so aussehen:

$dsn = "mysql://paul:geheim@localhost/daten";

Der erste Teil eines DSNs ist der so genannte phptype. Er definiert, welche Art von Datenbank genutzt wird. mysql: steht in diesem Beispiel also für einen MySQL-Datenbankserver. Als weitere Typen stehen die Datenbanken aus Tabelle 15.2 zur Verfügung.


Tabelle 15.2 Von PEAR::DB unterstützte Datenbanken
Datenbank Typ
dBase dbase
FrontBase fbsql
InterBase ibase
Informix ifx
Mini SQL msql
Microsoft SQL Server mssql
MySQL < Version 4 mysql
MySQL > Version 4 mysqli
Oracle 7/8/9 oci8
ODBC odbc
PostgreSQL pgsql
Sqlite SQLite
SyBase sybase

Direkt hinter dem phptype ist es möglich, in Klammern eine Syntax, genannt dbsyntax, für die Datenbank anzugeben. Einige Datenbanken unterstützen mehrere Syntax-Modelle, bzw. wenn Sie per ODBC auf eine Datenbank zugreifen, stellt sich natürlich auch die Frage, welche Syntax zu nutzen ist.

$dsn = "ifx(db2)://paul:geheim@localhost/daten";

Nach einem doppelten Slash folgen der Benutzername und das Passwort, mit denen die Verbindung aufgebaut werden soll. Sollte in einem dieser Strings, die mit einem Doppelpunkt voneinander abzugrenzen sind, ein Zeichen vorkommen, das für die Syntax des DSNs benötigt wird, so können Sie es durch seine hexadezimale Darstellung im Stil einer URL-Kodierung ersetzen.


Tabelle 15.3 15.3: Kodierung von Sonderzeichen
: = %3A / = %2F @ = %40 ? = %3F = = %3D
+ = %2B ( = %28 ) = %29 & = %26  

Dem @ folgt in den meisten Fällen nur der Name bzw. die IP-Adresse des Servers. Allerdings können Sie hier auch ein Protokoll angeben, mit dem auf den Server zugegriffen werden soll. In so einem Fall verbinden Sie das Protokoll und den Servernamen mit einem +. Zusätzlich können Sie hinter dem Server noch einen Port angeben, der mit einem Doppelpunkt abzutrennen ist.

$dsn = "mysql://paul:geheim@tcp+localhost:33001/daten";

Nach dem Server bzw. der Port-Nummer geben Sie den Namen der Datenbank an.

Mit dieser Vorgehensweise können Sie die meisten Aufgabenstellungen sicher abdecken, aber es gibt noch einige Sonderfälle. Um eine Verbindung über einen Socket aufzubauen, nutzen Sie das Protokoll unix und übergeben den Pfad zum Socket in Klammern.

$dsn="mysql://paul:geheim@unix(/pfad/zum/socket)/daten

Auch die Nutzung einer SQLite-Datenbank, die erst ab PHP 5 verfügbar ist, stellt einen Sonderfall dar. In diesem Fall ist der Pfad zur Datenbank-Datei anzugeben. Nach dem Dateinamen folgen die Option mode, die mit einem Fragezeichen angehängt wird, und der oktale Zugriffsmodus.

$dsn="sqlite:///data/sqlite.db?mode=0666";

Mithilfe des Fragezeichens können Sie auch noch weitere Optionen zum Zugriff auf die Datenbank übergeben, wenn das erforderlich sein sollte. Die Werte sind dann mit einem kaufmännischen Und (&) zu verknüpfen.

Alternativ können Sie die benötigten Daten auch in Form eines Arrays übergeben, was aber eher unüblich ist. Dieses assoziative Array ist folgendermaßen aufzubauen:

$dsn = array( 
   'phptype'  => 'mysql', 
   'dbsyntax' => false, 
   'username' => 'paul', 
   'password' => 'geheim', 
   'protocol' => false, 
   'hostspec' => 'localhost', 
   'port'     => false, 
   'socket'   => false, 
   'database' => 'daten', 
);

In diesem Beispiel habe ich alle verfügbaren Elemente genutzt. Die Felder, die den Wert false haben, müssten nicht angegeben werden. Alle Elemente, die Sie nicht angeben, werden mit false vorbelegt und dann ignoriert. Zusätzlich können Sie, in Abhängigkeit von der verwendeten Datenbank, noch weitere Felder vorsehen, in denen Optionen für die Datenbank bzw. den Treiber übergeben werden.

Um die eigentliche Verbindung zur Datenbank aufzubauen, ist die Methode connect() zu nutzen, die den DSN bzw. das Array als ersten Parameter übergeben bekommt.

Die Methode akzeptiert an zweiter Stelle noch ein Array als Parameter, mit dem Sie das Verhalten des Systems beeinflussen können. Die Schlüssel und Erläuterungen finden Sie in Tabelle 15.4.


Tabelle 15.4 Optionen, die an connect(  ) übergeben werden können
Schlüssel Beschreibung
'autofree' Der Standardwert false verhindert, dass das Ergebnis einer Abfrage automatisch aus dem RAM entfernt wird, wenn alle Zeilen ausgelesen wurden. Mit true wird es automatisch gelöscht.
'debug' Hier können Sie eine Zahl übergeben, die den Debug-Level definiert. Das System scheint hier momentan nur zwischen 0 (keine zusätzliche Information) und 2 (Ausgabe von Fehlern, die sonst unterdrückt werden) zu unterscheiden.
'persistent' Übergeben Sie hiermit true, wird eine persistente Datenbankverbindung genutzt, was standardmäßig oder mit dem Wert false nicht der Fall ist.
'portability' Hier können Sie eine der Konstanten aus Tabelle 15.5 übergeben, um ein höheres Maß an Kompatibilität zu gewährleisten.
'seqname_format' Ein String, der definiert, wie der interne Name einer Sequenz aufzubauen ist. Der Standard %s_seq nutzt den Namen, den Sie definieren, und hängt _seq an.
'ssl' Ein boolescher Wert, der definiert, ob eine SSL-Verbindung genutzt werden soll.

In Tabelle 15.5 finden Sie die Konstanten, die Sie im Feld portability nutzen können. Diese dienen dazu, die Portabilität zu erhöhen. Sie beeinflussen die Werte, die von den Datenbanken zurückgegeben werden. [Wie diese Schalter sich auf die einzelnen Datenbanken auswirken, können Sie unter http://pear.php.net/manual/en/package.database.db.intro-portability.php nachlesen. ]


Tabelle 15.5 Portabilitätsschalter
Konstante Bedeutung
DB_PORTABILITY_DELETE_COUNT Schaltet ein, dass alle Datenbanken die Anzahl der gelöschten Zeilen bei einem DELETE zurückgeben.
DB_PORTABILITY_ERRORS Vereinheitlicht die Fehlermeldungen.
DB_PORTABILITY_LOWERCASE Namen der Tabellen und Felder werden in Kleinschrift zurückgegeben.
DB_PORTABILITY_NONE Default-Einstellung, keine besondere Portabilität
DB_PORTABILITY_NULL_TO_EMPTY Konvertiert NULL-Werte in Abfrage-Ergebnissen in Leerstrings.
DB_PORTABILITY_NUMROWS Sorgt dafür, dass die Funktion numRows() in Kombination mit Oracle funktioniert.
DB_PORTABILITY_RTRIM Wendet die Funktion rtrim() auf alle Rückgabewerte an.
DB_PORTABILITY_ALL Schaltet alle Portabilitätsfeatures ein.

Die Methode connect() gibt Ihnen ein entsprechend initialisiertes DB-Objekt zurück, mit dem Sie auf die Datenbank zugreifen können. Sie können diese Werte auch nachträglich setzen, indem Sie aus dem DB-Objekt heraus die Methode setOption() aufrufen, als ersten Wert einen der Schlüssel übergeben und als zweiten den dazugehörigen Wert.

Nachdem Sie die Arbeit mit der Datenbank beendet haben, sollten Sie die Verbindung durch Aufruf der Methode disconnect() wieder trennen. Die Trennung erfolgt bei Ende des Scripts natürlich auch automatisch, aber je früher eine Ressource freigegeben wird, desto besser.

Bevor ich zur Nutzung der Datenbanktabellen komme, möchte ich kurz auf Sequenzen eingehen. Bei der Arbeit mit Datenbanken ist es üblich, die Tabellen mit einer Spalte zu versehen, in der eine eindeutige ID abgelegt wird. Diese ID ist meist eine fortlaufende Nummer, die auf den unterschiedlichen Datenbankarchitekturen auf verschiedenen Wegen erstellt werden kann. Haben Sie schon mit MySQL gearbeitet, werden Sie auto_increment kennen. Andere Hersteller wie beispielsweise Oracle nutzen eine Sequenz, von der Sie immer die nächste ID auslesen können.

Um hier eine Plattformunabhängigkeit zu erreichen, sind Sequenzen in PEAR::DB implementiert, die Ihnen immer eindeutige IDs zurückgeben. Eine Sequenz wird mit der Methode createSequence() angelegt. Die Sequenzen werden auf den einzelnen Datenbankarchitekturen unterschiedlich eingebunden, woraus resultiert, dass der erste Wert, den Sie erhalten, 0 oder 1 sein kann.

Den jeweils nächsten Wert können Sie mit nextId() auslesen.

$db=DB::connect($dsn); 
// Neue Sequenz anlegen 
$res=$db->createSequence('id_lieferant'); 
if (true == DB::isError($res)) 
{ 
   die ($res->getMessage()); 
} 
// Naechsten Wert auslesen 
$id = $db->nextId('id_lieferant'); 
if (true == DB::isError($id)) 
{ 
   die ($id->getMessage()); 
} 
$db->disconnect();

Mit diesem Beispiel würde eine neue Sequenz angelegt und der erste Wert ausgelesen. Beide Methoden benötigen also immer den Namen der Sequenz. Die Zahl, die Sie von der Methode nextId() erhalten, müssen Sie dann manuell in den SQL-Befehl einfügen, bevor Sie Daten einfügen.

Möchten Sie eine Sequenz wieder löschen, nutzen Sie dropSequence(), der Sie auch den Namen der entsprechenden Sequenz übergeben.

Um einen SQL-Befehl zur Datenbank zu schicken, können Sie query() nutzen. Die Methode bekommt den SQL-Befehl als ersten Parameter übergeben. Führen Sie ein SELECT aus, bekommen Sie die Daten in Form eines DB_result-Objekts zurück. Bei einem INSERT, einem UPDATE oder einem anderen Befehl, der keine Daten liefert, ist der Rückgabewert die Konstante DB_OK. Sollte ein Fehler auftreten, gibt die Methode ein DB_Error-Objekt zurück.

Führen Sie ein SELECT aus, kann es hilfreich sein, die Anzahl der zurückgegebenen Zeilen zu beschränken. Da nicht alle Datenbanken eine native Unterstützung für dieses Problem vorsehen, ist die Methode limitQuery() definiert. Auch sie bekommt den SQL-Befehl als ersten Parameter übergeben. Danach folgt aber eine Zahl, die definiert, welches die erste Zeile ist, die ausgelesen werden soll, und eine zweite Zahl, die festlegt, wie viele Zeilen ausgelesen werden sollen. Vor der Nutzung von limitQuery() sollten Sie mit

if (true == $db->provides('limit'))

testen, ob limitQuery() vom Treiber bzw. von der Datenbank unterstützt wird.

Wenn Sie herausfinden möchten, wie viele Zeilen von einem SQL-Befehl beeinflusst wurden, können Sie mit affectedRows() die Anzahl auslesen.

In Listing 15.1 finden Sie ein Beispiel zum Einfügen von Daten in eine Tabelle. Die Tabelle hat diesen Aufbau:

+----------+--------------+------+-----+---------+-------+ 
| Field    | Type         | Null | Key | Default | Extra | 
+----------+--------------+------+-----+---------+-------+ 
| id       | int(11)      |      | PRI | 0       |       | 
| vorname  | varchar(100) | YES  |     | NULL    |       | 
| nachname | varchar(100) | YES  |     | NULL    |       | 
| telefon  | varchar(100) | YES  |     | NULL    |       | 
+----------+--------------+------+-----+---------+-------+

Ein INSERT könnte wie folgt aussehen:

require_once('DB.php'); 
 
$dsn="mysql://user:geheim@localhost/datenbank"; 
 
$db = DB::connect($dsn); 
if (true == DB::isError($db)) 
{ 
   die ($db->getMessage()); 
} 
 
$id = $db->nextId('telefon'); 
if (true == DB::isError($id)) 
{ 
   die ($id->getMessage()); 
} 
 
$res = $db->query("INSERT INTO telefonbuch VALUES 
                      ($id, 'Paul', 'Paulsen','040/2619')"); 
if (true == DB::isError($res)) 
{ 
   die ($res->getMessage()); 
} 
$db->disconnect();

Listing 15.1 Einfügen von Daten

Um die Werte aus einem DB_result-Objekt auszulesen, stehen verschiedene Möglichkeiten zur Verfügung. Die einfachste Variante, um die Daten zu erhalten, ist fetchRow(). Die Methode gibt standardmäßig ein indiziertes Array mit den Werten einer Zeile zurück und null, wenn keine weiteren Werte im Ergebnis vorhanden sind. Da ein indiziertes Array nicht immer praktisch ist, können Sie den Typ des Rückgabewerts auch verändern. Übergeben Sie der Methode die Konstante DB_FETCHMODE_ASSOC, erhalten Sie ein assoziatives Array.

require_once('DB.php'); 
 
$dsn="mysql://user:geheim@localhost/geheim"; 
 
$db = DB::connect($dsn); 
if (true == DB::isError($db)) 
{ 
   die ($db->getMessage()); 
} 
 
$res = $db->query("SELECT * FROM telefonbuch"); 
if (true == DB::isError($res)) 
{ 
   die ($res->getMessage()); 
} 
 
echo '<table border="1">'; 
while ($data = $res->fetchRow(DB_FETCHMODE_ASSOC)) 
{ 
   echo '<tr>'; 
   echo "<td>$data[id]</td>"; 
   echo "<td>$data[vorname]</td>"; 
   echo "<td>$data[nachname]</td>"; 
   echo "<td>$data[telefon]</td>"; 
} 
echo '</table>'; 
$db->disconnect();

Listing 15.2 Auslesen von Daten mit PEAR::DB

Zusätzlich können Sie der Methode auch DB_FETCHMODE_OBJECT übergeben, womit ihr mitgeteilt wird, dass Sie ein Objekt erwarten. Hier wird standardmäßig PHPs stdClass genutzt. Möchten Sie eine eigene Klasse verwenden, sollten Sie diese mit der Methode setFetchMode() definieren, zu der Sie gleich noch mehr erfahren. Die Methode arbeitet intern mit einem Zeiger, auch Cursor genannt, der dafür sorgt, dass Sie Zeile für Zeile auslesen können. Allerdings können Sie der Methode als zweiten Parameter auch eine Zahl übergeben, die die Nummer der Zeile definiert, die Sie auslesen wollen.

Die zweite Methode, mit der Sie Daten aus einem Ergebnis-Objekt auslesen können, ist fetchInto(). Sie bekommt ein Array bzw. eine Variable als Parameter übergeben, in der die Methode die Werte zurückgibt. Nach dem Array können Sie noch eine der Konstanten, die bei fetchRow() erläutert wurden, sowie eine Zeilennummer angeben, wenn Sie das wünschen.

$res->fetchInto($daten,DB_FETCHMODE_ASSOC);

Solange eine Zeile ausgelesen werden konnte, gibt die Methode DB_OK als Wert zurück. Die Konstante ist als 1 definiert, so dass Sie die Methode auch direkt als Bedingung in einer Schleife nutzen können. Kann keine weitere Zeile gelesen werden, gibt die Methode false zurück.

while($res->fetchInto($daten)) 
{ 
   // Verarbeitung der Daten 
}

Hilfreich können auch noch die Methoden numCols(), numRows(), getRowCounter() und free() sein. numCols() und numRows() liefern Ihnen die Anzahl der Spalten und Zeilen im Ergebnis zurück. Verarbeiten Sie die Daten von einem SQL-Befehl, der mit limitQuery() zur Datenbank geschickt wurde, können Sie mit getRowCounter() in Erfahrung bringen, welche Zeile gerade verarbeitet wird.

Gerade bei umfangreichen Ergebnissen macht es Sinn, den Speicher, den das Abfrageergebnis belegt, möglichst schnell wieder freizugeben, was die Methode free() für Sie übernimmt.

Wie schon erwähnt, können Sie mit setFetchMode() auch den Modus festlegen, wie die Methoden fetchRow() und fetchInto() die Daten zurückliefern. Wichtig ist, dass setFetchMode() eine Methode des DB-Objekts und nicht des DB_result-Objekts ist. Sie muss also aufgerufen werden, bevor Sie den SQL-Befehl an die Datenbank schicken. Die Methode akzeptiert neben den schon bekannten Parametern DB_FETCHMODE_ASSOC und DB_FETCHMODE_OBJECT auch noch die Konstante DB_FETCHMODE_FLIPPED. Mit DB_FETCHMODE_FLIPPED werden die Zeilen und Spalten vertauscht.

Wenn Sie DB_FETCHMODE_OBJECT nutzen, können Sie in diesem Fall auch eine eigene Klasse angeben, die genutzt werden soll. Hierzu geben Sie als zweiten Parameter den Namen der Klasse an. Die Klasse muss allerdings so definiert sein, dass ihr das Ergebnis der Abfrage über den Konstruktor übergeben werden kann. Der Datensatz wird als Array an den Konstruktor übergeben.

class myResult 
{ 
   public $row; 
   public function __construct($res) 
   { 
      $this->row = $res; 
   } 
   public function __get($key) 
   { 
      return $this->row[$key]; 
   } 
   public function __set($key, $value) 
   { 
      $this->row[$key]=$value; 
   } 
} 
 
// Aufbau der Datenbankverbindung 
$db->setFetchMode(DB_FETCHMODE_OBJECT,'myResult'); 
// Abfrage der Daten 
$res = $db->query("SELECT * FROM telefonbuch"); 
// Liest jetzt ein Objekt aus 
$data = $res->fetchRow(); 
echo $data->id; 
echo $data->vorname; 
echo $data->nachname; 
echo $data->telefon;

Innerhalb des DB-Objekts sind aber auch noch Methoden definiert, mit denen Sie eine Abfrage ausführen können. Interessant an diesen Methoden ist, dass sie die gefundenen Werte direkt zurückgeben und nicht mithilfe der fetch-Methoden ausgelesen werden müssen. Die erste dieser Methoden, die alle mit get eingeleitet werden, ist getAll(). Sie gibt standardmäßig alle gefundenen Daten in einem verschachtelten, indizierten Array zurück. Die Daten der Zeilen werden also als indizierte Arrays zurückgegeben, die wiederum in ein indiziertes Array »verpackt« sind. Allerdings wird das Verhalten dieser Methode auch von setFetchmode() beeinflusst. Somit können die Daten der Zeilen auch als assoziative Arrays bzw. als Objekte zurückgegeben werden.

getAssoc() gibt alle Daten als assoziatives Array zurück. Hierbei fungieren die Inhalte der ersten selektierten Tabellenspalte als Schlüssel. Diese verweisen auf indizierte Arrays, in denen die restlichen Werte der Tabellenzeile zu finden sind. Das basiert natürlich auf der Annahme, dass die erste Spalte gleichzeitig der Primärschlüssel der Tabelle bzw. UNIQUE ist. Sollte das nicht der Fall sein und kommen Werte doppelt vor, ist das kein Problem. In so einem Fall verweist der Schlüssel dann auf ein indiziertes Array, in dem ein Feld für jede Zeile vorgesehen ist. Innerhalb dieses Feldes ist dann wieder ein Array mit den Werten der Zeile zu finden.

Auch diese Methode wird durch setFetchmode() beeinflusst. Im Modus DB_FETCHMODE_ASSOC werden die Zeilendaten als assoziatives Array zurückgegeben und bei der Nutzung des Modus DB_FETCHMODE_OBJECT als Objekt. [Zurzeit wird bei der Nutzung eines Objekts auch der Wert der ersten Spalte im Objekt zurückgegeben. ]

Auch die Methode getRow() wird vom Fetchmode beeinflusst. Sie liest nur die erste Zeile des Ergebnisses aus und liefert sie auf dem gewünschten Weg zurück.

Eine etwas ungewöhnliche Methode ist getCol(), mit der Sie eine Spalte aus dem Ergebnis auslesen können. Übergeben Sie der Methode nur einen SQL-Befehl als Parameter, erhalten Sie alle Werte der ersten Spalte als indiziertes Array zurück. Sie können der Methode aber noch eine Zahl als zweiten Parameter übergeben, um zu spezifizieren, welche Spalte Sie auslesen wollen. Mit 0 erhalten Sie die erste Spalte, mit 1 die zweite und so weiter.

Noch ausgefallener ist die Methode getOne(), die wiederum einen SQL-Befehl übergeben bekommt und nur den ersten Wert der ersten Zeile zurückgibt.


Rheinwerk Computing

15.1.1 prepare(  ) und execute(  )  downtop

Ein durchaus interessantes Feature ist die Möglichkeit, Abfragen mit der Methode prepare() vorzubereiten und dann mit execute() auszuführen. Die Abfrage, die an prepare() übergeben wird, kann dabei über Platzhalter verfügen. Diese Platzhalter werden dann von execute() durch konkrete Daten ersetzt, und der fertige SQL-Befehl wird dann direkt zur Datenbank geschickt.

Da dieses Feature nicht in allen Fällen unterstützt wird, können Sie mit der Methode provides() testen, ob diese Funktionalität vom aktuell genutzten Treiber unterstützt wird. Wenn Sie

$erg = $db->provides('prepare');

aufrufen und true zurückerhalten, können Sie das System unbesorgt nutzen.

Die Platzhalter, die prepare() akzeptiert, sind ?, ! und &. Benötigt Ihre Abfrage eines dieser Zeichen, so müssen Sie es mit einem Backslash entwerten.

Wird ein Fragezeichen durch einen Wert ersetzt, so wird dieser erst »escaped«. Es werden also alle Zeichen entwertet, die bei der Nutzung der selektierten Datenbank problematisch sein könnten.

Nutzen Sie ein Ausrufungszeichen als Platzhalter, wird der übergebene Wert direkt übernommen, ohne dass er weiter bearbeitet wird.

Besonders interessant ist das Et-Zeichen bzw. kaufmännische Und. In diesem Fall wird der String, der für diesen Platzhalter übergeben wird, als Dateiname gewertet. Die Datei wird eingelesen, und der Inhalt der Datei ersetzt dann den Platzhalter.

Nachdem prepare() den Rohbau der Abfrage verarbeitet hat, erhalten Sie eine Referenz auf den vorbereiteten Befehl zurück. Diese kann dann an execute() übergeben werden. Als zweiten Parameter erwartet diese Member-Funktion den oder die Werte, die in den SQL-Befehl eingefügt werden sollen. Nutzen Sie nur einen Wert, kann er direkt übergeben werden, und bei mehreren Platzhaltern erwartet die Methode ein Array.

Nutzung mit einem Platzhalter:

$prep = $db->prepare('INSERT INTO ids VALUES (?)'); 
$res = $db->execute($prep, 110);

Nutzung mit mehreren Platzhaltern:

$dat = array ( 
               12, // id des Eintrags 
               'Homer', // Vorname 
               'Simpson', //Nachname 
               '001 1221 1224375' //Telefonnummer 
        ); 
$prep = $db->prepare('INSERT INTO telefonbuch 
                                  VALUES (?,?,?,?)'); 
$res = $db->execute($prep, $dat);

Bezüglich der Rückgabewerte verhält execute() sich wie query(). Möchten Sie mehrere Abfragen ausführen, könnten Sie natürlich das execute() in einer Schleife ausführen, aber in solchen Fällen ist es eleganter, die Methode executeMultiple() zu nutzen. executeMultiple() bekommt als ersten Wert wieder den Rückgabewert eines prepare() übergeben. Der zweite Wert ist dann aber ein zweidimensionales Array, in dem die einzelnen Werte übergeben werden. So ein Array wäre dann beispielsweise so aufzubauen:

$dat = array ( 
         array(12,'Homer','Simpson','001 1221 1224375'), 
         array(13,'Marge','Simpson','001 1221 1224375'), 
         array(14,'Monty','Burns','001 1221 665665'), 
       );

Neben prepare() und execute() sind auch noch die Methoden autoPrepare() und autoExecute() definiert. Diese ersparen es Ihnen, selbst einen INSERT- oder einen UPDATE-Befehl tippen zu müssen.

autoPrepare() bekommt als ersten Parameter den Namen der Tabelle übergeben, auf die sich der Befehl beziehen soll. Danach folgt ein Array mit den Namen der Spalten, auf die sich der Befehl beziehen soll. Um ein INSERT auszuführen, müssen Sie als nächsten Parameter die Konstante DB_AUTOQUERY_INSERT übergeben und für ein UPDATE nutzen Sie die Konstante DB_AUTOQUERY_UPDATE. Die Methode gibt die Referenz auf einen vorbereiteten Befehl zurück, die Sie dann an execute() übergeben können.

$tabelle = 'telefonbuch'; 
$spalten = array ('id', 'vorname', 'nachname', 'telefon'); 
$werte = array (12, 'Homer', 'Simpson', '001 1221 1224375'); 
$prep = autoPrepare($tabelle, $spalten, DB_AUTOQUERY_INSERT); 
$res = execute($prep, $werte);

Da bei einem UPDATE häufig eine WHERE-Klausel benötigt wird, können Sie die eigentliche Bedingung noch nach der Konstante anhängen, also beispielsweise mit 'id = 12'.

Die Methode autoExecute() geht noch einen Schritt weiter. Sie stellt eine Kombination von autoPrepare() und execute() dar. Sie konstruiert also den Befehl und schickt ihn direkt zur Datenbank. Der erste Parameter ist auch hier wieder der Name der Tabelle. Das Array, das als zweiter Parameter übergeben wird, ist in diesem Fall assoziativ. Die Schlüssel stellen die Namen der Spalten dar, und die Werte werden als Werte in das INSERT bzw. das UPDATE übernommen. Die nachfolgenden Parameter sind mit denen von autoPrepare() identisch.

Wenn Sie einen Befehl nutzen, der mit prepare() oder autoPrepare() verarbeitet wurde, belegt dieser natürlich auch Speicher. Diesen können Sie mit der Methode freePrepared() wieder freigeben:

$prep = $db->prepare('INSERT INTO ids VALUES (?)'); 
// Verarbeitung des vorbereiteten Befehls 
$db->freePrepared($prep); // Speicherfreigabe

Auch wenn diese Befehle alle sehr einfach zu nutzen sind, vergessen Sie bitte nicht zu prüfen, ob ein Fehler aufgetreten ist. Beachten Sie des Weiteren, dass die Funktionen alle nur darauf ausgelegt sind, konstante Werte zu verarbeiten. Die Nutzung von SQL-Befehlen in diesen Konstrukten ist nicht vorgesehen.


Rheinwerk Computing

15.1.2 Informationen über Tabellen und Datenbank  downtop

In einigen Fällen kann es sehr hilfreich sein, Informationen über einzelne Tabellen oder die Datenbank auszulesen.

Die Methode tableInfo() teilt Ihnen mit, wie eine Tabelle aufgebaut ist, und gibt die Informationen in einem Array zurück. Mit dem Befehl

print_r($db->tableInfo('telefonbuch'));

würden für die oben beschriebene Tabelle diese Daten ausgegeben:

Array 
( 
    [0] => Array 
        ( 
            [table] => telefonbuch 
            [name] => id 
            [type] => int 
            [len] => 11 
            [flags] => not_null primary_key 
        ) 
 
    [1] => Array 
        ( 
            [table] => telefonbuch 
            [name] => vorname 
            [type] => string 
            [len] => 100 
            [flags] => 
        ) 
// Hier kommen noch zwei weitere Felder 
)

Für jede Spalte werden also alle relevanten Informationen zurückgegeben. Um zusätzliche Informationen auszulesen, können Sie der Methode noch eine weitere Konstante übergeben. Mit DB_TABLEINFO_ORDER wird dem Array mit den Spalten ein Element namens num_fields vorangestellt, das die Anzahl der Felder enthält. Des Weiteren wird ein zusätzliches Array-Element eingefügt, das Informationen über die Spalten enthält:

    [order] => Array 
        ( 
            [id] => 0 
            [vorname] => 1 
            [nachname] => 2 
            [telefon] => 3 
        )

Bei Nutzung von DB_TABLEINFO_ORDERTABLE wird das Element num_fields eingefügt und zusätzlich eine Zusammenfassung der Tabellenspalten wie die folgende. Dabei werden die Informationen weiter verschachtelt, um sie besser mit anderen Daten kombinieren zu können.

  [ordertable] => Array 
        ( 
            [telefonbuch] => Array 
                ( 
                    [id] => 0 
                    [vorname] => 1 
                    [nachname] => 2 
                    [telefon] => 3 
                ) 
        )

Hinter den Namen der Spalten, die als Schlüssel dienen, wird zusätzlich die Ordnungsnummer zurückgegeben.

Besonders spannend ist die Methode getListOf(), mit der Sie eine Liste der Benutzer ('user'), der Datenbanken ('databases'), Views ('views') und Stored Procedures ('functions') erhalten können. Die Daten werden dann jeweils als Array zurückgegeben. Das ist zumindest dann der Fall, wenn der Account, mit dem Sie sich angemeldet haben, berechtigt ist, diese Informationen auszulesen. Mit

$tables=$db->getListOf('tables');

würden Sie die Namen aller Tabellen auslesen, die in der selektierten Datenbank vorhanden sind.


Rheinwerk Computing

15.1.3 Quoting/Escaping  downtop

Übernehmen Sie Werte aus einer Abfrage oder einer externen Datenquelle, so müssen Sie sich immer bewusst sein, dass es viele Gefahren durch Hacker oder gefährliche Scripts gibt. Das heißt, Daten, die Sie übernehmen, stellen immer eine potenzielle Bedrohung dar und sollten daher immer »gequoted« werden, damit sie keine Gefahr für Ihre Datenbank sind.

Die Methode quoteSmart() bekommt einen Wert übergeben, der in einem SQL-Befehl genutzt werden soll. quoteSmart() sorgt aber nicht nur dafür, dass Anführungs- und andere Zeichen korrekt entwertet werden, sondern die Methode konvertiert die Daten auch so, dass sie mit der gewählten Datenbank korrekt verwendet werden können. Möchten Sie beispielsweise den booleschen Wert true in einer Abfrage nutzen, so erwartet eine Datenbank den Text TRUE, wohingegen eine andere T oder vielleicht eine 1 erwartet. Diese und andere Konvertierungen führt die Methode auch durch.

$wert = $db->quoteSmart($wert);

Eine vereinfachte Variante steht in Form von escapeSimple() zur Verfügung. Diese Methode entwertet nur Anführungs- und Sonderzeichen, konvertiert aber keine Werte. Somit würde aus 'wie geht's' beispielsweise ein 'wie geht\'s', wenn die Datenbank das erfordert.

Wenn Sie auch die Namen von Spalten aus einer externen Quelle übernehmen, macht es Sinn, diese mit Anführungszeichen zu versehen. Um dies für Feldbezeichner vorzunehmen, ist die Methode quoteIdentifier() vorgesehen, die den Namen einer Spalte übergeben bekommt und ihn korrekt konvertiert zurückgibt.

$spalte = $db->quoteIdentifier($spalte);

Rheinwerk Computing

15.1.4 Transaktionsorientierung  toptop

Unterstützt die verwendete Datenbank Transaktionen, können Sie diese über PEAR::DB auch nutzen. Eine Transaktionsorientierung bedeutet, dass ein SQL-Befehl erst »temporär ausgeführt« wird. Erst dann, wenn Sie den oder die Befehle explizit bestätigen, wird er auch mit dem Echtdatenbestand ausgeführt. Die Idee dahinter ist, dass inkonsistente Datenbestände verhindert werden sollen, wenn Client oder Server abstürzen oder ein Fehler auftritt.

Leider ist die Nutzung nicht ganz datenbankunabhängig, so dass zuerst zu prüfen ist, ob die von Ihnen genutzte Datenbank Transaktionen unterstützt. Am einfachsten nutzen Sie die Methode provides() und übergeben ihr den String 'transactions'. Liefert sie false zurück, unterstützt die Datenbank keine Transaktionen. Erhalten Sie true zurück, kann es sein, dass die Datenbank Transaktionen unterstützt. So erhalten Sie bei der Nutzung einer MySQL-Datenbank beispielsweise true, aber MySQL unterstützt nur dann Transaktionen, wenn die Tabelle vom Typ InnoDB ist. Wollen Sie also ganz sicher gehen, bleibt keine andere Möglichkeit, als auszuprobieren, ob Transaktionen unterstützt werden, indem Sie einfach mal versuchen, die Auswirkung eines SQL-Befehls rückgängig zu machen.

Zuerst müssen Sie die Datenbank in einen bekannten Zustand für die Transaktionen bringen, da Sie nicht wissen, ob die Transaktionen von allein bestätigt werden. Rufen Sie die Methode autoCommit() mit dem Parameter false auf, wird die automatische Bestätigung ausgeschaltet, und mit true würde sie eingeschaltet. Haben Sie SQL-Befehle zur Datenbank geschickt, so können Sie diese entweder mit der Methode rollback() wieder verwerfen, oder Sie rufen commit() auf, um die Richtigkeit zu bestätigen und die Transaktion abzuschließen.

$db->autoCommit(false); 
// SQL-Befehle ausfuehren 
// Waren alle Befehle in Ordnung? 
if (true === $alleBefehleOK) 
{  // Daten bestaetigen 
   $db->commit(); 
} 
else 
{  // Transaktionen rueckgaengig machen 
   $db->rollback(); 
}
 <<   zurück
     
  Zum Katalog
Zum Katalog: PHP PEAR
PHP PEAR
Jetzt bestellen!
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: PHP 5.6 und MySQL 5.7






 PHP 5.6 und
 MySQL 5.7


Zum Katalog: Einstieg in PHP 5.6 und MySQL 5.6






 Einstieg in PHP 5.6
 und MySQL 5.6


Zum Katalog: Responsive Webdesign






 Responsive Webdesign


Zum Katalog: Moderne Websites entwickeln






 Moderne Websites
 entwickeln


Zum Katalog: MySQL 5.6






 MySQL 5.6


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Rheinwerk Verlag GmbH 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de