如何动态地向 php 对象添加新方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 
原文地址: http://stackoverflow.com/questions/2938004/
Warning: these are provided under cc-by-sa 4.0 license.  You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to add a new method to a php object on the fly?
提问by MadBad
How do I add a new method to an object "on the fly"?
如何“动态”向对象添加新方法?
$me= new stdClass;
$me->doSomething=function ()
 {
    echo 'I\'ve done something';
 };
$me->doSomething();
//Fatal error: Call to undefined method stdClass::doSomething()
采纳答案by karim79
You can harness __callfor this:
你可以利用__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();
回答by Mohamed Ramrami
With PHP 7 you can use anonymous classes
使用 PHP 7,您可以使用匿名类
$myObject = new class {
    public function myFunction(){}
};
$myObject->myFunction();
回答by alexroat
Using simply __call in order to allow adding new methods at runtime has the major drawback that those methods cannot use the $this instance reference. Everything work great, till the added methods don't use $this in the code.
简单地使用 __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
In order to solve this problem you can rewrite the pattern in this way
为了解决这个问题,你可以用这种方式重写模式
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));
    }
}
In this way you can add new methods that can use the instance reference
通过这种方式,您可以添加可以使用实例引用的新方法
$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"
回答by Pekka
Update:The approach shown here has a major shortcoming: The new function is not a fully qualified member of the class;
$thisis not present in the method when invoked this way. This means that you would have to pass the object to the function as a parameter if you want to work with data or functions from the object instance! Also, you will not be able to accessprivateorprotectedmembers of the class from these functions.
更新:这里显示的方法有一个主要缺点:新函数不是类的完全限定成员;
$this以这种方式调用时,方法中不存在。这意味着如果要使用对象实例中的数据或函数,则必须将对象作为参数传递给函数!此外,您将无法从这些函数访问该类private或protected该类的成员。
Good question and clever idea using the new anonymous functions!
使用新的匿名函数的好问题和聪明的想法!
Interestingly, this works: Replace
有趣的是,这有效:替换
$me->doSomething();    // Doesn't work
by call_user_func on the function itself:
通过 call_user_func函数本身:
call_user_func($me->doSomething);    // Works!
what doesn't work is the "right" way:
行不通的是“正确”的方式:
call_user_func(array($me, "doSomething"));   // Doesn't work
if called that way, PHP requires the method to be declared in the class definition.
如果以这种方式调用,PHP 需要在类定义中声明该方法。
Is this a  private/ public/ protectedvisibility issue?
这是一个  private/ public/protected可见性问题?
Update:Nope. It's impossible to call the function the normal way even from within the class, so this is not a visibility issue. Passing the actual function to call_user_func()is the only way I can seem to make this work.
更新:没有。即使在类中也不可能以正常方式调用该函数,因此这不是可见性问题。将实际函数传递给call_user_func()是我似乎可以完成这项工作的唯一方法。
回答by miglio
you can also save functions in an array:
您还可以将函数保存在数组中:
<?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);
?>
回答by trincot
Without the __callsolution, you can use bindTo(PHP >= 5.4), to call the method with $thisbound to $melike this:
如果没有__call解决方案,您可以使用bindTo(PHP >= 5.4) 来调用$this绑定到$me这样的方法:
call_user_func($me->doSomething->bindTo($me, null)); 
The complete script could look like this:
完整的脚本可能如下所示:
$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"
Alternatively, you could assign the bound function to a variable, and then call it without call_user_func:
或者,您可以将绑定函数分配给一个变量,然后在不使用的情况下调用它call_user_func:
$f = $me->doSomething->bindTo($me);
$f(); 
回答by ivans
To see how to do this with eval, you can take a look at my PHP micro-framework, Halcyon, which is available on github. It's small enough that you should be able to figure it out without any problems - concentrate on the HalcyonClassMunger class.
要了解如何使用 eval 执行此操作,您可以查看我的 PHP 微框架 Halcyon,它可以在github 上找到。它足够小,您应该能够毫无问题地弄清楚它 - 专注于 HalcyonClassMunger 类。
回答by Zilverdistel
There's a similar post on stackoverflowthat clears out that this is only achievable through the implementation of certain design patterns.
stackoverflow 上有一个类似的帖子,明确指出这只能通过实现某些设计模式来实现。
The only other way is through the use of classkit, an experimental php extension. (also in the post)
唯一的另一种方法是使用classkit,这是一个实验性的 php 扩展。(也在帖子里)
Yes it is possible to add a method to a PHP class after it is defined. You want to use classkit, which is an "experimental" extension. It appears that this extension isn't enabled by default however, so it depends on if you can compile a custom PHP binary or load PHP DLLs if on windows (for instance Dreamhost does allow custom PHP binaries, and they're pretty easy to setup).
是的,可以在定义后向 PHP 类添加方法。您想使用 classkit,这是一个“实验性”扩展。但是,似乎默认情况下未启用此扩展程序,因此这取决于您是否可以编译自定义 PHP 二进制文件或在 Windows 上加载 PHP DLL(例如 Dreamhost 确实允许自定义 PHP 二进制文件,并且它们很容易设置)。
回答by Roman Toasov
karim79answer works however it stores anonymous functions inside method properties and his declarations can overwrite existing properties of same name or does not work if existing property is privatecausing fatal error object unusable i think storing them in separate array and using setter is cleaner solution. method injector setter can be added to every object automatically using traits.
karim79答案有效,但是它在方法属性中存储匿名函数,并且他的声明可以覆盖同名的现有属性,或者如果现有属性private导致致命错误对象无法使用则不起作用我认为将它们存储在单独的数组中并使用 setter 是更清晰的解决方案。可以使用traits自动将方法注入器 setter 添加到每个对象。
P.S. Of course this is hack that should not be used as it violates open closed principle of SOLID.
PS 当然,这是不应该使用的 hack,因为它违反了 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);

