Das Cachesystem, welches im PEAR Repository zu finden ist und unter anderem von Ulf Wendel und Sebastian Bergmann stammt, ist eine faszinierende Technik. Auf phpTutorials hat es sich bereits bestens bewährt und spart auf dem Server Rechenzeit, was sich auch in einer schnelleren Auslieferung der erzeugten Seiten äußert. Da dieser Cache serverseitig ist, ist es natürlich vollkommen unerheblich, ob die Seite vom selben Besucher aufgerufen wird oder nicht (Eine Ausnahme hierbei sind Seiten, die Ihren Content besucherabhängig generieren). Der PEAR Cache bietet hierbei einiges. Angefangen beim Datencache, der, als Grundlage für alles andere, beliebige Daten cachen kann, geht es bis zum Funktionscache, der rechenaufwendige Funktionen zu cachen versteht. Der URLCache bietet die Möglichkeit komplette Seiten von anderen Servern zu cachen, was je nach Server einen enormen Geschwindigkeitsgewinn bedeuten kann. Der Outputcache, welcher auch auf phpTutorials.de verwendet wird, bietet mit der Möglichkeit ganze Seiten unter Berücksichtigung eventueller GET- und POST-Parameter zwischenzuspeichern einen ähnlichen Service wie der URLCache.
Die Speicherung der Daten ist dabei flexibel gestaltet worden. Zum dem Zeitpunkt, als dieser Artikel geschrieben wurde, standen eine Speicherung in Dateien und über die PEAR und PHPLIB Datenbankabstraktion auch in Datenbanken zur Verfügung. In der Entwicklung befand sich eine für die Geschwindigkeit sehr vielversprechende Shared-Memory-Variante.
Es soll jedoch noch darauf hingewiesen werden, dass der PEAR Cache sich zu diesem Zeitpunkt noch in Entwicklung befindet. Er ist praktisch als Alpha- bzw. Betacode anzusehen. Wen dies nicht abschreckt kann sich auf eine faszinierende Neuerung einlassen.
Installation
Der PEAR Cache ist eine Sammlung von Klassen, welche einfach in ein bestehendes Projekt eingebunden werden können. Zur Installation lädt man sich folglich die Klassen entweder vom CVS-Server oder auch von phpTutorials in der Version herunter, die zum Zeitpunkt des Schreibens aktuell war. Diese Dateien sind einfache PHP Dateien, die man am besten in ein entsprechendes Unterverzeichnis auf seinen Webserver lädt, falls das PEAR Repository dort nicht schon vorhanden ist.
Im folgenden wird davon ausgegangen, dass der PEAR Cache in das selbe Verzeichnis wie die Scripts, die Diesen verwenden, installiert wurde. Das heißt, dass PEAR.php und Cache.php im selben Verzeichnis sind und ein Unterverzeichnis namens /Cache/ existiert. Bei anderen Verzeichnissen muss der include_path entsprechend angepasst werden.
Datencache als Grundlage
Der Datencache ist die Grundage sämtlicher darauf aufbauender Caches. Hiermit hat man die Möglichkeit beliebige Daten zu cachen, damit diese nicht mit jedem Aufruf generiert werden müssen. Die Vorgehensweise hierbei ist erstaunlich einach. Man instantiiert zuerst ein Cache-Objekt. Bei dieser Instantiierung gibt man dem Konstruktor der Cache-Klasse als erstes Argument den zu benutzenden Container zur Speicherung der Cachedaten an. Hierbei sind zur Zeit "file", "db", "dbx" und "phplib" denkbar. Für unsere Beispiele soll der Cache prinzipiell in lokalen Dateien gespeichert werden. Das zweite Argument des Konstruktors der Cache-Klasse enthält ein Array mit weiteren Optionen für den "Storage Driver", also weitere Optionen zur Speicherung. Bei der Speicherung in Dateien wird man hier üblicherweise das Speicherverzeichnis angeben oder ein eventuelles Präfix vor den generierten Dateinamen.
Hierbei sollte Folgendes beachtet werden: Die PEAR Cache Version, die zu diesem Zeitpunkt aktuell ist, weist auf einigen Servern eine Ungereimtheit im Zusammenhang mit dem File-Storage-Container auf. Relativ angegebene Pfade führen hier dazu, dass die Option cache_dir gelöscht wird. Folglich sollte man hier absolute Pfadangaben im Konstruktor des Cache-Objektes machen. Ausserdem müssen die Rechte auf den Ordner, in dem die Cachefiles gesichert werden sollen, so gesetzt sein, dass das Script darin schreiben darf. Beachtet werden sollte auch, dass, wenn keine Gruppe angegeben wird, automatisch /default/ an den Speicherpfad angehängt wird. Dies macht jedoch keinerlei Probleme.
Nach der Instantiierung eines Cache-Objektes lässt man dieses über die Methode generateID() eine eindeutige ID erzeugen. Diese ID wird verwendet, um die Daten der entsprechenden Quelle zuzuordnen. Beim Outputcache würde man hier zum Beispiel den Seitennamen inklusiver der entsprechenden GET- und/oder POST-Parameter und/oder Cookies als Argument für generateID() verwenden, um bei den selben Parametern immer die selbe ID zu erzeugen. Die Methode erzeugt bei den selben Argumenten dabei immer die selbe ID und nimmt als Argument beliebige Datentypen, weswegen man auch ein Array mit den POST-Parametern übergeben kann.
Nachdem man diese ID generiert hat, kann man seine Daten über die get() Methode, falls im Cache vorhanden, aus diesem laden lassen. Falls diese ID im Cache nicht gefunden wurde, so wird hier false zurückgegeben, weshalb man über ein if() Konstrukt die Daten, wenn diese nicht vorhanden sind, erzeugen lassen kann. Daraufhin wird man den Cache über die save() Methode noch speichern.
Dieses Prinzip findet man im kommentierten Beispiellisting 1 als lauffähigen Sourcecode.
Löschen des Cache
Prinzipiell verwaltet sich der Cache selbst. Der Destruktor des Cache-Objektes, welcher bei Beendigung des Scripts automatisch von PHP aufgerufen wird, ruft automatisch die Funktion garbageCollection() auf, welche den Cache aufräumt. Jedoch wird hierbei die Mithilfe des Programmierers benötigt. Standardmäßig wird jeder Eintrag unendlich lange im Cache gehalten, aber die "Lebensdauer" kann innerhalb der save() Methode als optionaler dritter Parameter in Sekunden angegeben werden. Wenn der Cache regelmäßig entleert werden soll, muss die Lebensdauer hier mit übergeben werden.
Alternativ kann man aber bestimmte Cache-Einträge auch manuell löschen. Dies wird über die delete() Methode erreicht und ist im Beispiellisting 1 in Aktion zu sehen.
Eine dritte Möglichkeit neben dem automatischen Löschen im Konstruktor und dem manuellen Löschen eines einzelnen Datensatzes ist es, manuell den gesamten Cache zu löschen. Zuständig für eine vollständige Entleerung des Caches ist die Methode flush() des Cache Objektes.
Der Funktionscache
(Hinweis: Der Funktionscache funktioniert nur bei PHP Versionen 4.0.4 oder neuer!)
Obwohl sämtliche Funktionsweisen des Caches auch schon über den reinen Datencache und also der Cache-Klasse erreichbar sind, gibt es ein paar weitere Klassen und Funktionen, die spezielle Anwendungen vereinfachen. Der Funktionscache ist die erste Spezialanwendung des Caches. Seine Aufgabe ist das Cachen von Funktionswerten, um bestimmte, zeitaufwendige Funktionen zu beschleunigen. Ein Manko des Funktionscaches ist es dabei, dass dieser nur auf Funktionen angewendet werden kann, die nicht von externen Datenquellen wie globale Variablen oder Datenbanken angewiesen sind. Ebenso problematisch sind statische Variablen. Cachbar sind nur Funktionen, die bei bestimmten Aufrufparametern immer den selben Rückgabewert liefern, da nur der Funktionsname und die Parameter zur Bildung der ID herangezogen werden können. Ausserdem darf die Funktion nicht selber Text ausgeben, da diese Textausgabe bei einem Laden des Rückgabewertes aus dem Cache nicht wiederholt würde.
Die Anwendung des Funktionscaches ist dabei denkbar einfach, da er selbst nur aus einer einzigen Funktion besteht. Diese instantiiert sich selbst ihr eigenes Cache Objekt, wofür allerdings am Anfang des Scriptes bestimmte Variablen zur Konfiguration gesetzt werden müssen. Diese entsprechen dabei den Parametern des Konstruktors der Cache Klasse. Die Variable $FUNCTION_CACHE_CONTAINER wird vor dem ersten Aufruf des Funktionscaches mit dem zu verwendenden Container belegt. Möglich sind auch hier die Werte "file", "db", "dbx" und "phplib". $FUNCTION_CACHE_CONTAINER_OPTIONS ist hier das Array mit den Optionen für den Cache Container, wie üblicherweise Speicherverzeichnis für den File-Container.
Nachdem diese beiden Variablen gesetzt wurden, kann man den Funktionscache verwenden, der nur aus der Funktion cached_function_call() besteht, die als ersten Parameter den Namen der zu cachenden Funktion und als weitere Parameter jeweils die Argumente für die eigene Funktion erhält. Die Parameter, die der zu cachenden Funktion übergeben werden sollen werden hier einfach wie gewohnt übergeben, da cached_function_call() beliebig viele Parameter annehmen kann und diese dann in die richtige Form an die zu cachende Funktion übergibt.
Demonstriert wird der Funktionscache im Beispiellisting 2.
Der URL Cache
Der URL Cache ist eine besondere Erleichterung für Programmierer, die externe Seiten aufrufen und auswerten. Er hat die Funktion, die Ausgabe von anderen Webservern auf dem eigenen Server zwischenzuspeichern. So spart man sich in der gecacheten Zukunft einen Zugriff auf den externen Server. Damit spart man natürlich die Zeit, die das Script auf die Antwort des Servers warten müsste und kann sogar Downtimes des anderen Servers überbrücken, da dieser ja nicht abgefargt werden muss. Problematisch ist dies insofern, als der Cache natürlich die gespeicherte Seite zurückliefert und somit Änderungen auf dem externen Server nur zeitverzögert nach dem nächsten Ablauf der Lebenszeit mitbekommen werden.
Die Anwendung ist hierbei zu der des Funktionscaches sehr analog. Es muss die Datei Cache/URL.php hinzugeladen werden und wiederum vor dem ersten Aufruf der Funktionalität 2 Variablen gesetzt werden. Diese sind hier mit $URL_CACHE_CONTAINER und $URL_CACHE_CONTAINER_OPTIONS benannt. Nachdem die Konfiguration also analog zum Funktionscache vorgenommen wurde kann der Cache benutzt werden. Hierfür ist die Funktion get_cached_url() zuständig. Dieser muss mindestens ein Parameter übergeben werden, welcher die zu ladende URL enthält. Ein Möglicher zweiter Parameter nimmt die Lebensdauer des Caches in Sekunden auf.
Der Outputcache
Ein weiteres, sehr interessantes Feature und das, welches den Cachegedanken am weitesten ausreizt, ist der Outputcache. Hier werden die PHP4-eigenen Output Buffering Funktionen benutzt, um dem kompleten Output von PHP abzufangen und zwischenzuspeichern. Hierbei wird ein wenig anders vorgegangen als im Funktions- oder URL Cache. Der Outputcache ist eine weitere Klasse, welche den Datencache erweitert. Somit ist hier bei der Anwendung wie beim zugrundeliegenden Datencache vorzugehen.
Zuerst läd man die Klassendefinition aus der Datei Cache/Output.php hinzu und instantiiert daraus ein Objekt. Hierbei werden beim Konstruktor wieder die Optionen wie Cache Container und die für den Container nötigen Optionen (Speicherpfad) angegeben. Der nächste Schritt ist wieder die Generierung einer eindeutigen ID für den Cache. Dabei ist hier zu Berücksichtigen, wovon die Generierung unserer Seite abhängt. Wenn die Seite nur GET-Parameter nimmt, so ist als Parameter für die generateID() die Übergabe der $REQUEST_URI ausreichend. Wenn jedoch noch POST-Variablen oder Cookies die Seite beeinflussen, so sollten diese natürlich in die ID-Generierung mit einfliessen. Dazu erweist es sich wieder als praktisch, dass generateID() beliebige Typen von Variablen annimmt, so dass man zum Beispiel auch folgendes übergeben könnte:
generateID(array("file" => $PHP_SELF,
"get" => $HTTP_GET_VARS,
"post" => $HTTP_POST_VARS,
"cookie" => $HTP_COOKIE_VARS));
Damit wäre eine eindeutige ID (fast) sichergestellt. Nun kann der Cache benutzt werden. Dazu überprüft man mittels der start() Methode, welche als Parameter die generierte ID erhält, ob die Seite schon gecached worden ist. Wenn ja, muss man den Inhalt nur noch ausgeben und das Script beenden. Da mit dem Aufruf von start() das Output buffering bereits eingeschaltet wurde, muss man im Folgenden fast nichts mehr beachten. Man erzeugt im darauffolgenden Code ganz normal den Content der Seite.
Bevor man jedoch das Script beendet, fügt man am Ende noch einen weiteren Aufruf einer Methode ein, damit der Content ausgegeben und gespeichert wird. Hierfür stehen verschiedene Methoden zur Verfügung. end() sichert den Output und gibt ihn als Return-Wert zurück. Man muss diesen daraufhin also ausgeben lassen, damit etwas angezeigt wird. Diese Ausgabe übernimmt endPrint() automatisch, indem es end() aufruft und den Content an den Client ausgibt.
Falls man den Cache noch nicht speichern möchte, so kann man endGet() benutzen, welches den Content zurückgibt aber nicht speichert. Diesen kann man daraufhin noch an eine Templateengine oder ähnliches zur Nachbearbeitung übergeben und später über die save() Methode der Basisklasse speichern.
Zur Erläuterung habe ich noch ein lauffähiges und kommentiertes Beispiel zum Outputcache zur Verfügung gestellt.
Fazit
Wie man gesehen hat ist der PEAR Cache eine interessante Alternative zum ZEND Cache die für geringe Budgets (Kosten sind gleich Null) einiges an Möglichkeiten bietet. |