2012-06-11 5 views
23

実行時にPHPで新しいメソッドをクラスに追加する方法があるのだろうかと思います。 私は、インスタンスレベルではなく、クラスに直接関係するので、新しく作成されたすべてのインスタンスにこの新しいメソッドがあることを意味します。 そのようなことは、反射で行うことができますか?実行時にphp create classメソッド

おかげ

答えて

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方法のポイントは何ですか好きですか? \口元 '@jayarjo :: bind' – jayarjo

+2

は匿名関数(この場合には、それはクロージャ内、' $ this'が正しいオブジェクトを参照する、ことを確認します)のコンテキストを変更します。しかし、その関数を '$ this'のメソッドにするわけではありません。我々はまだそれが法であるかのように外部のコードはクロージャを呼び出すことができるようにするために、 '__call'を必要としています。 –

3

あなたがドキュメントにcreate_function()を見て撮影したことがありますか?また、希望結果をoverloadingで達成することもできます。

+0

する。create_functionは(FASとして、私が知っているように)グローバルコンテキスト内の関数を作成し、私は理解 – Thomas

+0

することに希望通りのオーバーロードは、クリーンなソリューションではありません。しかし、このシナリオで理想的なPHPは、動的な言語ではありません。私は[Reflection](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); 
関連する問題