Anonyme Methoden - ActiveRecord in PHP?
Montag, 13. August 2007
Was mir besonders an Ruby on Rails gefallen hat, ist die Eleganz die die Datenbank-Abstraktion bietet. Die ActiveRecord getaufte Bibliothek ermöglicht einfache Suchabfragen der Art:
Beides gibt ein Objekt zurück, in dem jeweils das erste Objekt der Suchabfrage enthalten ist. Beides sind sehr schöne Möglichkeiten, um lesbaren Code zu bekommen. Es ist sofort klar, was man für Daten haben will. Außerdem muss für diese Abfrage nichts weiter programmieren. Alle Logik für diese Methoden sind in ActiveRecord enthalten. Man muss also keinerlei zusätzliche Entwicklung leisten.
So lange ich jetzt schon PHP programmiere habe ich eine ähnliche Implementierung noch nicht gesehen. Es ist mir kein Framework bekannt, was eine ähnliche Eleganz unter PHP erlaubt. Trotzdem hat es mich interessiert, ob eine solche Lösung auch unter PHP möglich ist.
Ich habe versucht, das ganze zu implementieren, in dem ich ein Objekt mit anonymen Methoden ausstatten will. Ich bin damit zu folgendem Ansatz gekommen. Das ganze stellt hier zwar nur Beispiel-Code dar, könnte aber dann im Endeffekt für das spezifische Problem implementiert werden:
Hier wird als erstes eine dynamische Klasse erstellt. Diese hat zwei normale Funktionen. Zusätzlich implementiert sie die magische Methode __call. Diese wird immer dann aufgerufen, wenn eine Methode in der Klasse aufgerufen werden soll, die bisher nicht existiert.
Innerhalb der Methode wird als erstes geprüft, ob die aufzurufende Methode dynamisch hinzugefügt wurde. Ist das der Fall, so wird die Methode einfach aufgerufen. Wenn nicht, wird das Programm beendet. Zum Schluss wird in dem Script die Klasse und die dynamisch erzeugte Methode geprüft.
Soweit funktioniert das ganze. Man hat den Eindruck, als wenn eine neue dynamische Methode erzeugt wurde. Leider sieht man in dem kurzen Beispiel aber schon ein großes Problem: Die Klassen-Variable $test ist public deklariert. Außerdem wird vor dem Aufruf der dynamischen Methode zusätzlich noch das eigene Objekt in den Parameter-Stack eingeführt.
Das Problem, was sich hier nämlich ergibt ist, dass die neue Methode sich nicht in die Klasse eingliedert, sondern komplett selbstständig ist. Sie wird zwar aus dem Kontext der Klasse aufgerufen, ist selber aber nicht Teil dieses Kontexts der Klasse! Es ist somit nicht möglich auf Variablen zuzugreifen die private oder protected deklariert wurden.
Im Gegensatz zu der Implementierung von Ruby on Rails habe ich hier versucht, ein Objekt mit einer anonymen Methode auszustatten. Das hat nur bedingt funktioniert. Schaut man sich jedoch die Lösung in Ruby an, ist das ganze nicht gelöst worden, in dem anonyme Methoden erstellt wurden, sondern in dem einfach nicht existierende Funktionen abgefangen werden, und diese dann analysiert werden, und die passenden Daten geladen werden.
Interessant ist nun, dass ich genau dieses Vorgehen hier genutzt habe, um anonyme Methoden zu interpretieren. Das heißt aber auch, dass nichts dagegen spricht, ein ähnliches Verhalten, wie es Ruby on Rails mit ActiveRecord zeigt, auch unter PHP zu implementieren. Dann kann man auch auch create_function und call_user_func_array verzichten.
Ich hatte hierbei speziell die Ansprüche, dass das ganze unter PHP5 funktioniert. Meiner Meinung nach ist es heutzutage nicht mehr nötig PHP4 zu unterstützten! Der gezeigte Code wird daher nicht unter PHP4 funktionieren. Ich denke auch nicht, dass es möglich wäre ähnliches unter PHP4 zu implementieren.
Modell.find_by_Tabellenspalte(:first, Wert)zu machen. Natürlich kann das alternativ auch noch mit and verknüpft werden:
Modell.find_by_Tabellenspalte1_and_Tabellenspalte2(:first, Wert1, Wert2)
Beides gibt ein Objekt zurück, in dem jeweils das erste Objekt der Suchabfrage enthalten ist. Beides sind sehr schöne Möglichkeiten, um lesbaren Code zu bekommen. Es ist sofort klar, was man für Daten haben will. Außerdem muss für diese Abfrage nichts weiter programmieren. Alle Logik für diese Methoden sind in ActiveRecord enthalten. Man muss also keinerlei zusätzliche Entwicklung leisten.
So lange ich jetzt schon PHP programmiere habe ich eine ähnliche Implementierung noch nicht gesehen. Es ist mir kein Framework bekannt, was eine ähnliche Eleganz unter PHP erlaubt. Trotzdem hat es mich interessiert, ob eine solche Lösung auch unter PHP möglich ist.
Ich habe versucht, das ganze zu implementieren, in dem ich ein Objekt mit anonymen Methoden ausstatten will. Ich bin damit zu folgendem Ansatz gekommen. Das ganze stellt hier zwar nur Beispiel-Code dar, könnte aber dann im Endeffekt für das spezifische Problem implementiert werden:
class dynamic_class {
private $methods = array();
public $test = null;
function addMethod($name, $method) {
$this -> methods[$name] = $method;
}
function showTest() {
echo $this -> test . "\n";
}
function __call($method, $parameters) {
if (array_key_exists($method, $this -> methods)) {
array_unshift($parameters, $this);
call_user_func_array($this -> methods['test'], $parameters);
}
else {
// error
die('METHOD NOT FOUND!');
}
}
}
$test = new dynamic_class();
$test -> addMethod('test', create_function('$obj, $args', '$obj -> test = $args;'));
$test -> test('dynamische methoden funktionieren!');
$test -> showTest();Hier wird als erstes eine dynamische Klasse erstellt. Diese hat zwei normale Funktionen. Zusätzlich implementiert sie die magische Methode __call. Diese wird immer dann aufgerufen, wenn eine Methode in der Klasse aufgerufen werden soll, die bisher nicht existiert.
Innerhalb der Methode wird als erstes geprüft, ob die aufzurufende Methode dynamisch hinzugefügt wurde. Ist das der Fall, so wird die Methode einfach aufgerufen. Wenn nicht, wird das Programm beendet. Zum Schluss wird in dem Script die Klasse und die dynamisch erzeugte Methode geprüft.
Soweit funktioniert das ganze. Man hat den Eindruck, als wenn eine neue dynamische Methode erzeugt wurde. Leider sieht man in dem kurzen Beispiel aber schon ein großes Problem: Die Klassen-Variable $test ist public deklariert. Außerdem wird vor dem Aufruf der dynamischen Methode zusätzlich noch das eigene Objekt in den Parameter-Stack eingeführt.
Das Problem, was sich hier nämlich ergibt ist, dass die neue Methode sich nicht in die Klasse eingliedert, sondern komplett selbstständig ist. Sie wird zwar aus dem Kontext der Klasse aufgerufen, ist selber aber nicht Teil dieses Kontexts der Klasse! Es ist somit nicht möglich auf Variablen zuzugreifen die private oder protected deklariert wurden.
Im Gegensatz zu der Implementierung von Ruby on Rails habe ich hier versucht, ein Objekt mit einer anonymen Methode auszustatten. Das hat nur bedingt funktioniert. Schaut man sich jedoch die Lösung in Ruby an, ist das ganze nicht gelöst worden, in dem anonyme Methoden erstellt wurden, sondern in dem einfach nicht existierende Funktionen abgefangen werden, und diese dann analysiert werden, und die passenden Daten geladen werden.
Interessant ist nun, dass ich genau dieses Vorgehen hier genutzt habe, um anonyme Methoden zu interpretieren. Das heißt aber auch, dass nichts dagegen spricht, ein ähnliches Verhalten, wie es Ruby on Rails mit ActiveRecord zeigt, auch unter PHP zu implementieren. Dann kann man auch auch create_function und call_user_func_array verzichten.
Ich hatte hierbei speziell die Ansprüche, dass das ganze unter PHP5 funktioniert. Meiner Meinung nach ist es heutzutage nicht mehr nötig PHP4 zu unterstützten! Der gezeigte Code wird daher nicht unter PHP4 funktionieren. Ich denke auch nicht, dass es möglich wäre ähnliches unter PHP4 zu implementieren.
Hallo, ich bin Sebastian!