2012-06-11 102 views
23

我想知道是否有一种方法可以在运行时在php中将新方法附加到类中。 我的意思是,不是在实例级别,而是直接到类,以便所有新创建的实例都有这个新方法。 这样的事情能用反射来完成吗?php在运行时创建类方法

感谢

回答

20

是的,你可以。

下面是在运行时在php 5.4.x中创建方法的方法。

匿名函数由从5.3.x开始的Closure类表示。从5.4.x开始,它添加一个Closure::bind静态方法来将匿名函数绑定到特定的对象或类。

实施例:

class Foo { 
    private $methods = array(); 

    public function addBar() { 
     $barFunc = function() { 
     var_dump($this->methods); 
     }; 
     $this->methods['bar'] = \Closure::bind($barFunc, $this, get_class()); 
    } 

    function __call($method, $args) { 
      if(is_callable($this->methods[$method])) 
      { 
      return call_user_func_array($this->methods[$method], $args); 
      } 
    } 
} 

$foo = new Foo; 
$foo->addBar(); 
$foo->bar(); 
+2

太棒了。喜欢这个新的PHP特性 – Thomas

+0

这里的__call方法有什么意义,如果\ Closure :: bind做的事情,反之亦然? – jayarjo

+2

@jayarjo''\ Closure :: bind'改变了匿名函数的上下文(在这种情况下,它确保在关闭中'$ this'指向正确的对象)。但是,它不会将该函数转换为'$ this'的方法。我们仍然需要'__call'来允许外部代码像调用方法那样调用闭包。 –

3

你已经采取了看看在文档create_function()?你也可以通过overloading达到预期的结果。

+0

create_function(如FAS我所知)在全球范围内创造功能和超载是不是干净的一个解决方案,我想它是 – Thomas

+0

我明白了。然而,PHP不像在这种情况下理想的动态语言。我相信[反思](http://www.php.net/manual/en/intro.reflection.php)或[Runkit](http://www.php.net/manual/en/intro.runkit.php )将有必要实现一个更接近的解决方案,你在你的问题中提到你。 –

+0

我想用反射解决方案,但我似乎无法找到任何信息。看看API没有什么我可以使用的 – Thomas

7

有没有玩过整个事情。似乎只有你可以用ReflectionClass做的事情是替换现有的方法。但即使如此也是间接的。

我实际上不知道任何基于类的语言,其中存在动态类(然后再次,我的知识是相当有限的)。我只看到它在基于原型的语言(javascript,ruby,smalltalk)中完成。相反,你可以做什么,在PHP 5.4,是使用Closure并添加新的方法,现有对象

这是一类将让你执行这样变态到任何对象:

class Container 
{ 
    protected $target; 
    protected $className; 
    protected $methods = []; 

    public function __construct($target) 
    { 
     $this->target = $target; 
    } 

    public function attach($name, $method) 
    { 
     if (!$this->className) 
     { 
      $this->className = get_class($this->target); 
     } 
     $binded = Closure::bind($method, $this->target, $this->className); 
     $this->methods[$name] = $binded; 
    } 

    public function __call($name, $arguments) 
    { 
     if (array_key_exists($name, $this->methods)) 
     { 
      return call_user_func_array($this->methods[$name] , $arguments); 
     } 

     if (method_exists($this->target, $name)) 
     { 
      return call_user_func_array( 
       array($this->target, $name), 
       $arguments 
       ); 
     } 
    } 
} 

要利用这一点,你必须提供的构造与现有对象。这里是一个小例子的用法:

class Foo 
{ 
    private $bar = 'payload'; 
}; 
$foobar = new Foo; 
// you initial object 


$instance = new Container($foobar); 
$func = function ($param) 
{ 
    return 'Get ' . $this->bar . ' and ' . $param; 
}; 
$instance->attach('test', $func); 
// setting up the whole thing 


echo $instance->test('lorem ipsum'); 
// 'Get payload and lorem ipsum' 

不完全是你想要的,但AFAIK这是尽可能接近你可以得到。

3

,这是可能与runkit扩展的runkit_method_add()。虽然在生产中要小心使用它。

例子:

<?php 
class Example {} 

$e = new Example(); 

runkit_method_add(
    'Example', 
    'add', 
    '$num1, $num2', 
    'return $num1 + $num2;', 
    RUNKIT_ACC_PUBLIC 
); 

echo $e->add(12, 4);