PHP Magische Methoden

Diese Woche habe ich mir eines meiner Lieblingsthemen für PHP ausgesucht. Mit magischen Methoden kann man ziemlich coole Sachen anstellen. Insgesamt gibt es 8 magische Methoden, die man dadurch erkennt, dass sie alle mit zwei Unterstrichen beginnen: __construct(), __desctruct(), __toString(), __get(), __set(), __call(), __wakeup() und __sleep(). Ich gehe die Methoden einfach mal alle durch und bei der ein oder anderen kann ich auch ein kleines Anwendungsbeispiel vorstellen.

__construct()
Der Konstruktor: Diese Methode wird aufgerufen, sobald ein Objekt einer Klasse angelegt wurde – das sollte allerdings schon jedem bekannt sein. In PHP4 musste der Konstruktor noch den gleichen Namen wie die Klasse tragen, das ist aber seit PHP5 und der Einführung von __construct() nicht mehr notwendig. Das macht das Umbenennen von Klassen schonmal etwas leichter.

__destruct()
Der Destruktor: Wenn ein Objekt mit unset() gelöscht wird, wird kurz zuvor nochmal der Destruktor aufgerufen.

__toString()
Wenn ein Objekt als String verwendet wird, kann man mit dieser Methode die Ausgabe bestimmen. Beispiel:

class Katze
{
    protected $name = "";
    protected $farbe = "";
 
    public function __construct($name, $farbe)
    {
        $this->name = $name;
        $this->farbe = $farbe;
    }
 
    public function __toString()
    {
        return "Meine Katze {$this->name} ist {$this->farbe}.";
    }
}
$objekt = new Katze("Charly", "schwarz-weiß");
echo $objekt; // Meine Katze Charly ist schwarz-weiß.

Natürlich funktioniert __toString() nicht nur bei echo, sondern überall dort, wo ein String erwartet wird.

__get($var)
Wird aufgerufen, sobald eine nicht definierte Membervariable der Klasse gelesen wurde. Als Parameter bekommt diese Methode den gewünschten Variablen-Namen. Also: echo $objekt->nichtDefiniert

__set($var, $value)
Funktioniert ähnlich wie __get(), ist aber zum Schreiben. Also: $objekt->nichtDefiniert = true

__sleep() und __wakeup()
Wenn ihr mal ein Objekt mit serialize() abspeichern wollt, dann könnten diese Methoden recht hilfreich sein. __sleep() wird jeweils vor serialize() aufgerufen und __wakeup() vor unserialize(). Ich habe das bisher noch nicht gebraucht…

__call($funktion, $parameter)
Zum Schluss nochmal die interessanteste und zudem mein Favorit unter den magischen Methoden. Mit __call() können aufgerufene Methoden abgefangen werden, die gar nicht existieren. Das eignet sich hervorragend, um automatisch Setter- und Getter-Methoden zu generieren. Hier ein kleines Beispiel:

class Katze
{
    protected $dataArray = array();
 
    public function __construct(array $dataArray = null)
    {
        if(is_array($dataArray))
        {
            $this->dataArray = $dataArray;
        }
    }
 
    public function __call($funktion, $parameter)
    {
        $name = strtolower(substr($funktion, 3));
        switch(substr($funktion, 0, 3))
        {
            case "get":
                return $this->dataArray[$name];
            break;
            case "set":
                $this->dataArray[$name] = $parameter[0];
            break;
        }
    }
}
 
$objekt = new Katze();
$objekt->setName("Charly");
echo $objekt->getName();

Man kann und sollte das __call() natürlich noch etwas ausschmücken und Fehler abfangen, aber das ist der minimale Weg, um in PHP dynamische Getter- und Setter-Methoden zu generieren. Ich nutze diese Funktion inzwischen ziemlich häufig dafür, aber es gibt bestimmt noch bessere Anwendungsmöglichkeiten für __call().

Da fällt mir doch gleich das nächste Thema ein: Magische Funktionen. Darüber werde ich dann demnächst schreiben.

SPL: DirectoryIterator und SplFileInfo

Heute geht es um die Standard PHP Library, kurz SPL. Oder genauer gesagt, um die Klassen DirectoryIterator und SplFileInfo. Vorab aber erstmal ein paar Infos zur SPL.

Die SPL ist eine Art Framework für PHP. Es besteht aber noch ein großer Unterschied zu normalen Frameworks, wie das Zend Framework, CodeInteger und Co, denn die SPL liegt in kompilierter Form vor. Das macht die Bibliothek nicht nur schneller, sondern bietet auch viel mehr Möglichkeiten. Die SPL ist nämlich genau wie die Sprache PHP selbst in C geschrieben. So kann man zum Beispiel mit SPL-Interfaces den eigenen Klassen ganz neue Funktionalitäten hinzufügen, die man mit Standard-Funktionen gar nicht erreichen kann. Über die SPL-Interfaces werde ich demnächst auch nochmal berichten – ist nämlich auch ein ziemlich interessantes Thema. Jetzt aber zum DirectoryIterator und der SplFileInfo.

Wenn man mit PHP Standard-Mitteln ein Verzeichnis ausgeben wollte, würde das ungefähr so aussehen:

function showDirectoryListing($path)
{
    $basename = basename($path);
    $handle = opendir($path);
    echo '<ul>';
    while($file = readdir($handle))
    {
        if($file != '.' && $file != '..')
        {
            if(is_dir($basename.$file))
            {
                echo '<li class="directory">'.$file.'</li>';
            }
            else
            {
                echo '<li class="file">'.$file.'</li>';
            }
        }
    }
    closedir($handle);
    echo '</ul>';
}

Besonders schön ist diese Lösung nicht, aber jetzt kommt die Klasse DirectoyIterator ins Spiel. Mit dieser Klassen können wir Verzeichnisse viel komfortabler Auslesen, denn wir bekommen jeden Eintrag als Objekt der Klasse SplFileInfo zur weiteren Verarbeitung zurückgeliefert. Die SplFileInfo-Klasse stellt viele Methoden zur Verfügung, um Dateien und Verzeichnisse besser bearbeiten zu können. Hier nochmal die Überarbeitete Fassung der obigen Funktion:

function showDirectoryListing($path)
{
    echo '<ul>';
    $handle = new DirectoryIterator($path);
    foreach($handle as $file)
    {
        if(!$file->isDot())
        {
            if($file->isDir())
            {
                echo '<li class="directory">'.$file->getFilename().'</li>';
            }
            else
            {
                echo '<li class="file">'.$file->getFilename().'</li>';
            }
        }
    }
    echo '</ul>';
}

Diese Variante sieht doch schon um einiges sauberer aus. Einen richtigen Unterschied wird man aber erst dann richtig bemerken, wenn man noch mehr mit den Dateien arbeiten muss (z.B. wenn es darum geht noch weitere Informationen zu den Dateien auszugeben).

Insgesamt helfen diese zwei Klassen also, den Code sauberer und komfortabler zu gestalten. Außerdem wird dadurch endlich der Zugriff auf Verzeichnisse und Dateien mit einer performanten Lösung standardisiert. Wenn ihr also das nächste Mal auf ein Verzeichnis zugreifen müsst, solltet ihr diesen Weg nutzen.

Und noch ein kleiner Tipp: SPL-Klassen lassen sich ohne Weiteres erweitern und so könnt ihr problemlos eine eigene, verbesserte FileInfo-Klasse schreiben (falls ihr so was braucht).