|
OOP mit PHP4 - Klassen in Klassen
|
Was bringt "class nesting" ?
Ich programmiere seit mehreren Jahren PHP und bin dabei zu folgender Erkenntnis gekommen:
Es macht weder Sinn noch Spass, elementare Funktionen jedesmal neu zu schreiben.
Wichtige Stichworte sind Modularität und wiederverwendbarer Code, um Zeitverluste bei Erstellung sowie Änderung von Applikationen zu vermeiden und potentielle Fehlerquellen auszuschliessen. Darum will ich eine Sammlung von Klassen schreiben, die dann als Basis für die eigentlichen Applikationen dienen sollen. In dieser Sammlung wären dann z.B. Klassen für Datenbanken, File-Handling, Mails, Programmausführung, o.ä. Darauf aufbauende Klassen könnten z.B. Guestbooks, Foren, Statistiken, CMS oder sonstwas sein.
Da ich es auch gleich als Beispiel nehme, betrachten wir doch einmal eine Guestbook-Klasse. Diese sollte vielleicht folgende Funktionen beinhalten:
a) Einträge in eine Datenbank schreiben
b) Vorhandene Einträge darstellen
c) Neue Einträge per mail an den owner schicken
d) Ungewollte Einträge wieder löschen
e) Zur Darstellung Templates benutzen, die in Dateien bereit liegen
f) ...
Mit extends kommen wir hier nicht wirklich weiter, da 2-3 Klassen ( Datenbank,Mail,File) benutzt werden sollen, die keine Beziehung untereinander haben. Wie kann man nun in der Guestbook-Klasse die anderen Klassen verfügbar machen ? Indem man Instanzen dieser Klassen in der Guestbook-Klasse erzeugt und verwendet. In diesem Artikel will ich versuchen, einen möglichen Weg dafür zu beschreiben.
Am besten lernt man Sachen anhand von Beispielen - definieren wir nun als erstes eine Beispiel-Datenbankklasse mit ein paar Funktionen:
<?php
// Dateiname: db_klasse.php
class db_klasse
{
var $host = "localhost";
var $user = "ich";
var $pass = "meinpass";
var $dbname = "meinedb";
var $db_link = false;
function db_klasse()
{
$this->db_connect($this->host,$this->user,$this->pass,$this->dbname);
}
function db_connect($host,$user,$pass,$dbname)
{
$this->db_link = @mysql_pconnect($host,$user,$pass) or die ("Datenbankverbindung nicht möglich!");
$this->db_choose($dbname);
}
function db_choose($dbname)
{
@mysql_select_db($dbname) or die ("Datenbank konnte nicht ausgewählt werden!");
}
function db_query($query)
{
$res = @mysql_query($query, $this->db_link) or die ("Abfrage war ungültig!".mysql_error());
return $res;
}
}
?>
SQL-Statement für die im folgenden benutzte Tabelle "guest":
create table guest (
NUMMER int not null auto_increment,
DATUM date,
NAME varchar(255),
EMAIL varchar(255),
HEADING varchar(255),
MESSAGE text,
key(NUMMER)
);
Nun wollen wir eine Guestbook-Klasse schreiben, die Gebrauch von unseren
Datenbankfunktionen macht. Dazu muss innerhalb der Klasse eine Instanz der Klasse
"db_klasse" erzeugt werden. Diese speichern wir in der Klassenvariable "class_obj".
Da wir nun ein "normale" Instanz haben, können wir natürlich auf alle Funktionen und
Variablen der eigentlichen Klasse zurückgreifen. Ein "echo $class_obj->user;" würde
also "ich" ausgeben.
<?php
// Dateiname: guestbook.php
class guestbook
{
var $class_obj;
function guestbook($class_needed,$file_ending)
{
// Hier wird die Klasse eingebunden
include($class_needed.$file_ending);
// Hier erzeugen wir eine Instanz und speichern sie in "class_obj"
$this->class_obj = new $class_needed;
}
function insert_entry($name,$email,$heading,$message)
{
$datum = date("Ymd");
// Nun greifen wir auf die Funktionen der "db_klasse"-Instanz zu
$this->class_obj->db_query("insert into guest ( DATUM,NAME,EMAIL,HEADING,MESSAGE) values
('$datum','$name','$email','$heading','$message')");
}
function show_all()
{
// Wiederum wird die "db-klasse" benutzt
$res = $this->class_obj->db_query("select * from guest order by NUMMER desc");
while ( $row = mysql_fetch_array($res) )
{
echo "
Eintrag Nummer $row[0]:<br>
Datum: $row[1]<br>
Name: <a href=\"mailto:".$row[3]."\">$row[2]</a><br><br>
$row[4]<br>
$row[5]<br><br><br>
";
}
}
}
?>
Zum Testen dieser Klassen schreiben wir noch eine test.php. Nun sollte
eigentlich alles funktionieren :
<?php
include("guestbook.php");
$TEST = new guestbook("db_klasse",".php");
$TEST->insert_entry("Olaf Waltersdorf","olaf@waltersdorf.net","Dies ist ein Gästebucheintrag...","...aber kein besonders guter!");
$TEST->show_all();
?>
Interessanter Seiteneffekt
Da unsere Klassenvariable "class_obj" nun eine vollständige Instanz von
"db_klasse" ist, können wir mit ihr genauso arbeiten wie mit einer
"normal" erzeugten Instanz. Es gibt dabei zwei verschiedene Möglichkeiten,
die ich einmal kurz vorstellen will:
Die Instanz-Kopie:
<?php
include("guestbook.php");
$GUEST = new guestbook("db_klasse",".php");
// Noch ist $GUEST->user = ich
$GUEST->class_obj->user = "Guestbook_Instanz_User";
// Wir erzeugen eine KOPIE der Instanz, und zwar im zum Zeitpunkt der Zuweisung aktuellen! Zustand
$DB = $GUEST->class_obj;
echo $DB->user."<br>";
// Nun ändern wir den user in der Instanz-Kopie
$DB->user = "User_geaendert_von_Instanz_Kopie";
echo $DB->user."<br>";
// Wie man sieht hat die Kopie keinen Einfluss mehr auf die ursprüngliche Instanz
echo $GUEST->class_obj->user;
?>
Der Nutzen liegt klar auf der Hand: Mal angenommen, die Daten wurden aus einem Formluar geschickt und zusätzlich soll jeder Seitenzugriff geloggt oder z.B. der Klickpfad des User gespeichert werden. Nach dem Abarbeiten der Guestbook-Klasse können wir die "db_klasse" weiterbenutzen. Mit $DB->db_connect("localhost","anderer_user","passwort","SITE_LOG") und $DB->query("insert ...") können wir z.B. direkt eine andere Datenbank ansprechen.
Die Instanz-Referenz:
Wir ändern in obigem Beispiel nur eine Zeile:
$DB = & $GUEST->class_obj;
Der einzige Unterschied liegt in der Zuweisung, diesmal nämlich als Referenz.
Dadurch erfolgen Änderungen wie z.B. $DB->user = "test" immer in $GUEST->class_obj,
d.h. der Instanz von "db_klasse" in der Klasse "guestbook", wie man an obigem Beispiel testen kann.
Diese Art des "nestings" kann man IMHO beliebig weiterführen, bzw. verschachteln.
Schlusswort
Dieser Artikel ist der deutschen PHP-Community gewidmet, ohne die ich ihn nicht hätte schreiben können. Es ist an der Zeit, etwas zurückzugeben...hoffentlich hilft es dem ein oder anderen!
Alle Beispielskripte stehen zum freien Download bereit.
Gruß,
Olaf Waltersdorf
olaf@waltersdorf.net
|