Heute hab ich mir mal diverse magische Funktionen angeschaut. Seit PHP5 kann man in Klasse diverse dieser Methoden definieren, damit bestimmte Dinge automatisiert funktionieren. Da wäre zum einen “__set()” und “__get()” zu erwähnen. Diese beiden Methoden werden von PHP aufgerufen, wenn im Code auf einen Membervariable zugreifen will, die entweder nicht existiert, oder aber auf die nicht zugegriffen werden darf, weil diese “private” deklariert ist.
Folgendes Code-Beispiel zeigt, wie das gemeint ist:
class test {
private $iTest = 42;
function test ()
{
return;
}
function __get($psVar)
{
return $this->$psVar;
}
function __set($psVar, $pmVal)
{
$this->$psVar = $psVal;
}
}
$oTest = new test();
$oTest->iTest = 23;
echo $oTest->iTest;
Obwohl die Membervariable $iTest private ist, kann so trotzdem darauf zugegriffen werden. Natürlich ergibt das oben genannte Beispiel so wenig Sinn. In diesen “setter” und “getter” Methoden würde man noch eine gewisse Logik implementieren.
Es lässt sich so zwar schöner programmieren, aber jeder Automatismus hat eben in der Regel auch den Nachteil, dass er Rechenzeit benötigt. PHP muss beim Zugriff auf die Variable erst mal erkennen, dass diese nicht existiert oder das nicht darauf zugegriffen werden darf. Wenn dies passiert ist, muss PHP nach einer “__get()” bzw. “__set()” Methode suchen und diese ausführen. Und so wirkt sich das aus, wenn man es mit dem klassischen “set_iTest()”-Ansatz (eine Getter- / Setter-Methode pro Variable) vergleicht:


Man kann also schön sehen, dass das magische __get() 73,25% mehr Zeit benötigt. Das magische __set() benötigt 62,90% mehr Zeit.
Das gleiche gibt es auch für Funktionsaufrufe. Da definiert man eine “__call()” Methode, die aufgerufen wird, wenn die tatsächlich aufgerufenen Methode nicht existiert. Damit lässt sich auf einfachste Art und Weiße eine Proxie-Klasse erschaffen, doch zu welchem Preis gegenüber dem direkten Aufruf?

41,30% Aufpreis an Zeit.
Fazit: Natürlich waren die verwendeten Skripte nur auf das Minimum beschränkt, in einer Gesamtapplikation würde sich der negative Effekt durch das ganze automagische nicht so stark auswirken, aber im großen und ganzen ist eben zu erkennen. Flexibilität kostet Performance und das nicht zu knapp.
OP-Code-Cache: Die Idee, dass hier evtl. ein OP-Code-Cache helfen kann, hatte ich auch, allerdings funktioniert dass nicht. Denn innerhalb der “__get()”-Methode z.B. bekommt man die eigentlich angeforderte Variable per Argument übergeben. Um dann an die richtige Variable heranzukommen ist ein solches Konstrukt nötig:
...
function __get($psVar)
{
return $this->$psVar;
}
...
Und dieser dynamische Zugriff kann erst zur Laufzeit aufgelöst werden, nicht vorher, daher steht im OP-Code etwa das gleich drin wie hier, lediglich als OP-Code. Der dynamische Zugriff und damit das eigentlich langsame bleiben auch da erhalten.
Der Test: Durchgeführt wurde der Test mit PHP 5.2.5 mit Suhosin Patch 0.9.6.2 auf einem Intel P4 mit 2.8 GHz und 2GB Ram.
Weiterführende Ideen: Da das mit dem OP-Code-Cache nichts gebracht hat, könnte evtl. der Einsatz eines Optimizers etwas richten. Das werde ich evtl. mal angehen, denn XCache wird demnächst einen Optimizer haben und ich hab was von einem ZendOptimizer gehört.