2010-12-26 7 views
81

クロージャを変数に再割り当てしてから呼び出すことなく、オブジェクトのプロパティに直接割り当てるクロージャを呼び出すことができます。これは可能ですか?オブジェクトプロパティに割り当てられたクロージャを直接呼び出す

以下のコードは動作せず、Fatal error: Call to undefined method stdClass::callback()が発生します。それがStdClass上では動作しませんが

$obj = new stdClass(); 
$obj->callback = function() { 
    print "HelloWorld!"; 
}; 
$obj->callback(); 
+1

これはまさにあなたが必要としているものです:https://github.com/ptrofimov/jslikeobjectさらに:$ thisをクロージャ内で使用し、継承を使用することができます。 PHP> = 5.4のみ! –

答えて

66

PHP7のとして、あなたは、

$obj = new StdClass; 
$obj->fn = function($arg) { return "Hello $arg"; }; 
echo ($obj->fn)('World'); 

または使用Closure::call()を行うことができます。

class Foo 
{ 
    public function __call($method, $args) 
    { 
     if(is_callable(array($this, $method))) { 
      return call_user_func_array($this->$method, $args); 
     } 
     // else throw exception 
    } 
} 

$foo = new Foo; 
$foo->cb = function($who) { return "Hello $who"; }; 
echo $foo->cb('World'); 
を(あなたが __callメソッドを追加することはできませんので、当然の StdClassことは不可能である)


はPHP7前に、コールバックを呼び出しをインターセプトし、呼び出すための魔法__callメソッドを実装する必要があると思いますこれがの引き金になるので、あなたが、__call体内の

return call_user_func_array(array($this, $method), $args); 

を行うことはできません

注意無限ループで。

+2

場合によっては、この構文が便利です - call_user_func_array($ this-> $ property、$ args); メソッドではなく、呼び出し可能なクラスプロパティに関する場合。 –

+0

PHP7用のこの構文は素晴らしいです!ありがとうございました – stamster

7

call_user_func()を使用している可能性があります。

call_user_func($obj->callback); 

でも、エレガントではありません.... @Gordonはおそらく唯一の方法だと言います。

7

よく、あなたが本当にを主張するならば。もう1つの回避策は次のとおりです。

$obj = new ArrayObject(array(),2); 

$obj->callback = function() { 
    print "HelloWorld!"; 
}; 

$obj['callback'](); 

しかし、これは一番良い構文ではありません。

ただし、PHPパーサは常にメソッドコールとしてT_OBJECT_OPERATOR,IDENTIFIER(を扱います。 ->をメソッドテーブルをバイパスし、代わりに属性にアクセスさせるための回避策はないようです。コールバックが配列である場合、または動作しませんもちろん

$obj = new stdClass(); 
$obj->callback = function() { 
    print "HelloWorld!"; 
}; 
$obj->callback->__invoke(); 

:それは関数のように振る舞うために使用するオブジェクトのマジックメソッドだから

95

あなたは、閉鎖に__invoke呼び出すことによってこれを行うことができます文字列(PHPで有効なコールバックになることもできます) - クローズや__invokeの動作を持つその他のオブジェクトの場合

+3

+1完璧な解決策!!! – marcio

+3

@marcioAlmada非常に醜いですが。 – Mahn

+1

@マーン私は受け入れられた答えよりも明白だと思います。この場合、明示的な方が良いです。あなたが本当にかわいい解決策を気にかけているなら、 'call_user_func($ obj-> callback)'はそれほど悪くはありません。 – marcio

2

よく、変数にクロージャを格納し、varibleを呼び出すことは実際には(呼び名が)速く、呼び出し量に応じて、xdebug(非常に正確な測定)で非常に多くなることを強調する必要があります。私たちは1,5について話しています(__invokeを直接呼び出すのではなく、変数を使用することによって、変数を閉じて変数を呼び出すだけです)。

0

PHP 5.4以上を使用している場合オブジェクトのスコープに呼び出し可能オブジェクトをバインドして、カスタム動作を呼び出すことができます。たとえば、次のような設定を行う場合などです。

function run_method($object, Closure $method) 
{ 
    $prop = uniqid(); 
    $object->$prop = \Closure::bind($method, $object, $object); 
    $object->$prop->__invoke(); 
    unset($object->$prop); 
} 

そして、あなたがそうのようなクラスで動作しました。..

class Foo 
{ 
    private $value; 
    public function getValue() 
    { 
     return $this->value; 
    } 
} 

あなたがオブジェクトの範囲内で動作しているかのようにあなたが独自のロジックを実行することができ

$foo = new Foo(); 
run_method($foo, function(){ 
    $this->value = 'something else'; 
}); 

echo $foo->getValue(); // prints "something else" 
0

受け入れられた答えに基づいていますが、stdClassを直接拡張する別の方法があります:

class stdClassExt extends stdClass { 
    public function __call($method, $args) 
    { 
     if (isset($this->$method)) { 
      $func = $this->$method; 
      return call_user_func_array($func, $args); 
     } 
    } 
} 

使用例:

$foo = new stdClassExt; 
$foo->blub = 42; 
$foo->whooho = function() { return 1; }; 
echo $foo->whooho(); 

あなたは、おそらくしかしcall_user_funcまたは__invokeを使用したほうが良いです。

3

オブジェクトプロパティをクロージャとして正常に呼び出す別の方法があります。
あなたは、このコアオブジェクトの使用を変更したくない場合は:

$obj = new AnyObject(); // with or without __invoke() method 
$obj->callback = function() { 
    return function() { 
      print "HelloWorld!"; 
    }; 
}; 
$obj->callback(); 

UPDATE:

$obj = new AnyObject(); // with or without __invoke() method 
$obj->callback = function() { 
    print "HelloWorld!"; 
}; 
$callback = $obj->callback; 
$callback(); 
+0

これを試しましたか?それは動作しません。 '未定義のメソッドを呼び出すAnyObject :: callback()'(クラスAnyObjectはもちろん存在します) – Kontrollfreak

+0

これは動作しません。それ以外の場合は簡単でしょう:D –

+0

更新されたコードの作業OK – Chris

5

私はこれが古いですけど、私はあなたがある場合形質はうまくこの問題を扱うと思いますPHP 5を使用します。4+

まず、プロパティを呼び出し可能になりトレイト作成:次に

trait CallableProperty { 
    public function __call($method, $args) { 
     if (property_exists($this, $method) && is_callable($this->$method)) { 
      return call_user_func_array($this->$method, $args); 
     } 
    } 
} 

を、あなたのクラスでその形質を使用することができます。

class CallableStdClass extends stdClass { 
    use CallableProperty; 
} 

を今、あなたは無名関数を経由してプロパティを定義することができます直接電話する:

$foo = new CallableStdClass(); 
$foo->add = function ($a, $b) { return $a + $b; }; 
$foo->add(2, 2); // 4 
+0

Wow :)これは私がやろうとしていたよりもはるかにエレガントです。私の唯一の質問は、英国のホット/コールドタップコネクタの要素と同じです:それは既に組み込まれていませんか? – dkellner

+0

ありがとう:)それはおそらくそれが作成するあいまいさのために組み込まれていません。私の例では、実際には「add」という関数と「add」というプロパティがあったとします。かっこの存在はPHPにその名前の関数を探すよう指示します。 – SteveK

13

PHP 7のうち、 llowing:閉鎖call()方法使用して呼び出すことができPHP 7ため

($obj->callback)(); 
5

:(Korikulumにより説明したように)PHP 7ため

$obj->callback->call($obj); 

があまりに任意(...)式に操作を実行することができます:

($obj->callback)(); 

他の一般的なPHP 5つのアプローチがある:

  • call_user_func()機能

    call_user_func($obj->callback); 
    
  • を用いて魔法(Brilliandにより説明したように)方法__invoke()

    $obj->callback->__invoke(); 
    
  • を使用して使い方グラム表現

    ($_ = $obj->callback) && $_(); 
    

それぞれの方法は、独自の長所と短所を持っていますが、最も過激と決定的な解決策はまだGordonによって提示された1つのままで中間変数

class stdKlass 
{ 
    public function __call($method, $arguments) 
    { 
     // is_callable([$this, $method]) 
     // returns always true when __call() is defined. 

     // is_callable($this->$method) 
     // triggers a "PHP Notice: Undefined property" in case of missing property. 

     if (isset($this->$method) && is_callable($this->$method)) { 
      return call_user_func($this->$method, ...$arguments); 
     } 

     // throw exception 
    } 
} 

$obj = new stdKlass(); 
$obj->callback = function() { print "HelloWorld!"; }; 
$obj->callback(); 
関連する問題