Die Flagge des Marasek

Dekostreifen

English

Aktuell Texte Der Comic Impressum Kalender Suche PHP-Klassen Container-Wizard main.s21

Kategorien

Buch
Computer
Computerspiele
Film
Geschichte
Gesellschaft
Idee
Kunst
Natur
Persönlich
Politik
Programmieren
Religion & Philosophie
Weblog
Weltpolitik
Weltsicht
{{login}}

Das Caller-Schema

Permalink
Vorheriger: Angst vor der Bombe?Nächster: Das übliche Schema...
Eingeordnet in: Programmieren

Seit ich meine Webseiten in PHP programmiere, stellt sich mir die Frage nach der Organisation meiner Dateien. Dies war zu Beginn noch relativ einfach, da meine ersten PHP-Seiten im wesentlichen aus einem include für die Navigation und einem include für die aufzurufende Seite bestanden. Aber meine Anwendungen wurden bald komplizierter; sehr viel komplizierter.

Sobald ich anfing, beispielsweise Bilder nicht direkt, sondern über ein Wrapper-Skript auszuliefern, hatte ich PHP-Bestandteile, die nicht mehr zu der Seite gehörten. Sie waren zu Beginn noch einzelne Skripte (z. B. download.php). Als nächstes benötigte ich Administrationsmenüs, um meine eigenen Datenbankinhalte zu pflegen; sie waren in einem Unterverzeichnis (/data) angelegt.

Es gab jetzt also index.php5, das über Parameter verschiedene includes für die Hauptseite lud, download.php5 und /data/index.php5, das im Grunde ähnlich wie die Hauptseite funktionierte. Dazu noch eine Klassenbibliothek und weiteres Material (Bilder usw. usf).

Mit zunehmender Verwendung von Datenbanken und objektorientierter Programmierung stiess das Schema an seine Grenzen. ich erachtete es als Nachteil, dass download.php5 nichts von der üblichen Umgebung (Klassenbibliothek, Datenbankverbindung, Pfade...) wusste, und das ganze nochmal seperat für /data/index.php5 zu verwalten war. Unschön.

Ich begann, meine Webseite in "Modi" aufzuteilen; verschiedene, übergeordnete Zustände ein- und desselben Skripts, index.php5:

  • main, der Hauptmodus (also Texte darstellen)
  • download, Dateidownload
  • backend, Datenpflege (Dies exemplarisch; ich behielt das /data/-Verzeichnis länger bei, um die Sicherheit einer Apache-Passwordabfrage zu geniessen.)

Ich erstellte ein Verzeichnis /mode/ und begann, darunter die verschiedenen Modi und Zustände einzusortieren. Der GET-Parameter mode gab den Modus; per default, bei ungesetztem Parameter wurde main angenommen. Die zentrale index.php5 entschied über den Modus, und gab die Kontrolle dann an untergeordnete Skripte ab, die für ihren Teil Unterentscheidungen trafen, beispielsweise /mode/main.php, welcher topic angezeigt werden sollte. Dazu hatte ich die Bestandteile der Hauptseite schön geordnet unter /mode/main/ abgelegt.

Der Vorteil liegt auf der Hand: endlich hatten alle der Seite zugehörigen Skripte die gleiche Umgebung: die gleiche Datenbankverbindung, den gleichen Autoloader und die gleichen, eventuell gesetzten globalen Variablen (etwas, das ich nicht mehr benutze). Der Nachteil ist allerdings, dass für jeden Unterzustand immer die gleiche Menge an Vorbereitungen getroffen wird; eine akzeptable Einbusse an Performance.
Bei dieser Gelegenheit ein Seitenhieb auf Projekte wie PhpMyAdmin und Mantis, die den umgekehrten Weg beschreiten, nämlich ihre Applikation als Ansammlung von direkt aufzurufenden *.php-Dateien realisieren, in denen dann meist ein Verweis auf ein zentrales include zu finden ist: ich.habe.das.nie.ver-stan-den. Aber wenn jemand gerne einen Satz Einzeldateien pflegt: go ahead.

Jedenfalls hatte ich irgendwann genug von den includes. Ich hatte mit anderen Sprachen gespielt, bei denen es dieses gefürchtete Konstrukt PHPs nicht gab. Sollte ich jemals eine echte Anwendung schreiben wollen, würde ich mich wohl nicht mehr auf eine Ansammlung von includes verlassen können. Ich hatte kleine Experimente mit grafischen Oberflächen gemacht; meine Idee war nun, meine Webseite so zu programmieren, als wäre sie eine echte Applikation.

Ich hatte bereits meinen eigenen, sehr komfortablen Autoloader, der mir aus meinem Klassenbibliotheksverzeichnis holte, was immer ich auch benötigte. Was lag also ferner, die "Modi" ebenfalls als Klassen zu realisieren, und die einzelnen Zustände dort aufzurufen?

<?php
class Mode {
    private 
$mode;
    private 
$pdo;
    function 
__construct(PDO $pdo) {
        
$this->mode "main";
        if(isset(
$_GET["mode"])) {
            
$this->mode $_GET["mode"];
        }
        
$this->pdo $pdo;
    }
    
    function 
call() {
        
$method "call".ucfirst($this->selected);
        if(!
is_callable(array(&$this$method))) {
            throw new 
Exception("cannot call ".__CLASS__."::call".$method);
        }
    
$array func_get_args();
    return 
call_user_func_array(array(&$this$method), $array);
    }
    
    function 
callMain() {
        
// do whatever main has to do
    
}
    
    function 
callDownload() {
        
// do whatever download has to do
    
}
}

$mode = new Mode();
echo 
$mode->call();

?>

Die Klasse Mode entscheidet als erste Klasse, was zu tun ist. Die einzelnen Zustände sind als Methoden definiert, die mittels Callback aufgerufen werden. Es fällt auf, dass ich den importierten $_GET-Wert keinerlei Prüfung unterziehe; dies ist ja auch nicht nötig, da mittels diesen Wertes nur Methoden von Mode aufgerufen werden können, die auch existieren; alles andere wird in einer Exception resultieren. Vorbei die ewig gleichen Prüfungen von $_GET-Werten, um mich vor directory traversal-Attacken oder remote includes zu schützen; ein komfortabler Weg, sicherzustellen, dass wirklich nur das aufgerufen werden kann, was aufgerufen werden soll.
Die einzelnen callback-Methoden erledigen jetzt entweder direkt ihre Arbeit oder rufen noch weitere Klassen auf, die spezifischere Aufgaben erledigen. Grundsätzlich versuche ich die Methoden so klein wie möglich zu halten, wenn ein Unterabschnitt also eine erweiterte Logik hat, dann bekommt er eine eigene Klasse. Der Download von Bildern wird beispielsweise in der Methode selber abgehandelt, die Hauptseite hat aber ihre eigene Klasse Main. Das Impressum wird per Main::callImpressum ausgegeben, Main::callViewer, der die Texte anzeigt, greift noch auf eine weitere Klasse Viewer zurück.

Im übrigen sind die wenigsten Methoden als Prozeduren realisiert; ein weiterer Grund, der für dieses Schema spricht. Die Methoden liefern Rückgabewerte, die ich in ein Template einfügen kann. Das Caller-Schema bedeutete für mich auch die Abkehr von typischem PHP-Code à la echo "<td>".$irgendwas."</td>; oder <td><?php echo $irgendwas; ?></td>. Ausserdem hätte ich einen Ansatzpunkt, beispielsweise eine Anfrage nach "Text X" direkt in Mode::callMain() oder tiefer in Main::callViewer() ein Caching-System zu implementieren.

Kommentieren

Bitte beachten: Kommentare sind nicht sofort sichtbar, sondern werden erst nach einer kurzen Prüfung freigegeben, sofern keine rechtliche Beanstandung vorliegt.
Rechtlich bedenkliche Inhalte werden entweder entschärft oder nicht veröffentlicht.

* Titel  
* Nickname  
* Kommentar