如何“动态”向对象添加新方法?
$me= new stdClass;
$me->doSomething=function ()
{
echo 'I\'ve done something';
};
$me->doSomething();
//Fatal error: Call to undefined method stdClass::doSomething()
答案 0 :(得分:92)
您可以使用__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();
答案 1 :(得分:40)
With PHP 7 you can use anonymous classes
$myObject = new class {
public function myFunction(){}
};
$myObject->myFunction();
答案 2 :(得分:18)
使用简单的__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"
答案 3 :(得分:12)
更新:此处显示的方法有一个主要缺点:新功能不是该类的完全限定成员;以这种方式调用时,方法中不存在
$this
。这意味着如果要使用对象实例中的数据或函数,则必须将对象作为参数传递给函数!此外,您将无法从这些功能中访问该班级的private
或protected
成员。
使用新的匿名函数的好问题和聪明的想法!
有趣的是,这有效:替换
$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()
是我能够做到这一点的唯一方法。
答案 4 :(得分:2)
您还可以将函数保存在数组中:
<?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);
?>
答案 5 :(得分:1)
如果没有__call
解决方案,您可以使用bindTo
(PHP&gt; = 5.4)来调用绑定到$this
$me
的方法,如下所示:
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"
或者,您可以将绑定函数分配给变量,然后在不使用call_user_func
的情况下调用它:
$f = $me->doSomething->bindTo($me);
$f();
答案 6 :(得分:1)
这对我有用:
$obj = new stdClass();
$obj->test = function(){
return 'Hi!';
};
return ($obj->test)();
答案 7 :(得分:0)
要了解如何使用eval执行此操作,您可以查看我的PHP微框架Halcyon,它可以在github上找到。它足够小,你应该能够毫无问题地解决它 - 专注于HalcyonClassMunger类。
答案 8 :(得分:0)
有一个similar post on stackoverflow清除了这只能通过实现某些设计模式来实现。
唯一的另一种方法是使用 classkit ,一个实验性的php扩展。 (也在帖子中)
是的,可以添加方法 定义后的PHP类。您 想要使用classkit,这是一个 “实验性”扩展。它出现 此扩展程序未启用 但是默认,这取决于是否 你可以编译自定义PHP二进制文件或 如果在Windows上加载PHP DLL(for 实例Dreamhost允许自定义 PHP二进制文件,它们非常简单 设置)。
答案 9 :(得分:0)
karim79 回答可行,但它将匿名函数存储在方法属性中,并且他的声明可以覆盖同名的现有属性,或者如果现有属性为private
导致致命错误对象无法使用,则无效认为将它们存储在单独的数组中并使用setter是更清晰的解决方案方法注入器设置器可以使用traits自动添加到每个对象。
P.S。当然这是不应该使用的黑客,因为它违反了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);