Możesz użyć __call. Ale proszę nie używać __call. Dynamiczna zmiana zachowania obiektu to bardzo łatwy sposób na uczynienie kodu nieczytelnym i niemożliwym do utrzymania.
Bardzo, bardzo sprytny, ale niezdarny w prawdziwym projekcie IMO! (i +1 do kontrataku anonimowego przeciwnika)
Pekka
2
@pekka - ale jak właściwie to jest niezdarne? Jasne, nie twierdzę, że jestem ekspertem w rozszerzaniu obiektu w PHP w czasie wykonywania, ale szczerze mówiąc, nie mogę powiedzieć, że widzę w tym wiele złego. (może po prostu mam kiepski gust)
karim79
16
Dodałbym is_callable check in that if (Więc nie próbujesz przypadkowo wywołać ciągu). A także wyrzuć BadMethodCallException (), jeśli nie możesz znaleźć metody, więc nie musisz jej zwracać i myślisz, że powróciła pomyślnie. Ponadto, uczyń pierwszy parametr funkcji samym obiektem, a następnie wykonaj procedurę array_unshift($args, $this);przed wywołaniem metody, aby funkcja otrzymała odniesienie do obiektu bez konieczności jawnego wiązania go ...
ircmaxell
3
Myślę, że ludzie nie mają sensu, pierwotnym wymaganiem było użycie obiektu bezklasowego.
thomas-peter
1
Właściwie sugerowałbym całkowite usunięcie testu isset (), ponieważ jeśli ta funkcja nie jest zdefiniowana, prawdopodobnie nie chcesz wracać po cichu.
Używanie prostego __call w celu umożliwienia dodawania nowych metod w czasie wykonywania ma tę główną wadę, że te metody nie mogą używać odwołania $ this do instancji. Wszystko działa świetnie, dopóki dodane metody nie używają $ this w kodzie.
Aktualizacja: Przedstawione tutaj podejście ma poważną wadę: nowa funkcja nie jest w pełni wykwalifikowanym członkiem klasy; $thisnie występuje w metodzie, gdy jest wywoływana w ten sposób. Oznacza to, że musisz przekazać obiekt do funkcji jako parametr, jeśli chcesz pracować z danymi lub funkcjami z instancji obiektu! Ponadto nie będzie można uzyskać dostępu privateani protectedczłonków klasy z tych funkcji.
Dobre pytanie i sprytny pomysł z wykorzystaniem nowych anonimowych funkcji!
Co ciekawe, to działa: Wymień
$me->doSomething();// Doesn't work
przez call_user_func na samej funkcji :
call_user_func($me->doSomething);// Works!
to, co nie działa, to „właściwy” sposób:
call_user_func(array($me,"doSomething"));// Doesn't work
jeśli tak się nazywa, PHP wymaga zadeklarowania metody w definicji klasy.
Czy jest to problem z widocznością private/ public/ protected?
Aktualizacja: nie . Niemożliwe jest wywołanie funkcji w normalny sposób, nawet z poziomu klasy, więc nie jest to problem z widocznością. Przekazanie faktycznej funkcji call_user_func()to jedyny sposób, w jaki mogę to zrobić.
Aby zobaczyć, jak to zrobić za pomocą eval, możesz rzucić okiem na mój mikro-framework PHP, Halcyon, który jest dostępny na github . Jest na tyle mały, że powinieneś być w stanie to rozgryźć bez żadnych problemów - skoncentruj się na klasie HalcyonClassMunger.
Zakładam (podobnie jak mój kod), że pracujesz na PHP <5.3.0 i dlatego potrzebujesz kludges i hacków, aby to zadziałało.
ivans
Pytanie, czy ktoś chciałby skomentować głos przeciw nie ma sensu, przypuszczam ...
ivans
Prawdopodobnie tak jest, że dzieje się tutaj masowe przegłosowanie. Ale może chcesz trochę rozwinąć to, co zrobiłeś? Jak widać, jest to interesujące pytanie, na które nie ma jeszcze określonej odpowiedzi.
Pekka
1
Bez __callrozwiązania możesz użyć bindTo(PHP> = 5.4), aby wywołać metodę z $thispowiązaniem w $menastępujący sposób:
$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"
Alternatywnie możesz przypisać powiązaną funkcję do zmiennej, a następnie wywołać ją bez call_user_func:
Jest podobny post w stackoverflow, który wyjaśnia, że jest to osiągalne tylko poprzez implementację pewnych wzorców projektowych.
Jedynym innym sposobem jest użycie classkit , eksperymentalnego rozszerzenia php. (również w poście)
Tak, istnieje możliwość dodania metody do klasy PHP po jej zdefiniowaniu. Chcesz użyć zestawu klas, który jest rozszerzeniem „eksperymentalnym”. Wygląda na to, że to rozszerzenie nie jest domyślnie włączone, więc zależy to od tego, czy możesz skompilować niestandardowy plik binarny PHP lub załadować biblioteki DLL PHP w systemie Windows (na przykład Dreamhost zezwala na niestandardowe pliki binarne PHP i są one dość łatwe do skonfigurowania ).
odpowiedź karim79 działa, ale przechowuje anonimowe funkcje we właściwościach metody, a jego deklaracje mogą nadpisywać istniejące właściwości o tej samej nazwie lub nie działa, jeśli istniejąca właściwość privatepowoduje błąd krytyczny, obiekt jest bezużyteczny. metodę ustawiającą wtryskiwacz można dodać do każdego obiektu automatycznie przy użyciu cech .
PS Oczywiście jest to hack, którego nie należy używać, ponieważ narusza on zasadę open closed SOLID.
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);
Odpowiedzi:
Możesz to wykorzystać
__call
:źródło
array_unshift($args, $this);
przed wywołaniem metody, aby funkcja otrzymała odniesienie do obiektu bez konieczności jawnego wiązania go ...W PHP 7 możesz używać anonimowych klas
PHP RFC: Anonimowe klasy
źródło
Używanie prostego __call w celu umożliwienia dodawania nowych metod w czasie wykonywania ma tę główną wadę, że te metody nie mogą używać odwołania $ this do instancji. Wszystko działa świetnie, dopóki dodane metody nie używają $ this w kodzie.
Aby rozwiązać ten problem, możesz przepisać wzór w ten sposób
W ten sposób możesz dodać nowe metody, które mogą używać odwołania do instancji
źródło
Dobre pytanie i sprytny pomysł z wykorzystaniem nowych anonimowych funkcji!
Co ciekawe, to działa: Wymień
przez call_user_func na samej funkcji :
to, co nie działa, to „właściwy” sposób:
jeśli tak się nazywa, PHP wymaga zadeklarowania metody w definicji klasy.
Czy jest to problem z widocznością
private
/public
/protected
?Aktualizacja: nie . Niemożliwe jest wywołanie funkcji w normalny sposób, nawet z poziomu klasy, więc nie jest to problem z widocznością. Przekazanie faktycznej funkcji
call_user_func()
to jedyny sposób, w jaki mogę to zrobić.źródło
możesz także zapisać funkcje w tablicy:
źródło
Aby zobaczyć, jak to zrobić za pomocą eval, możesz rzucić okiem na mój mikro-framework PHP, Halcyon, który jest dostępny na github . Jest na tyle mały, że powinieneś być w stanie to rozgryźć bez żadnych problemów - skoncentruj się na klasie HalcyonClassMunger.
źródło
Bez
__call
rozwiązania możesz użyćbindTo
(PHP> = 5.4), aby wywołać metodę z$this
powiązaniem w$me
następujący sposób:Kompletny skrypt mógłby wyglądać następująco:
Alternatywnie możesz przypisać powiązaną funkcję do zmiennej, a następnie wywołać ją bez
call_user_func
:źródło
Jest podobny post w stackoverflow, który wyjaśnia, że jest to osiągalne tylko poprzez implementację pewnych wzorców projektowych.
Jedynym innym sposobem jest użycie classkit , eksperymentalnego rozszerzenia php. (również w poście)
źródło
odpowiedź karim79 działa, ale przechowuje anonimowe funkcje we właściwościach metody, a jego deklaracje mogą nadpisywać istniejące właściwości o tej samej nazwie lub nie działa, jeśli istniejąca właściwość
private
powoduje błąd krytyczny, obiekt jest bezużyteczny. metodę ustawiającą wtryskiwacz można dodać do każdego obiektu automatycznie przy użyciu cech .PS Oczywiście jest to hack, którego nie należy używać, ponieważ narusza on zasadę open closed SOLID.
źródło