Abstraktionsvermögen

Datenbankprogrammierung ist für Webmaster längst Pflicht. Wer mit PHP relational gespeicherte Daten verarbeiten will, kann seit einiger Zeit auf DBMS-unabhängige Hilfe zurückgreifen.

vorlesen Druckansicht 6 Kommentare lesen
Lesezeit: 12 Min.
Von
  • Jochem Huhmann
Inhaltsverzeichnis

PHP hat sich als eine Sprache für datenbankgestützte Webapplikationen durchgesetzt. Obwohl PHP von Haus aus viele DBMS unterstützt, ist normalerweise schon im Anfangsstadium der Entwicklung die Festlegung auf ein DBMS notwendig. Unter Umständen bedeutet dies aber eine Einschränkung, denn nicht jedes Datenbanksystem ist überall vorhanden, und häufig steht man vor der Aufgabe, eine Applikation in bestehende Systeme mit vorliegenden Daten zu integrieren.

PHP verwendet nicht nur verschiedene Funktionen zum Verbinden mit Datenbanken, teilweise erwarten diese Funktionen auch unterschiedliche Parameter. Wäre dies das einzige Hindernis, ließe sich dies Manko mit Wrappern um die entsprechenden Funktionen umgehen, zur Verwendung einer anderen Datenbank wäre nur deren Anpassung nötig, keine Änderung der eigentlichen Applikation. Kaum eine kommt aber damit aus, sich mit der Datenbank zu verbinden und eine Tabelle auszuwählen.

Schon bei einfachen SQL-Queries wie „gib die ersten 10 Datensätze aus“ finden sich drastische Unterschiede: Während der SQL-Server ein SELECT TOP 10 * FROM table möchte, verlangt MySQL nach SELECT * FROM table LIMIT 10. Ähnlich sieht es bei Zeit und Datum aus, und kompliziert geht es bei Features zu, die eine der unterstützten Datenbanken nicht anbietet. Will man all dies in eine generische API verpacken, fragt man sich, ob nicht andere schon vor ähnlichen Schwierigkeiten gestanden und eine Lösung gefunden haben. Und in der Tat, es gibt dafür nicht nur eine in Library-Form gegossene Lösung, sondern (wie sollte es anders sein) gleich mehrere.

An Microsofts ActiveX Data Objects angelehnt, aber plattformunabhängig und ebenfalls als Python-Version nutzbar, bietet das bei Sourceforge angesiedelte Projekt ADOdb Funktionen/Methoden, diverse DBMS anzusprechen (siehe Tabelle „Schnittstelleneigenschaften“). Ab Seite 116 der aktuellen Print-Ausgabe geht es dezidiert um dieses Werkzeug, anhand dessen exemplarisch gezeigt wird, wie solche Schnittstellen sich nutzen lassen. Dieser Artikel vergleicht weitere drei Projekte: Metabase, PEAR DB und PEAR MDB (bei PEAR handelt es sich um das PHP Extension and Application Repository, pear.php. net).

Schnittstelleneigenschaften
Schnittstelle ADOdb PEAR DB PEAR MDB Metabase
Eigenschaften
OO-API x x x x
Prepare/Execute x x x x
Sequences x x x x
Limit x x x x
Transaktionen x x x x
Replace-Emulation x x
Large Object Support x x
RDBMS-Management
x x
Datentypkonvertierung x x x
DBMS-Support
Access x
dBase x x
DB2 x
FrontBase x x
InterBase x x x
Informix x x x
MiniSQL x x x
MS SQL x x x x
MySQL x x x x
Oracle x x x x
ODBC x x x
PostgreSQL x x x x
SAP DB x
SQLite x x x x
Sybase x x x
Lizenz BSD und LGPL PHP-Lizenz PHP-Lizenz BSD-ähnlich

Metabase (von Manuel Lemos) unterstützt wichtige DBMS (siehe die genannte Tabelle). Die Bibliothek unterliegt einer BSD-ähnlichen Lizenz und kann damit (wie die anderen Lösungen) in freien und kommerziellen Anwendungen ohne Stolpersteine genutzt werden. Der Zugriff auf die Funktionen erfolgt wahlweise über globale Funktionen oder eine objektorientierte Schnittstelle.

Den je nach DBMS unterschiedlichen Datentypen begegnet Metabase mit eigenen, die Anwender durchgängig verwenden können und die Metabase in für die verwendete Datenbank passende Typen konvertiert. Das Werkzeug unterstützt Text (mit und ohne Längenbegrenzung, 8-Bit-sicher mit automatischem Quoten von Sonderzeichen), Boolean (1 oder 0, je nach Datenbank konvertiert zu Ganzzahl- oder einstelligen Textfeldern), Integer, Dezimal, Fließkommazahlen, Datum (ISO-8601), Zeit (dito) und schließlich Large Object Data Types als CLOB (Character Large Objects, ausschließlich ASCII-Zeichen) und BLOB (Binary Large Objects).

Die Installation beschränkt sich auf das Auspacken des Pakets. Der Verbindungsaufbau zur Datenbank

include("metabase/metabase_interface.php");
include("metabase/metabase_database.php");
$error = MetabaseSetupDatabase(array(
"Type"=>"mysql",
"User"=>"username",
"Password"=>"password"
), $database);

erzeugt ein passendes Objekt für das verwendete RDBMS und liefert in $database eine Ganzzahl, die diese Verbindung identifiziert. Ein eventueller Fehler hinterlässt die Fehlermeldung in $error. Die zu verwendende Datenbank kann der Anwender nun mit MetabaseSetDataBase() auswählen.

Queries lassen sich direkt ausführen oder als „Prepared Queries“ vorbereiten. Letztere haben den Vorteil schnellerer Verarbeitung, aber nur wenige DBMS unterstützen sie. Metabase emuliert vorbereiteten Anfragen allerdings bei Bedarf, so dass man sie unabhängig vom Datenbanktyp nutzen kann.

$query = "SELECT name,id FROM kunden WHERE name=heise";
$result = MetabaseQuery($database, $query);

Als Prepared Query stellt sich dies wie in Listing 1 dar. Es bereitet eine Anfrage vor, wobei die später einzufügenden Werte mit „?“ markiert sind, auf die MetabaseQuerySetText() mit Ganzzahlen (beginnend mit 1) Bezug nimmt. MetabaseQuerySetText() konvertiert gleichzeitig die Daten passend für die Datenbank, analoge Funktionen für weitere Datentypen sind vorhanden.

Mehr Infos

Listing 1: Prepared Query

$query="SELECT name,password FROM kunden WHERE name=?";
$prepared_query=MetabasePrepareQuery($database, $query);
MetabaseQuerySetText($database,
$prepared_query,1,"heise");
$result=MetabaseExecuteQuery($database, $prepared_query)

Für die Konvertierung in die andere Richtung existieren zusätzlich zur generischen Funktion MetabaseFetchResult(), die die Daten so liefert, wie sie in der Datenbank stehen, Funktionen wie MetabaseFetchBooleanResult() oder MetabaseFetchDateResult() zum passenden Konvertieren der Daten (siehe Listing 2). Auf diese Weise ist sichergestellt, dass das Datum innerhalb der Applikation unabhängig von der Repräsentation in der Datenbank immer in derselben Form vorliegt.

Mehr Infos

Listing 2: Automatische Datumskonvertierung

$query="SELECT name, lastcontact FROM kunden WHERE name=heise";
$result=MetabaseQuery($database, $query);

$name=MetabaseFetchResult($database, $result,0,"name");
$lastcontact=MetabaseFetchDateResult($database, $result,0,"lastcontact")

Metabase kennt Funktionen zur Bereichsauswahl der zurückgegebenen Ergebniszeilen einer Anfrage (beispielsweise für die seitenweise Ausgabe in Tabellenform), zur spaltenweise Ausgabe der Ergebnisse sowie zur Ausgabe aller Zeilen und Spalten in ein zweidimensionales Array. Weiterhin können Anwender Datenbanken und Tabellen erzeugen, löschen und ändern, ohne auf direkte SQL-Kommandos zurückgreifen zu müssen.

Ein spezielles Feature sind XML-Schemata zur Generierung von Datenbanken und Tabellen. Der umgekehrte Weg, nämlich aus einer bestehenden Datenbank ein solches XML-Schema zu erzeugen, ist bisher nur für MySQL implementiert, was den Nutzen dieser Einrichtung zur Übertragung von Datenbankdefinitionen etwas einschränkt. Bringt man aber die Disziplin auf, das Datenbankschema nur in diesen XML-Dateien zu ändern, hat man immerhin eine saubere Grundlage zur automatischen Dokumentation der Schemata.

Die Dokumentation von Metabase ist zwar umfangreich, leidet aber an einer gewissen Geschwätzigkeit und schlechten Gliederung, eine zusätzliche knappe Referenz wäre sehr hilfreich.

Auch das „PHP Extension and Application Repository“ (kurz PEAR), das seit 1999 eine strukturierte Bibliothek von Open-Source Code für PHP-Entwickler anbietet, beinhaltet eine DB-Abstraktionsschicht namens PEAR DB.

Im Vergleich zu PEAR MDB, ADOdb oder Metabase kommt PEAR DB deutlich schlanker daher, hat aber verglichen mit Metabase eine deutlich komfortablere objektorientierte Programmierschnittstelle und unterstĂĽtzt eine beeindruckende Liste an Datenbanken (siehe die genannte Tabelle).

Auf einem vollständig installierten System mit PHP besteht eine hohe Wahrscheinlichkeit, PEAR samt PEAR DB schon vorzufinden. Falls nur die PEAR-Basis vorhanden ist, nicht aber PEAR DB, reicht ein pear install DB. Ist PEAR gar nicht vorhanden, kann man das Basispaket und das DB-Modul per Download holen, auspacken und das Verzeichnis zum include_path hinzufügen.

Zum Aufbau einer Datenbankverbindung erstellt man einen „Data Source Name“ und erzeugt damit ein PEAR-DB-Objekt. Listing 3 baut eine Verbindung auf und gibt im Fehlerfalle eine Meldung aus. Zusätzlich zur getMessage()-Methode des Objekts gibt es noch weitere Möglichkeiten, bei Fehlern an mehr Informationen zu kommen, beispielsweise liefert getDebugInfo() die „native“ Fehlermeldung des jeweiligen DBMS.

Mehr Infos

Listing 3: PEAR-DB-Verbindungsaufbau

require_once("DB.php");
$dsn = "mysql://benutzername:passwort@host/datenbankname";
$database = DB::connect($dsn);

@lf:if (DB::isError($database)) { die ($database->getMessage()); }

SQL-Anfragen lassen sich direkt ausführen oder in einer Prepared Query speichern und später ausführen:

$sql = "SELECT id FROM kunden";
$result =& $database->query($sql);

oder fĂĽr eine Prepared Query:

$sql  = "SELECT id FROM kunden WHERE name = ?";
$data = "heise";
$result = $database->query($sql, $data);

Um an die einzelnen Zeilen des Ergebnisses zu kommen, bietet das result-Objekt zwei Methoden: fetch Row() und fetchInto(), wobei Letztere besonders komfortabel ist:

while ($result->fetchInto($row)) {
$id = $row[0];
}

Weitere nĂĽtzliche Methoden dieses Objekts sind numRows() und affected Rows() - beide geben die Anzahl der von der Query ausgegebenen beziehungsweise betroffenen Zeilen aus - und numCols() zur Ausgabe der von der Query gelieferten Spalten.

PEAR DB unterstützt Commit und Rollback bei RDBMS, die dies anbieten, kennt eine einfache Methode zur Erstellung von Sequences (garantiert eindeutige IDs, ähnlich AUTO_INCREMENT bei MySQL), emuliert LIMIT für alle unterstützten Datenbanken und wirkt insgesamt überaus aufgeräumt. Die vollständige Dokumentation trägt zu diesem Eindruck ebenso bei wie die konsequente und systematische Benennung der Objekte und Methoden, die einen zweiten Blick in die Dokumentation oft überflüssig macht.

Allerdings zeigt sich bei PEAR DB ein Mangel, unter dem solche Abstraktionsschichten notorisch leiden: Geht die Abstraktion nicht weit genug, sodass der Entwickler letztlich doch auf spezielle SQL-Eigenheiten und Datentypen der jeweiligen Datenbank Rücksicht nehmen muss, besteht die Gefahr, sich letztlich nur neue Komplexität aufzuhalsen, ohne das gewünschte Ergebnis zu erreichen: von der jeweiligen Datenbank unabhängig zu sein. Immerhin hält sich bei PEAR DB die Komplexität nicht nur in engen Grenzen, es bietet sogar durchaus genug Komfort, um es auch bei Beschränkung auf einen Datenbanktyp gewinnbringend einzusetzen.

PEAR MDB ist das Ergebnis eines Versuchs, Metabase mit PEAR DB zu vereinen und damit die Nachteile von Metabase (mangelnde Geschwindigkeit aufgrund der Kompatibilität zu PHP3 und wenig elegante API) zu beheben. Die Schnittstelle existiert mittlerweile in zwei Versionen: Eine API-stabile Version 1.3 und eine in der Entwicklung befindliche, aber schon brauchbare Version 2.0beta (Paket MDB2), die weitere Anpassungen an die OO-Features von PHP 5 bietet. Wer PHP 4 einsetzt, sollte sich an der Version 1.3 versuchen. MDB unterstützt ausreichend viele DBMS.

Bezüglich der Installation ist ähnlich vorzugehen wie bei PEAR DB. Im Falle einer vollständig manuellen Installation müssen Webmaster darauf achten, dass MDB neben dem PEAR-Basis-Paket noch das Paket XML_Parser benötigt. Nach der Aufnahme des Verzeichnisses, in dem die Pakete liegen, in den include_path steht dem Einsatz nichts mehr im Wege.

PEAR MDB bietet dieselben Funktionen mit derselben Programmierschnittstelle wie PEAR DB, zusätzlich aber einen Großteil der Metabase-Funktionen. Hier beginnt aber zumindest die Dokumentation zu schwächeln: Während sie praktisch identisch mit der des DB-Pakets ist und dementsprechend mit keinem Wort auf die von Metabase geerbten Features eingeht, enthält das Paket selbst ein von der Metabase-Dokumentation abgeleitetes Tutorial, das jedoch kaum als Referenz dienen kann. Im README wiederum empfiehlt der Autor, in der Datei metabase_wrapper.php die neuen Funktionsnamen ausfindig zu machen und sie anschließend in der Metabase-Dokumentation nachzuschlagen.

Glücklicherweise ist der Code von besserer Qualität als die Dokumentation, für praktischen Einsatz langt MDB durchaus. Die von PEAR DB übernommenen grundlegenden Funktionen sind komfortabel zu benutzen und auch die von Metabase geerbten weitergehenden Funktionen, etwa zum Konvertieren von Datentypen, fügen sich durchaus nahtlos ein. Trotzdem hinterlässt die unzureichende Dokumentation einen unguten Nachgeschmack.

Eins ist klar: Wer sich von der Festlegung auf ein DBMS durch den Einsatz einer Abstraktionsschicht befreien möchte, legt sich dafür auf eine solche Schicht fest. Sollte man zu spät feststellen, auf das falsche Pferd gesetzt zu haben, kann die Umstellung sich als aufwendig erweisen. Es empfiehlt sich daher dringend, sich über die benötigten Features klar zu werden und ausführlich mit den gewünschten Zieldatenbanken zu prüfen. Praktisch bedenkenlos empfehlen kann man im Grunde nur PEAR DB - die Funktionen sind überschaubar, komfortabel einsetzbar und auch bei Festlegung auf ein DBMS nützlich, die Dokumentation gut. Für MDB könnte dasselbe gelten, wäre nur die Dokumentation besser. Auf der anderen Seite sind die DB-Managementfunktionen und XML-Schemata von MDB und Metabase sonst nirgendwo zu haben.

Mehr Infos

Ein Wort zu Performancefragen: Bei einfachen Benchmarks (siehe „Online-Ressourcen“) zeigen sich klare Geschwindigkeitsvorteile für die nativen Funktionen. Von den hier vorgestellten Lösungen ist MDB mit etwa 50 % Overhead gegenüber den nativen MySQL-Funktionen die schnellste (ADOdb ist noch etwas schneller), PEAR DB kommt schon auf rund 150 % und Metabase gar auf 300 % Overhead. Allerdings entscheidet die Ausführungsgeschwindigkeit des PHP-Codes selten über die Geschwindigkeit einer Webapplikation, den Flaschenhals bildet meist das DBMS, das die Daten nicht schnell genug liefert. Durch Beschleunigung des PHP-Codes die Datenbank mit noch mehr Zugriffen zu belasten, führt selten zu spürbaren Performancegewinnen und kann sogar zu paradoxen Effekten wie weiteren Geschwindigkeitseinbußen führen.

Für PHP 5 ist mit Creole eine weitere Abstraktionsschicht für den portablen Datenbankzugriff entstanden, die auf den hier vorgestellten aufsetzt. Unter creole.phpdb.org/wiki/ finden sich Einzelheiten zu dem, was die Entwickler darin umgesetzt haben. iX stellt Creole in einer späteren Ausgabe gesondert vor.

Jochem Huhmann
ist freiberuflicher Autor und Berater.

Mehr Infos

iX-TRACT

  • Mit den Schnittstellen Metabase sowie PEAR DB und MDB existieren drei vergleichbare Werkzeuge, DBMS in PHP unabhängig vom verwendeten System anzusprechen.
  • Sämtlich mit einer objektorientierten API versehen, erlauben die Werkzeuge Prepared Queries, Transaktionen und die automatische Konvertierung von Daten in native DBMS- Formate.
  • Wen die durch die Schnittstellen zwangsläufig entstehende Zusatzlast nicht abschreckt, dem bieten alle drei ausreichende Eigenschaften. Das gilt nicht immer fĂĽr die Dokumentation.
Mehr Infos

iX-Wertung

ADOdb
(+) wenig Umlernzeit fĂĽr ADO-Kenner
(+) weitgehende Abstraktion von DB-Eigenheiten
(+) Treiber fĂĽr zahlreiche Datenbanken
(-) mehrere gleichzeitige Verbindungen nur umständlich möglich

Metabase
(+) weitgehende Abstraktion von RDBMS-spezifischen Eigenheiten
(+) XML-Schemata zur Definition von Datenbankstrukturen
(-) relativ langsam

PEAR MDB
(+) unterstĂĽtzt nahezu dieselben Features wie Metabase
(+) relativ schnell
(+) komfortable objektorientierte API
(+) dank PEAR auf vielen Systemen ohne weitere Installation vorhanden
(-) uneinheitliche Dokumentation

PEAR DB
(+) relativ schnell
(+) komfortable objektorientierte API
(+) dank PEAR auf vielen Systemen ohne weitere Installation vorhanden
(+) gute und vollständige Dokumentation
(-) nur unvollständige Abstraktion von RDBMS-Spezifika

(hb)