Bir nesneye "anında" yeni bir yöntemi nasıl eklerim?
$me= new stdClass;
$me->doSomething=function ()
{
echo 'I\'ve done something';
};
$me->doSomething();
//Fatal error: Call to undefined method stdClass::doSomething()
Bir nesneye "anında" yeni bir yöntemi nasıl eklerim?
$me= new stdClass;
$me->doSomething=function ()
{
echo 'I\'ve done something';
};
$me->doSomething();
//Fatal error: Call to undefined method stdClass::doSomething()
Yanıtlar:
Bunun için koşabilirsiniz __call
:
class Foo
{
public function __call($method, $args)
{
if (isset($this->$method)) {
$func = $this->$method;
return call_user_func_array($func, $args);
}
}
}
$foo = new Foo();
$foo->bar = function () { echo "Hello, this function is added at runtime"; };
$foo->bar();
array_unshift($args, $this);
yöntem çağrısından önce bir tane yapın , böylece işlev nesneye açıkça bağlanmasına gerek kalmadan bir başvuru alır ...
PHP 7 ile anonim sınıfları kullanabilirsiniz
$myObject = new class {
public function myFunction(){}
};
$myObject->myFunction();
Çalışma zamanında yeni yöntemler eklemeye izin vermek için basitçe __call kullanmak, bu yöntemlerin $ this örnek başvurusunu kullanamaması gibi büyük bir dezavantaja sahiptir. Eklenen yöntemler kodda $ this kullanmayana kadar her şey harika çalışıyor.
class AnObj extends stdClass
{
public function __call($closure, $args)
{
return call_user_func_array($this->{$closure}, $args);
}
}
$a=new AnObj();
$a->color = "red";
$a->sayhello = function(){ echo "hello!";};
$a->printmycolor = function(){ echo $this->color;};
$a->sayhello();//output: "hello!"
$a->printmycolor();//ERROR: Undefined variable $this
Bu sorunu çözmek için kalıbı bu şekilde yeniden yazabilirsiniz.
class AnObj extends stdClass
{
public function __call($closure, $args)
{
return call_user_func_array($this->{$closure}->bindTo($this),$args);
}
public function __toString()
{
return call_user_func($this->{"__toString"}->bindTo($this));
}
}
Bu şekilde, örnek referansını kullanabilen yeni yöntemler ekleyebilirsiniz.
$a=new AnObj();
$a->color="red";
$a->sayhello = function(){ echo "hello!";};
$a->printmycolor = function(){ echo $this->color;};
$a->sayhello();//output: "hello!"
$a->printmycolor();//output: "red"
Güncelleme: Burada gösterilen yaklaşımın büyük bir kusuru vardır: Yeni işlev, sınıfın tam nitelikli bir üyesi değildir;
$this
bu şekilde çağrıldığında yöntemde mevcut değildir. Bu, nesne örneğinden veri veya işlevlerle çalışmak istiyorsanız nesneyi bir parametre olarak işleve geçirmeniz gerektiği anlamına gelir! Ayrıca, bu işlevlerden sınıfın üyelerineprivate
veyaprotected
üyelerine erişemezsiniz .
Yeni anonim işlevleri kullanarak iyi soru ve akıllıca fikir!
İlginç bir şekilde, bu işe yarıyor: Değiştir
$me->doSomething(); // Doesn't work
call_user_func tarafından işlevin kendisinde :
call_user_func($me->doSomething); // Works!
işe yaramayan şey "doğru" yoldur:
call_user_func(array($me, "doSomething")); // Doesn't work
bu şekilde çağrılırsa, PHP, yöntemin sınıf tanımında bildirilmesini gerektirir.
Bu bir private
/ public
/ protected
görünürlük sorunu mu?
Güncelleme: Hayır. Sınıfın içinden bile işlevi normal şekilde çağırmak imkansızdır, bu nedenle bu bir görünürlük sorunu değildir. Gerçek işlevi devretmek call_user_func()
, bunu çalıştırabilmemin tek yolu.
fonksiyonları bir diziye de kaydedebilirsiniz:
<?php
class Foo
{
private $arrayFuncs=array();// array of functions
//
public function addFunc($name,$function){
$this->arrayFuncs[$name] = $function;
}
//
public function callFunc($namefunc,$params=false){
if(!isset($this->arrayFuncs[$namefunc])){
return 'no function exist';
}
if(is_callable($this->arrayFuncs[$namefunc])){
return call_user_func($this->arrayFuncs[$namefunc],$params);
}
}
}
$foo = new Foo();
//Save function on array variable with params
$foo->addFunc('my_function_call',function($params){
return array('data1'=>$params['num1'],'data2'=>'qwerty','action'=>'add');
});
//Save function on array variable
$foo->addFunc('my_function_call2',function(){
return 'a simple function';
});
//call func 1
$data = $foo->callFunc('my_function_call',array('num1'=>1224343545));
var_dump($data);
//call func 2
$data = $foo->callFunc('my_function_call2');
var_dump($data);
?>
Eval ile bunun nasıl görmek için, geçerli benim PHP mikro çerçevede, Halcyon, bir göz alabilir github . Herhangi bir sorun yaşamadan çözebilmeniz için yeterince küçüktür - HalcyonClassMunger sınıfına odaklanın.
Olmadan __call
çözümü kullanabilirsiniz bindTo
(PHP> = 5.4) ile yöntemini çağırmak için $this
bağlı $me
böyle:
call_user_func($me->doSomething->bindTo($me, null));
Komut dosyasının tamamı şöyle görünebilir:
$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"
Alternatif olarak, bağlı işlevi bir değişkene atayabilir ve ardından onu şun olmadan çağırabilirsiniz call_user_func
:
$f = $me->doSomething->bindTo($me);
$f();
Bir var stackoverflow benzer sonrası bu belli tasarım desenleri uygulanması yoluyla sadece ulaşılabilir olduğunu temizler.
Diğer tek yol, deneysel bir php uzantısı olan classkit'in kullanılmasıdır . (ayrıca gönderide)
Evet, tanımlandıktan sonra bir PHP sınıfına bir yöntem eklemek mümkündür. "Deneysel" bir uzantı olan classkit'i kullanmak istiyorsunuz. Görünüşe göre bu uzantı varsayılan olarak etkin değildir, bu nedenle özel bir PHP ikili dosyası derleyip derleyemeyeceğinize veya Windows üzerinde ise PHP DLL'leri yükleyip yükleyemeyeceğinize bağlıdır (örneğin Dreamhost özel PHP ikili dosyalarına izin verir ve bunların kurulumu oldukça kolaydır. ).
karim79 cevabı çalışır ancak anonim fonksiyonları metot özelliklerinde saklar ve bildirimleri aynı isimdeki mevcut özelliklerin üzerine yazabilir veya mevcut özellik private
ölümcül hata nesnesinin kullanılamamasına neden oluyorsa çalışmaz.Bence bunları ayrı bir dizide depolamak ve setter kullanmak daha temiz bir çözümdür. yöntem enjektör ayarlayıcı özellikleri kullanılarak her nesneye otomatik olarak eklenebilir .
Not Tabii ki bu, SOLID'in açık kapalı ilkesini ihlal ettiği için kullanılmaması gereken bir hack'tir.
class MyClass {
//create array to store all injected methods
private $anon_methods = array();
//create setter to fill array with injected methods on runtime
public function inject_method($name, $method) {
$this->anon_methods[$name] = $method;
}
//runs when calling non existent or private methods from object instance
public function __call($name, $args) {
if ( key_exists($name, $this->anon_methods) ) {
call_user_func_array($this->anon_methods[$name], $args);
}
}
}
$MyClass = new MyClass;
//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);