PHP-Klasse AhCache

PHP-Klasse zum Cachen von Content

Einleitung

For information in English see the Docs.

Manche Aufgaben beim verarbeiten eines Webserver-Requests brauchen Zeit. Sei es ein rekursiver Verzeichnisscan, ein aufwändiges Query oder Requests auf andere Server.
Solche zeitaufwändigen Aufgaben möchte man nicht bei jedem Seitenaufruf ausführen, weil es den Seitenaufbau immer arg verlangsamt, sondern nur "gelegentlich". Dazu speichert man einmalig das Ergebnis in einem Cache und verwendet den Cache solange man es für sinnvoll erachtet.

Diese Klasse verwende ich auf dieser Webseite zum Cachen von HTML-Code, RSS-Daten und Grafiken.
Weiterhin war diese Klasse in Version 1 auch mehrere Jahre auf der Webseite des Schweizer Radio DRS (www.drs.ch; nun SRF Radio) im Einsatz.

cache-class.png

Voraussetzung

  • PHP5

Versionsgeschichte

  • 2014-03-31 - Version 2.3
    • private Methode _setup() includet nun custom settings - damit kann man den Cache Pfad ausserhalb der Klasse definieren
    • für sehr viele Cacheitems (zuu viele Dateien in einem Verzeichnis sind nicht gut): neue Variable zum Anlegen kürzerer, aber dann verschachtelter Cache-Verzeichnisnamen
  • 2014-02-27 - Version 2.2
    • Umbenennung von Cache nach AhCache
    • _cleanup Methode prüft mit file_exists
  • 2012-05-17 - Version 2.1
    • geänderte Methode: isExpired() liefert Wert als bool (vorher Wert in Sekunden);
    • neue Methode: iExpired() liefert Wert in Sekunden
  • 2012-01-31 - Version 2.0
    • neu: es können alle serialisierbaren Variablen gecacht werden - also auch Arrays und Objekte - nicht nur Strings, wie in Version 1
    • Methode write() erweitert: als 2. Parameter kann eine Verfallszeit mitgegeben werden, die mit in der Cachedatei gespeichert wird
    • neue Methode: isExpired() zum einfacheren Feststellen, ob Cache veraltet ist (bedingt Verwendung des neuen 2. Parameters für write())
    • neue Methode: isNewerThanFile([Referenzdatei]) - zum einfacheren Feststellen, ob Cache veraltet ist
    • neue Methode: cleanup() - Löschen alter Cachedateien
  • 2011-08-27 - Version 1.1 - mehr Kommentare im PHP Code; sCacheFile is private
  • 2009-07-20 - Version 1.0 - erste öffentliche Version

Installation

Download

Download:
cache_class_php.zip (14 kB)

Auspacken

Kopieren Sie die Datei cache.class.php (und optional die Datei cache.class_config.php) in ihr PHP-Verzeichnis. Sie kann auch in einem beliebigen Unterordner abgelegt werden.

Einbindung in deine Webseite

Basics: Füllen und Lesen des Caches

(1)
Mit einer include_once/ require_once ist die Klasse zu laden.

(2)
Mit der Initialisierung sind 2 Parameter anzugeben:

  • der Name deiner Applikation / deines Moduls
    Im Hintergrund wird für die Applikation ein Unterverzeichnis angelegt. Dann können verschiedene Module dieselbe ID verwenden. Und man kann einfacher das Cacheverzeichnis eines Moduls leeren.
  • eine ID für die Daten
    Die ID kann ein beliebiger Text oder auch eine Zahl sein. Bei RSS Feeds kann man so direkt die URL des Feeds mitgeben; bei Datenbank-Queries kann es eine Basis ID sein, für die die Daten ermittelt wurden.


(3)
Die Funktionen read() und write([Daten , [Expire]]) lesen bzw. schreiben den Cache-Inhalt.

(4)
Mit am wichtigsten ist die Frage: wie lange man ich den Cache verwenden und wann muss ich diesen erneuern?
Es gibt mehrere Varianten:

  1. Zunächst werden die Daten mit write([Daten]) geschrieben.
    Mit der Funktion getAge() kann man das Alter des Caches in Sekunden ermitteln. Ist es false, so gibt es noch keinen Cache.
    Man kann den Cache anlegen/ erneuern lassen, wenn noch kein Cache existiert oder das Alter des Cache eine Toleranzschwelle übersteigt.
    Das ist eine Methode von Version 1; es sollte bevorzugt eine der nachfolgend erwähnten Varianten gewählt werden.
  2. mit der Methode write() gibt man als 2. Paramter eine Verfallszeit (Expire) in Sekunden mit. Diese Verfallszeit wird mit in der Cachedatei abgelegt.
    Dann kann man mit isExpired() feststellen, ob der Cache veraltet ist (negative Werte = Zeit in Sekunden bis Verfall; positive Werte = wie lange der Cache verfallen ist)
  3. Wenn man den Cache basierend auf einer anderen Datei aufbaut, kann man die Zeitstempel von Quelldatei und Cachedatei zum Vergleich heranziehen.
    Die Funktion isNewerThanFile([Dateiname-der-Referenzdatei]) gibt die Differenz der letzten Änderung in Sekunden zurück (negative Werte = Cachdatei ist älter als Referenzdatei).
    Die Referenzdatei ist der komplette Dateiname, den der Webserver finden können muss (lokal oder NFS Share etc.).
    Beispiele wären: ein verkleinertes Vorschaubild wird erst dann erneuert, wenn sich die Quelldatei geändert hat; oder: meine Tabelle mit Batch-Kommandos und Beispielen basieren auf einer CSV Datei - der Cache wird erst neu geschrieben, wenn ich die CSV Datei aktualisiere.

Ein Coderumpf als Ausgangslage zu 2) für eigene Entwicklungen sieht so aus:

$sContent='';  
$iTtl=60*5; // 5 min als max. zulaessiges Alter  
  
require_once("/php/cache.class.php");  
$myCache=new AhCache("mein-modul","Aufgaben-ID");  
  
if($myCache->isExpired()) {  
    // Cache existiert nicht oder ist veraltet  
    // hier folgt Code, der $sContent neu auffuellt ...  
    $sContent=...  
  
    // ... und im Cache ablegen  
    $myCache->write($sContent, $iTtl);  
  
} else {  
    // Cache-Daten lesen  
    $sContent=$myCache->read();  
}  
  
// Ausgabe  
echo $sContent;

... und ein Beispiel zu 3) zum Vergleich des Zeitstempels. $sCsvFile enthält nachfolgend den Dateinamen des Quellfiles:

require_once("/php/cache.class.php");
$sCsvFile="Name_der_Quelldatei.csv"  
  
$myCache=new AhCache("mein-modul","Aufgaben-ID");
$sContent=$myCache->read(); // einmal den Inhalt lesen  
  
// Vergleich des Alters des Cache mit dem einer Referenzdatei  
if (!$sContent || !$myCache->isNewerThanFile($sCsvFile)) {  
  
    // hier folgt Code, der $sContent neu auffuellt ...  
    $sContent=...  
  
    // ... und im Cache ablegen  
    $myCache->write($sContent);  
};  
  
// Ausgabe  
echo $sContent;

Zugriff auf div. Zeiten und Alter bestimmen

Hilfreich ist es, wenn ein Time to live Wert (TTL) bei der Methode write() als zweiter Parameter angegeben wird:

$myCache->write($sContent, $iTtl);

Dann kann man mit

$myCache->isExpired()

recht einfach abfragen, ob der Cache abgelaufen ist.

Es gibt aber auch Methoden für detailliertere Zeitangaben:

  • getAge()
    gibt das Alter der geschriebenen Cache-Datei in Sekunden zurück
     
  • getExpire() (*)
    gibt einen Unix-Zeitstempel zurück, wann der Cache abgelaufen ist
     
  • getTtl() (*)
    gibt den TTL Wert des Caches zurück; Wert ist in Sekunden
     
  • iExpired() (*)
    gibt an wie lange der Cache abgelaufen ist; negative Werte besagen, wie lange der Cache noch gültig ist; Wert ist in Sekunden
     
  • isNewerThanFile($sRefFile)
    vergleicht, ob das Cachefile (immernoch) neuer ist, als eine angegebene Referenzdatei.
     

(*) - dies ist nur verfügbar, wenn TTL bei der write-Methode mitgegeben wurde

$myCache->write($sContent, $iTtl);


All die obigen Methoden sind Lesezugriffe.
Schreibende Wirkung haben diese Methoden:

  • setTtl($iTtl)
    setzt den TTL-Wert des Objektes in Sekunden
    Es muss anschliessend noch die write()-Methode ausgeführt werden (dies kann nach setTtl ohne den 2. Parameter erfolgen), damit der neue TTL-Wert "dauerhaft" für nachfolgende Requests in das Cachefile geschrieben wird. Beachte: Damit wird das Expire ab dem aktuellen Zeitpunkt neu gesetzt.
     
  • touch()
    Erneuern des Cache-Objektes mit den bisherigen Daten. Der Inhalt des Caches wird beibehalten; das Expire-Datum wird mit dem bisherigen TTL neu ab dem aktuellen Zeitpunkt gesetzt.
     
  • write( $data = false, $iTtl = -1)
    Schreibt die zu cachenden Daten; bei Angabe 2er Parameter ist der zweite der TTL-Wert des Objektes in Sekunden

Löschen von Cachefiles

Man kann von einem zu cachenden Inhalt einen einzelnen Cache gezielt löschen. Dazu muss das entsprechende Objekt initialisiert und die die Methode delete() aufgerufen werden.

  1. require_once("/php/cache.class.php");
  2. $myCache=new Cache("mein-modul","Aufgaben-ID");
  3. $myCache->delete(); // einzelnen Cache loeschen
PHP: cache - Beispiel mit Vergleich des Zeitstempels einer Referenzdatei


Um im Cacheverzeichnis alle Cache-Dateien löschen, die älter als n Sekunden sind, gibt es die cleanup-Methode. Das Cache-Objekt ist mit einer beliebigen Modul-ID zu initialisieren.

  1. require_once("/php/cache.class.php");
  2. $o=new Cache("mein-modul");
  3. $o->cleanup(60*60*24*1); // alle Cachefiles des Moduls "mein-modul" älter 1 Tag löschen
PHP: Löschen des Caches eines Moduls



Initialisiert man die Klasse ganz ohne Modul und ruft die cleanup Methode auf, so werden alle cachefiles aller Module aufgeräumt.

  1. require_once("/php/cache.class.php");
  2. $o=new Cache();
  3. $o->cleanup(0); // alle Cachefiles aller Module löschen
PHP: Löschen des Caches aller Module


Anm.:

  • Der Parameter von cleanup() ist das max. erlaubte Alter der Cachedateien in Sekunden. Ist der Wert 0, so werden *alle* Cachedateien gelöscht.
  • Die cleanup()-Mehode ignoriert alle anderen Dateitypen, die keine Cachedateien sind.
  • Leere Verzeichnisse ab dem Cache-Root werden gelöscht.

Dokumentation

Class: AhCache

Methoden verstecken

 __constructpublic __construct( $sModule = '.', $sCacheID = '.')
constructor
@param string $sModule name of module or app that uses the cache
@param string $sCacheID cache-id (must be uniq for a module; used to generate filename of cachefile)
@return boolean

 _setupprivate _setup()
load custom config from cache.class_config.php and set a cache
directory

 _cleanupDirprivate _cleanupDir( $sDir, $iTS) ... all param(s) required
recursive cleanup of a given directory; this function is used by
public function cleanup()
@since 2.0
@param string $sDir full path of a local directory
@param string $iTS timestamp
@return true

 _getAllCacheDataprivate _getAllCacheData()
private function _getAllCacheData() - read cachedata and its meta infos
@since 2.0
@return array array with data, file stat

 _getCacheFilenameprivate _getCacheFilename()
private function _getCacheFilename() - get full filename of cachefile
@return string full filename of cachefile

 cleanuppublic cleanup( $iSec = false)
Cleanup cache directory; delete all cachefiles older than n seconds
Other filetypes in the directory won't be touched.
Empty directories will be deleted.

Only the directory of the initialized module/ app will be deleted.
$o=new Cache("my-app"); $o->cleanup(60*60*24*3);

To delete all cachefles of all modules you can use
$o=new Cache(); $o->cleanup(0);

@since 2.0
@param int $iSec max age of cachefile; older cachefiles will be deleted
@return true

 deletepublic delete()
public function delete - delete a single cachefile if it exist
@return boolean

 dumppublic dump()
public function dump() - dump variables of cache class
@return true

 getAgepublic getAge()
public function getCacheAge() - get age in seconds of exisiting cachefile
@return int age in seconds; -1 if cachefiles does not exist

 getExpirepublic getExpire()
public function getExpire() - get TS of cache expiration
@since 2.0
@return int unix ts of cache expiration

 getTtlpublic getTtl()
public function getTtl() - get TTL of cache in seconds
@since 2.0
@return int get ttl of cache

 isExpiredpublic isExpired()
public function isExpired() - cache expired? To check it
you must use ttl while writing data, i.e.
$oCache->write($sData, $iTtl);
@since 2.0
@return bool cache is expired?

 iExpiredpublic iExpired()
public function iExpired() - get time in seconds when cachefile expires
you must use ttl while writing data, i.e.
$oCache->write($sData, $iTtl);
@since 2.1
@return int expired time in seconds; negative if cache is not expired

 isNewerThanFilepublic isNewerThanFile( $sRefFile) ... all param(s) required
function isNewerThanFile($sRefFile) - is the cache (still) newer than
a reference file? This function returns difference of mtime of both
files.
@since 2.0
@param string $sRefFile local filename
@return integer time in sec how much the cache file is newer; negative if reference file is newer

 readpublic read()
public function getCacheData() - read cachedata if it exist
@return various cachedata or false if cache does not exist

 setDatapublic setData( $data) ... all param(s) required
public function setData($data) - set cachedata into cache object
data can be any serializable type, like string, array or object
Remark: You additionally need to call the write() method to store data in the filesystem
@since 2.0
@param various $data data to store in cache
@return boolean

 setTtlpublic setTtl( $iTtl) ... all param(s) required
public function setTtl() - set TTL of cache in seconds
You need to write the cache data to ap
Remark: You additionally need to call the write() method to store a new ttl value with
data in the filesystem
@since 2.0
@param type $iTtl ttl value in seconds
@return int get ttl of cache

 touchpublic touch()
public function touch() - touch cachefile if it exist
For cachedata with a ttl a new expiration will be set
@return boolean

 writepublic write( $data = false, $iTtl = -1)
Write data into a cache.
- data can be any serializable type, like string, array or object
- set ttl in s (from now); optional parameter
@param various $data data to store in cache
@param int $iTtl time in s if content cache expires (min. 0)
@return bool success of write action

Properties verstecken

$sModulepublic $sModule = false
a module name string is used as relative cache path
@var string
$sCacheIDpublic $sCacheID = false
id of cachefile (filename will be generated from it)
@var string
$_sCacheDirprivate $_sCacheDir = false
where to store all cache data - it can be outside docRoot. If it is
below docRoot think about forbidding access
If empty it will be set in the constructor to [webroot]/~cache/
or $TEMP/~cache/ for CLI
@var string
$_sCacheFileprivate $_sCacheFile = false
absolute filename of cache file
@var string
$_sCacheDirDeviderprivate $_sCacheDirDevider = false
divider to limit count of cachefiles
@var type
$_sCacheExtprivate $_sCacheExt = cacheclass2
fileextension for storing cachefiles (without ".")
@var string
$_tsExpireprivate $_tsExpire = -1
Expiration timestamp;
It will be calculated with current time + ttl in the write() method
TTL can be read with getExpire()
@var integer
$_iTtlprivate $_iTtl = -1
TTL (time to live) in s;
TTL can be set in methods setTtl($iTtl) or write($data, $iTtl)
TTL can be read with getTtl()
@var integer
$_aCacheInfosprivate $_aCacheInfos = false
cachedata and file infos of cachefile (returned array of php function stat)
@var array