2012-06-11 141 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);