如何动态地向php对象添加新方法?

Mad*_*Bad 94 php methods

如何"动态"向对象添加新方法?

$me= new stdClass;
$me->doSomething=function ()
 {
    echo 'I\'ve done something';
 };
$me->doSomething();

//Fatal error: Call to undefined method stdClass::doSomething()
Run Code Online (Sandbox Code Playgroud)

kar*_*m79 93

你可以利用__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();
Run Code Online (Sandbox Code Playgroud)

  • 我会添加一个is_callable检查,如果那样(所以你不小心尝试调用一个字符串).如果找不到方法,也会抛出一个BadMethodCallException(),所以你没有返回它并认为它确实返回成功.另外,将函数的第一个参数设置为对象本身,然后在方法调用之前执行`array_unshift($ args,$ this);`,以便函数获取对对象的引用,而无需明确地绑定它. .. (15认同)
  • 非常,非常聪明,但在现实世界项目IMO kludgy!(以及+1来反击匿名下注者) (6认同)
  • 我认为人们忽略了这一点,最初的要求是使用无类对象. (3认同)
  • @pekka - 但是,究竟是怎么回事呢?当然,我不是说我是在PHP运行时扩展对象的专家,但老实说我不能说我看到它有多大错.(也许我的味道很差) (2认同)
  • 我实际上建议您完全删除 isset() 测试,因为如果未定义该函数,您可能不想静默返回。 (2认同)

Moh*_*ami 43

使用PHP 7,您可以使用匿名类

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

$myObject->myFunction();
Run Code Online (Sandbox Code Playgroud)

PHP RFC:匿名类

  • 这帮助了我.非常感谢. (3认同)

ale*_*oat 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
Run Code Online (Sandbox Code Playgroud)

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

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

$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"
Run Code Online (Sandbox Code Playgroud)


Pek*_*ica 12

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

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

有趣的是,这有效:替换

$me->doSomething();    // Doesn't work
Run Code Online (Sandbox Code Playgroud)

通过函数本身的call_user_func :

call_user_func($me->doSomething);    // Works!
Run Code Online (Sandbox Code Playgroud)

什么是行不通的是"正确的"方式:

call_user_func(array($me, "doSomething"));   // Doesn't work
Run Code Online (Sandbox Code Playgroud)

如果这样调用,PHP需要在类定义中声明该方法.

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

更新:不.即使从类中也不可能以正常方式调用函数,因此这不是可见性问题.传递实际功能call_user_func()是我能够做到这一点的唯一方法.