Vorwort
Als PHP 5 vor über 3 Jahre eingeführt wurde, brachte es eine ganze Reihe Neuerungen mit sich. Richtig durchgesetzt haben sich nur wenige, obwohl die meisten dieser Neuerungen das Programmiererleben erleichtern und/oder mächtigere Werkzeuge sind als sie es noch in den Vorgängerversionen waren. Ein Grund, der wichtigste Grund mag sein, dass viele Provider den Umstieg auf PHP 5 scheuten, weil dadurch viele Scripts der Kunden ihren Dienst versagten.
Zum Glück haben sich die Zeiten geändert und PHP 5 ist mittlerweile bei den meisten Providern, zumindest auf Nachfrage, verfügbar. Spätestens ab August 2008 wird PHP 4 nach und nach verschwinden, weil PHP.net den Support bereits zum Ende des Jahres 2007 eingeläutet hat. Es wird also höchste Zeit sich einige der wichtigsten Neuerungen von PHP 5 anzuschauen, wenn man nicht den Anschluss verpassen möchte. Eine dieser neuen Errungenschaften ist MySQLi.
Das i in MySQLi steht für
improved, was übersetzt
verbessert bedeutet. Das die neue MySQLi Erweiterung tatsächlich diverse Vorteile mit sich bringt sollte nach diesem kleinen Einführungstutorial offensichtlich werden.
Im Internet und in Büchern findet man schon diverse Tutorials zu diesem Thema, aber leider findet man Informationen nur häppchenweise auf zig Quellen verteilt. Deshalb versuche ich mit diesem Tutorial die wichtigsten Funktionen und die Handhabung zu bündeln, damit man sich nicht alles wie ein Eichhörnchen zusammen suchen muss.
Voraussetzungen
Um diesem Tutorial folgen zu können sind einige Voraussetzungen zu erfüllen:
- PHP 5 (optimalerweise PHP 5.2.5 oder höher)
- Aktivierte MySQLi Unterstützung (erfährt man über phpinfo)
- MySQL 4.1 oder höher (optimal 5.x oder höher)
- Zumindest Grundlagenwissen im Umgang mit PHP und MySQL
- Kenntnisse über objektorientierte Programmierung in PHP erleichtern das Verstehen dieses Tutorials
- ... und natürlich Lernbereitschaft
Die MySQL Erweiterung von PHP war bisher ein Sammelsurium loser Funktionen, die nur eine prozedurale Programmierung ermöglichte. Dies zwang Programmierer dazu sich eigene Klassen für das DB Handling zu schreiben. Durch PHP 5 ist es nun aber möglich auch objektorientiert mit der DB zu kommunizieren, weil die MySQLi Erweiterung als Klasse (oder besser gesagt als Klassen-Paket) daher kommt und einheitliche Voraussetzungen für alle schafft. Um den Umstieg von MySQL nach MySQLi zu erleichtern, ist es aber immer noch möglich prozedural auf die Erweiterung zuzugreifen. Das widerspricht allerdings etwas dem "PHP 5 Spirit" der, nicht zuletzt durch die SPL und MySQLi, immer mehr in Richtung OOP geht. Aus diesem Grund werden die Beispiele in diesem Tutorial in guter OOP Manier präsentiert.
MySQL vs MySQLi
Werfen wir mal einen Blick auf einige Vorteile von MySQLi gegenüber MySQL.
- Der OOP Zugriff ist einheitlich, wodurch arbeiten im Team oder an Fremdprojekten vereinfacht wird
- Größere Geschwindigkeit - Laut Hersteller kann ein vielfaches an Geschwindigkeit erreicht werden.
- Verbesserte Sicherheit - Für die PHP-MySQL Verbindung wird eine SSH-ähnliche Authentifizierung benutzt
- Das neue Binärprotokoll von MySQL wird verwendet. Dadurch stehen mehr Funktionen zur Verfügung und die Verarbeitung ist schneller und effizienter
- Keine Standardverbindung und Prepared Statements erzwingen eine saubere und somit sicherere Programmierung
- Verbesserte Trace- und Debug-Funktionen
- Multi-Queries werden unterstützt
Dies sind nur einige der Gründe, die im übrigen sowohl aus dem Internet, der Online Doku aber auch aus PHP 5 Bücher stammen, die einen Umstieg auf MySQLi rechtfertigen.
Die MySQLi Erweiterung bietet aber auch wesentlich mehr Funktionen als die "klassische" MySQL Erweiterung. Das erkennt man schon daran, dass für MySQL knapp 50 prozedurale Funktionen zur Verfügung stehen, während die 3 wichtigsten MySQLi Klassen es bereits auf knapp 80 Methoden bringen.
Neben den reinen Fakten, macht MySQLi durch die OOP Schnittstelle aber auch mehr Spaß und wie wir weiter unten noch sehen werden, kann man effektiver und zeitsparender damit arbeiten.
Prozedural oder objektorientiert?
MySQLi wurde entwickelt damit es sich in das objektorientierte Umfeld von PHP 5 einreiht. Die prozeduralen Funktionen wurden aus Gründen der Kompatibilität und eines einfachen Umstiegs für notorische Fortschrittsverweigerer beibehalten. Deswegen ist der OOP Weg dem prozeduralen Weg klar vorzuziehen.
Bevor wir endlich zum Code-Teil kommen, schauen wir uns kurz die Unterschiede zwischen MySQL, MySQLi prozedural und MySQLi objektorientiert an.
Für Umsteiger von MySQL
Der Umstieg ist denkbar einfach, denn alles das sich mehr oder weniger ändert ist, dass aus dem Präfix mysql_ ein mysql
i_ wird. Hier einige Beispiele:
PHP-Code:
// Links der MySQL Befehl, rechts das MySQLi Gegenstueck. Der Pfeil bedeutet "wird zu"
mysql_connect() -> mysqli_connect()
mysql_query() -> mysqli_query()
mysql_fetch_array() -> mysqli_fetch_array()
// und so weiter
Die Methoden beim OOP sind (meistens) ebenso eindeutig. So wird aus dem Präfix mysqli_ der Objektname mit dem -> Operator. Beispiel:
PHP-Code:
// Angenommen unser Objekt traegt den Name $objekt dann sehen die Methoden wie folgt aus
mysqli_query() -> $objekt->query()
mysqli_fetch_array() -> $objekt->fetch_array()
// und so weiter
Nichts desto Trotz gibt es aber einige Besonderheiten bzw Unterschiede bei der prozeduralen und OOP Schreibweise. Einige prozedurale Funktionen haben nämlich keine OOP Methode, sondern stehen beim OOP als Eigenschaft zu Verfügung! Schauen wir uns das mal an...
MySQLi Unterschiede der prozeduralen und OOP Schreibweise
Funktionen die einen Zähl-Wert zurück liefern stehen beim OOP als Eigenschaft zur Verfügung. Das sieht wie folgt aus:
PHP-Code:
// Liefert die Anzahl der gefundenen Datensaetze
mysqli_num_rows() -> $objekt->num_rows
// Liefert die Anzahl der betroffenen Datensaetze
mysqli_affected_rows() -> $objekt->affected_rows
Dem
affected_rows fällt eine besondere Rolle zu. So kann man es bei MySQLi jetzt auch als Ersatz für
num_rows verwenden. Benutzt man in einem Query INSERT, UPDATE, REPLACE oder DELETE, liefert
affected_rows die Anzahl der betroffenen Datensätze. Benutzt man in einem Query SELECT, liefert
affected_rows die Anzahl der gefundenen Datensätze.
Insgesamt sind die Funktions-/Methodennamen bei MySQLi so gewählt, dass jeder halbwegs erfahrene PHP Programmierer problemlos umsteigen kann, da sich die Namen an die altbekannten MySQL Funktionen anlehnen. Es kamen aber auch neue Funktionen hinzu, von denen wir uns im weiteren Verlauf des Tutorial einige anschauen wollen.
Datenbankverbindung öffnen/schließen
Ohne eine Verbindung zur Datenbank wird es schwer Daten auszulesen bzw. einzutragen. Deswegen kümmern wir uns erst einmal um einen Connect.
PHP-Code:
<?php
// Neues Datenbank-Objekt erzeugen
$db = @new mysqli( 'localhost', 'root', '', 'tutorials' );
// Pruefen ob die Datenbankverbindung hergestellt werden konnte
if (mysqli_connect_errno() == 0)
{
/*
* Hier wird Code ausgefuehrt, wenn die Datenbankverbindung
* fehlerfrei hergestellt werden konnte.
*/
}
else
{
// Es konnte keine Datenbankverbindung aufgebaut werden
echo 'Die Datenbank konnte nicht erreicht werden. Folgender Fehler trat auf: <strong>' .mysqli_connect_errno(). ' : ' .mysqli_connect_error(). '</strong>';
}
// Datenbankverbindung schliessen
$db->close();
?>
Es ist nicht schwer zu erkennen was hier geschieht. Wie erzeugen ein Datenbankobjekt, wodurch automatisch eine Verbindung zur DB geöffnet wird, und prüfen ob dies fehlerfrei geschehen ist. Falls nicht, lassen wir uns den Fehlercode mit Fehlermeldung ausgeben. Am Ende des Script schließen wir die Verbindung wieder.
Aber der Reihe nach. Direkt in der ersten Zeile kommt schon etwas neues.
PHP-Code:
$db = @new mysqli( 'localhost', 'root', '', 'tutorials' );
Das
"new mysqli()" entspricht dem
"mysqli_connect()" der prozeduralen Schreibweise. Dieser Connect erwartet 3 Pflicht-Parameter, nämlich Serveradresse, Benutzername, Benutzerpasswort. Optional kann man aber auch noch direkt den Datenbankname angeben! Es ist also nicht nötig erst ein
mysqli_select_db() auszuführen. Weitere optionale Parameter beim Connect sind
Port und
Sockel.
Mit
mysqli_connect_errno() prüfen wir ob ein Fehler auftrat. Der Fehlercode 0 (Null) bedeutet das alles okay ist. Falls ein Fehler aufgetreten ist lassen wir uns diesen, zusammen mit dem Fehlercode, im else-Block ausgeben.
MySQLi kommt auch mit einer eigenen Exception Klasse (mysqli_sql_exception), die man natürlich erweitern kann um entsprechend auf Fehler zu reagieren.
Jetzt da wir wissen wie man eine Verbindung herstellt, schauen wir uns ein Beispiel an wie man Daten ausliest. Zu diesem Zweck benutze ich eine Mini-Tabelle mit einigen TP-Mod Namen und deren Zuständigkeitsbereich. Den Dump dieser Tabelle, sowie die Dateien zu diesem Tutorial gibt's weiter unten als Download.
Datensätze auslesen (Query Methode)
Beim auslesen von Datensätze hat sich nicht viel geändert; zumindest nicht, wenn man den gewohnten Weg geht. Hier das Script mit dem wir unsere Mod-Liste auslesen können.
PHP-Code:
<?php
// Neues Datenbank-Objekt erzeugen
$db = @new mysqli( 'localhost', 'root', '', 'tutorials' );
// Pruefen ob die Datenbankverbindung hergestellt werden konnte
if (mysqli_connect_errno() == 0)
{
// Query vorbereiten und an die DB schicken
$sql = 'SELECT `name`, `bereich` FROM `moderatoren`';
$ergebnis = $db->query( $sql );
// Anzahl gefunde Datensaetze ausgeben
echo "<p>Es wurden " .$ergebnis->num_rows. " Einträge gefunden.</p>";
// Ergebnisse ausgeben
while ($zeile = $ergebnis->fetch_object())
{
echo $zeile->name. " ist zuständig für " .$zeile->bereich. "<br />";
}
// Resourcen freigeben
$ergebnis->close();
}
else
{
// Es konnte keine Datenbankverbindung aufgebaut werden
echo 'Die Datenbank konnte nicht erreicht werden. Folgender Fehler trat auf: <strong>' .mysqli_connect_errno(). ' : ' .mysqli_connect_error(). '</strong>';
}
// Datenbankverbindung schliessen
$db->close();
?>
Zuerst wird unsere Statement formuliert, das wir in
$sql ablegen. In der Zeile ...
PHP-Code:
$ergebnis = $db->query( $sql );
... senden wir die Anfrage (Query) mit dem Statement ($sql) an die Datenbank und erhalten ein Ergebnis zurück das wir in
$ergebnis ablegen, das dadurch zu einem Objekt wird. In der nächsten Zeile geben wir aus wie viele Datensätze gefunden wurden. Die Anzahl steckt, wie oben erwähnt, in der
Eigenschaft num_rows unseres
$ergebnis Objekts.
Die eigentlichen Datensätze werden in der while-Schleife ausgegeben. Dadurch das wir die Treffer-Zeilen als Objekt fetchen, erhalten wir Zugriff auf die Felder in Form von Objekt Eigenschaften. Selbstverständlich könnten wir das Ergebnis auch mit
$ergebnis->fetch_array() ausgeben. In dem Fall würden wir auf die Zeilen als Array zugreifen:
$zeile['name'] und $zeile['bereich']
Jetzt schauen wir uns einen alternativen MySQLi Weg an, wie wir an die Ergebnisse kommen können.
Datensätze auslesen (Prepare Methode)
Neu in MySQLi ist, dass man Anweisungen an die Datenbank vorbereiten kann. Dadurch teilt man der DB mit welche Daten gesendet werden und wie damit zu verfahren ist. Es wird also nicht direkt eine Query an die DB geschickt, sondern erst eine Art Vorbote mit Informationen über die eigentliche Anweisung. Die Anweisung kann man zu einem beliebigen Zeitpunkt, beliebig oft an die DB senden.
Man bereitet also eine Anweisung
ein mal vor und kann diese dann
beliebig oft senden - das ist, besonders beim Eintragen in die DB, wesentlich schneller, übersichtlicher, flexibler und sicherer! Zunächst schauen wir uns aber an wie man
Prepared Statements (Vorbereitete Anweisung) beim Auslesen verwendet.
PHP-Code:
<?php
// Neues Datenbank-Objekt erzeugen
$db = @new mysqli( 'localhost', 'root', '', 'tutorials' );
// Pruefen ob die Datenbankverbindung hergestellt werden konnte
if (mysqli_connect_errno() == 0)
{
$sql = 'SELECT `name`, `bereich` FROM `moderatoren` ORDER BY `name`';
// Statement vorbereiten
$ergebnis = $db->prepare( $sql );
// an die DB schicken
$ergebnis->execute();
// Ergebnis an Variablen binden
$ergebnis->bind_result( $name, $bereich );
// Ergebnisse ausgeben
while ($ergebnis->fetch())
{
echo $name. " ist zuständig für " .$bereich. "<br />";
}
}
else
{
// Es konnte keine Datenbankverbindung aufgebaut werden
echo 'Die Datenbank konnte nicht erreicht werden. Folgender Fehler trat auf: <strong>' .mysqli_connect_errno(). ' : ' .mysqli_connect_error(). '</strong>';
}
// Datenbankverbindung schliessen
$db->close();
?>
Das Zusammenstellen der Abfrage ist identisch zum vorherigen Beispiel. Diesmal wird
$sql allerdings nicht direkt per
$ergebnis = $db->query( $sql ); an die DB gesendet, sondern wir teilen der DB mit einem ...
PHP-Code:
$ergebnis = $db->prepare( $sql );
... mit was wir von der Abfrage erwarten. Wir bereiten (prepare) also die eigentliche Anweisung vor. Ausgeführt wird die Anweisung dann mit ...
PHP-Code:
$ergebnis->execute();
Dieses
execute() kann man unendlich oft an die DB senden und es wird immer die zuletzt festgelegte Anweisung ausgeführt. Das hört sich momentan nach "na und?" an, ist aber beim befüllen der DB von großem Vorteil, wie wir weiter unten noch sehen werden. Das Abfrageergebnis steht jetzt zur Verfügung. Statt es direkt in einer Schleife auszugeben,
binden wir die Felder der einzelnen Zeilen unseres Ergebnis an selbst definierte Variablen.
PHP-Code:
$ergebnis->bind_result( $name, $bereich );
Die Anzahl der Parameter (Variablen) muß identisch mit der Anzahl der Felder unserer Abfrage sein. Ausgegeben wird das Ergebnis dann in einer while-Schleife ...
PHP-Code:
while ($ergebnis->fetch())
{
echo $name. " ist zuständig für " .$bereich. "<br />";
}
... bei der mittels
$ergebnis->fetch() Zeile für Zeile auf unser Ergebnis zugegriffen wird. Die Variablen
$name und
$bereich werden bei jedem Durchlauf mit den neuen Werten gefüllt.
Diese Art des
Ergebnis binden kann wesentlich übersichtlicher sein als ein Zugriff auf Objekt Eigenschaften oder assoziative Arrays. Desweiteren kann man sich auf diese Art teilweise das hantieren mit Aliases in der DB sparen.
Man kann aber nicht nur Abfrageergebnisse an Variablen binden, sondern auch Variablen an Anweisungen. Das, zusammen mit der Fähigkeit Anweisungen vorzubereiten, ermöglicht es uns sehr komfortabel und sicher Daten in die DB zu schreiben.