Sie könnten __call verwenden. Verwenden Sie jedoch bitte nicht __call. Das dynamische Ändern des Verhaltens eines Objekts ist eine sehr einfache Möglichkeit, Ihren Code unlesbar und nicht wartbar zu machen.
Sehr, sehr klug, aber klobig in einem realen Projekt IMO! (und +1, um anonymen Downvoter entgegenzuwirken)
Pekka
2
@ Pekka - aber wie genau ist es kludgy? Sicher, ich sage nicht, dass ich ein Experte für das Erweitern eines Objekts zur Laufzeit in PHP bin, aber ich kann ehrlich gesagt nicht sagen, dass ich viel falsch daran sehe. (Vielleicht habe ich nur einen schlechten Geschmack)
Karim79
16
Ich würde auch einen is_callable-Check hinzufügen (wenn Sie also nicht versehentlich versuchen, einen String aufzurufen). Und werfen Sie auch eine BadMethodCallException (), wenn Sie eine Methode nicht finden können, sodass sie nicht zurückgegeben wird und Sie glauben, dass sie erfolgreich zurückgegeben wurde. Machen Sie auch den ersten Parameter der Funktion zum Objekt selbst und führen Sie dann array_unshift($args, $this);vor dem Methodenaufruf einen aus, damit die Funktion einen Verweis auf das Objekt erhält, ohne es explizit binden zu müssen ...
ircmaxell
3
Ich denke, die Leute verpassen den Punkt, die ursprüngliche Anforderung war die Verwendung eines klassenlosen Objekts.
Thomas-Peter
1
Ich würde tatsächlich vorschlagen, dass Sie den isset () - Test vollständig entfernen, da Sie, wenn diese Funktion nicht definiert ist, wahrscheinlich nicht stillschweigend zurückkehren möchten.
Die Verwendung von einfach __call, um das Hinzufügen neuer Methoden zur Laufzeit zu ermöglichen, hat den Hauptnachteil, dass diese Methoden die $ this-Instanzreferenz nicht verwenden können. Alles funktioniert hervorragend, bis die hinzugefügten Methoden $ this nicht im Code verwenden.
Update: Der hier gezeigte Ansatz weist ein großes Manko auf: Die neue Funktion ist kein voll qualifiziertes Mitglied der Klasse. $thisist in der Methode nicht vorhanden, wenn sie auf diese Weise aufgerufen wird. Dies bedeutet, dass Sie das Objekt als Parameter an die Funktion übergeben müssen, wenn Sie mit Daten oder Funktionen aus der Objektinstanz arbeiten möchten! Außerdem können Sie über diese Funktionen nicht auf privateoder protectedMitglieder der Klasse zugreifen .
Gute Frage und clevere Idee mit den neuen anonymen Funktionen!
Interessanterweise funktioniert dies: Ersetzen
$me->doSomething();// Doesn't work
von call_user_func für die Funktion selbst :
call_user_func($me->doSomething);// Works!
Was nicht funktioniert, ist der "richtige" Weg:
call_user_func(array($me,"doSomething"));// Doesn't work
Wenn PHP so aufgerufen wird, muss die Methode in der Klassendefinition deklariert werden.
Ist das ein private/ public/ protectedSichtbarkeitsproblem?
Update: Nein. Es ist unmöglich, die Funktion auch innerhalb der Klasse auf normale Weise aufzurufen, daher ist dies kein Sichtbarkeitsproblem. call_user_func()Nur wenn ich die eigentliche Funktion an übergebe , kann ich scheinen, dass dies funktioniert.
Um zu sehen, wie dies mit eval gemacht wird, können Sie sich mein PHP-Mikro-Framework Halcyon ansehen, das auf github verfügbar ist . Es ist klein genug, damit Sie es problemlos herausfinden können - konzentrieren Sie sich auf die HalcyonClassMunger-Klasse.
Ich gehe davon aus (und mein Code auch), dass Sie auf PHP <5.3.0 laufen und daher Kludges und Hacks benötigen, damit dies funktioniert.
Ivan
Die Frage, ob jemand die Ablehnung kommentieren möchte, ist wohl sinnlos ...
ivans
Es ist wahrscheinlich, dass hier einige Massenabstimmungen stattfinden. Aber vielleicht möchten Sie etwas näher auf das eingehen, was Sie getan haben? Wie Sie sehen, ist dies eine interessante Frage, auf die es noch keine eindeutige Antwort gibt.
Pekka
1
Ohne die __callLösung können Sie bindTo(PHP> = 5.4) verwenden, um die Methode mit $thisgebundenem Wert $mewie folgt aufzurufen :
Das vollständige Skript könnte folgendermaßen aussehen:
$me =new stdClass;// Property for proving that the method has access to the above object:
$me->message ="I\'ve done something";
$me->doSomething =function(){
echo $this->message;};
call_user_func($me->doSomething->bindTo($me));// "I've done something"
Alternativ können Sie die gebundene Funktion einer Variablen zuweisen und sie dann aufrufen, ohne call_user_func:
Der einzige andere Weg ist die Verwendung von Classkit , einer experimentellen PHP-Erweiterung. (auch in der Post)
Ja, es ist möglich, einer PHP-Klasse eine Methode hinzuzufügen, nachdem sie definiert wurde. Sie möchten das Classkit verwenden, eine "experimentelle" Erweiterung. Es scheint jedoch, dass diese Erweiterung nicht standardmäßig aktiviert ist. Es hängt also davon ab, ob Sie eine benutzerdefinierte PHP-Binärdatei kompilieren oder unter Windows PHP-DLLs laden können (Dreamhost erlaubt beispielsweise benutzerdefinierte PHP-Binärdateien und sie sind recht einfach einzurichten ).
karim79 answer funktioniert, speichert jedoch anonyme Funktionen in Methodeneigenschaften und seine Deklarationen können vorhandene Eigenschaften mit demselben Namen überschreiben oder funktionieren nicht, wenn vorhandene Eigenschaften privateschwerwiegende Fehlerobjekte verursachen, die unbrauchbar sind. Ich denke, sie in einem separaten Array zu speichern und setter zu verwenden, ist eine sauberere Lösung. Der Methodeninjektorsetzer kann jedem Objekt automatisch mithilfe von Merkmalen hinzugefügt werden .
PS Natürlich ist dies ein Hack, der nicht verwendet werden sollte, da er gegen das Open-Closed-Prinzip von SOLID verstößt.
classMyClass{//create array to store all injected methodsprivate $anon_methods = array();//create setter to fill array with injected methods on runtimepublicfunction inject_method($name, $method){
$this->anon_methods[$name]= $method;}//runs when calling non existent or private methods from object instancepublicfunction __call($name, $args){if( key_exists($name, $this->anon_methods)){
call_user_func_array($this->anon_methods[$name], $args);}}}
$MyClass =newMyClass;//method one
$print_txt =function($text){
echo $text . PHP_EOL;};
$MyClass->inject_method("print_txt", $print_txt);//method
$add_numbers =function($n, $e){
echo "$n + $e = ".($n + $e);};
$MyClass->inject_method("add_numbers", $add_numbers);//Use injected methods
$MyClass->print_txt("Hello World");
$MyClass->add_numbers(5,10);
Antworten:
Sie können
__call
dies nutzen:quelle
array_unshift($args, $this);
vor dem Methodenaufruf einen aus, damit die Funktion einen Verweis auf das Objekt erhält, ohne es explizit binden zu müssen ...Mit PHP 7 können Sie anonyme Klassen verwenden
PHP RFC: Anonyme Klassen
quelle
Die Verwendung von einfach __call, um das Hinzufügen neuer Methoden zur Laufzeit zu ermöglichen, hat den Hauptnachteil, dass diese Methoden die $ this-Instanzreferenz nicht verwenden können. Alles funktioniert hervorragend, bis die hinzugefügten Methoden $ this nicht im Code verwenden.
Um dieses Problem zu lösen, können Sie das Muster auf diese Weise neu schreiben
Auf diese Weise können Sie neue Methoden hinzufügen, die die Instanzreferenz verwenden können
quelle
Gute Frage und clevere Idee mit den neuen anonymen Funktionen!
Interessanterweise funktioniert dies: Ersetzen
von call_user_func für die Funktion selbst :
Was nicht funktioniert, ist der "richtige" Weg:
Wenn PHP so aufgerufen wird, muss die Methode in der Klassendefinition deklariert werden.
Ist das ein
private
/public
/protected
Sichtbarkeitsproblem?Update: Nein. Es ist unmöglich, die Funktion auch innerhalb der Klasse auf normale Weise aufzurufen, daher ist dies kein Sichtbarkeitsproblem.
call_user_func()
Nur wenn ich die eigentliche Funktion an übergebe , kann ich scheinen, dass dies funktioniert.quelle
Sie können Funktionen auch in einem Array speichern:
quelle
Um zu sehen, wie dies mit eval gemacht wird, können Sie sich mein PHP-Mikro-Framework Halcyon ansehen, das auf github verfügbar ist . Es ist klein genug, damit Sie es problemlos herausfinden können - konzentrieren Sie sich auf die HalcyonClassMunger-Klasse.
quelle
Ohne die
__call
Lösung können SiebindTo
(PHP> = 5.4) verwenden, um die Methode mit$this
gebundenem Wert$me
wie folgt aufzurufen :Das vollständige Skript könnte folgendermaßen aussehen:
Alternativ können Sie die gebundene Funktion einer Variablen zuweisen und sie dann aufrufen, ohne
call_user_func
:quelle
Es gibt einen ähnlichen Beitrag zum Stapelüberlauf , der klarstellt , dass dies nur durch die Implementierung bestimmter Entwurfsmuster erreichbar ist.
Der einzige andere Weg ist die Verwendung von Classkit , einer experimentellen PHP-Erweiterung. (auch in der Post)
quelle
karim79 answer funktioniert, speichert jedoch anonyme Funktionen in Methodeneigenschaften und seine Deklarationen können vorhandene Eigenschaften mit demselben Namen überschreiben oder funktionieren nicht, wenn vorhandene Eigenschaften
private
schwerwiegende Fehlerobjekte verursachen, die unbrauchbar sind. Ich denke, sie in einem separaten Array zu speichern und setter zu verwenden, ist eine sauberere Lösung. Der Methodeninjektorsetzer kann jedem Objekt automatisch mithilfe von Merkmalen hinzugefügt werden .PS Natürlich ist dies ein Hack, der nicht verwendet werden sollte, da er gegen das Open-Closed-Prinzip von SOLID verstößt.
quelle