如何添加一个新的方法在飞行中的PHP对象?

如何添加一个新的方法到一个对象“即时”?

$me= new stdClass; $me->doSomething=function () { echo 'I\'ve done something'; }; $me->doSomething(); //Fatal error: Call to undefined method stdClass::doSomething() 

你可以利用__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(); 

使用PHP7,您可以使用匿名类

 $myObject = new class { public function myFunction(){} }; $myObject->myFunction(); 

PHP的RFC:匿名类

为了允许在运行时添加新方法,只使用__call有一个主要的缺点,那就是这些方法不能使用$ this实例引用。 一切都很好,直到添加的方法不在代码中使用$ this。

 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 

为了解决这个问题,你可以用这种方法重写模式

 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)); } } 

通过这种方式,您可以添加可以使用实例引用的新方法

 $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" 

更新:这里显示的方法有一个主要的缺点:新function不是类的完全合格的成员; 当这种方式调用时, $this不存在于方法中。 这意味着如果要使用对象实例中的数据或函数,则必须将对象作为parameter passing给函数! 此外,您将无法从这些function访问privateprotected的class级成员。

使用新的匿名函数的好问题和聪明的想法!

有趣的是,这个工作:replace

 $me->doSomething(); // Doesn't work 

通过函数本身的call_user_func:

 call_user_func($me->doSomething); // Works! 

不行的是“正确的”方式:

 call_user_func(array($me, "doSomething")); // Doesn't work 

如果以这种方式调用,PHP需要在类定义中声明该方法。

这是一个private / public / protected可见性问题?

更新:不。 即使在课堂上也不可能以正常的方式调用函数,所以这不是一个可见性问题。 将实际的函数传递给call_user_func()是我看起来能够完成这个工作的唯一方法。

这里有一个简单的类,允许设置匿名函数。 这是一个优化的类https://gist.github.com/nickunderscoremok/5857846

 <?php class stdObject { public function __construct(array $arguments = array()) { if (!empty($arguments)) { foreach ($arguments as $property => $argument) { if ($argument instanceOf Closure) { $this->{$property} = $argument; } else { $this->{$property} = $argument; } } } } public function __call($method, $arguments) { if (isset($this->{$method}) && is_callable($this->{$method})) { return call_user_func_array($this->{$method}, $arguments); } else { throw new Exception("Fatal error: Call to undefined method stdObject::{$method}()"); } } } $person = new stdObject(array( "name" => "nick", "age" => 23, "friends" => array("frank", "sally", "aaron"), "sayHi" => function() { return "Hello there"; } )); $person->sayHi2 = function() { return "Hello there 2"; }; $person->test = function() { return "test"; }; var_dump($person->name, $person->test(), $person->sayHi2()); ?> 

你也可以将函数保存在一个数组中:

 <?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); ?> 

在stackoverflow上有一个类似的post清楚地表明,这只能通过实现某些devise模式来实现。

唯一的方法是通过使用classkit ,一个实验性的php扩展。 (也在post中)

是的,可以在PHP类定义之后添加一个方法。 你想使用classkit,这是一个“实验”的扩展。 看起来这个扩展并没有默认启用,所以它取决于你是否可以编译一个自定义的PHP二进制文件,或者如果在windows上加载PHP DLL(例如Dreamhost允许自定义PHP二进制文件,而且它们很容易设置)。

如果没有__call解决scheme,你可以使用bindTo (PHP> = 5.4)来调用$this绑定到$me $this方法:

 call_user_func($me->doSomething->bindTo($me, null)); 

完整的脚本可能如下所示:

 $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" 

或者,您可以将绑定函数分配给variables,然后在不使用call_user_func情况下调用它:

 $f = $me->doSomething->bindTo($me); $f(); 

我永远不会使用它,语法是可怕的,但你可以添加一个对象的函数属性。 静态的时候可以保存inputnew() ;

 class SomeFuncs{ static function a(){ /* do something */ }} $o = new stdClass(); $o->f = SomeFuncs; $o->f::a(); 

karim79答案工作,但它存储方法属性内的匿名函数和他的声明可以覆盖现有的属性相同的名称或不工作,如果现有的属性是private导致致命的错误对象不可用我认为存储在单独的数组,并使用setter是更干净的解决scheme。 方法喷油器设置器可以使用特性自动添加到每个对象。

PS当然这是不应该使用的破解,因为它违反了SOLID的开放式封闭原则。

 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); 

要看看如何用eval来做到这一点,你可以看看我的PHP微型框架,Halcyon,这是在github上可用。 它足够小,你应该能够find没有任何问题 – 专注于HalcyonClassMunger类。