2008-09-08 23 views

答えて

157

マニュアルは同義的に「コールバック」と「呼び出し可能」という用語を使用しています、しかし、「コールバック」、伝統的に将来の呼び出しのために関数またはクラスメソッドを参照する、function pointerのように動作する文字列または配列の値を参照します。

$cb1 = 'someGlobalFunction'; 
$cb2 = ['ClassName', 'someStaticMethod']; 
$cb3 = [$object, 'somePublicMethod']; 

// this syntax is callable since PHP 5.2.3 but a string containing it 
// cannot be called directly 
$cb2 = 'ClassName::someStaticMethod'; 
$cb2(); // fatal error 

// legacy syntax for PHP 4 
$cb3 = array(&$object, 'somePublicMethod'); 

これは、一般的に呼び出し可能な値を使用する安全な方法です:

if (is_callable($cb2)) { 
    // Autoloading will be invoked to load the class "ClassName" if it's not 
    // yet defined, and PHP will check that the class has a method 
    // "someStaticMethod". Note that is_callable() will NOT verify that the 
    // method can safely be executed in static context. 

    $returnValue = call_user_func($cb2, $arg1, $arg2); 
} 

現代のPHPのバージョンは、最初の3つのフォーマットを許可これは味があるPHP 4以降、関数型プログラミングのいくつかの要素を許可しています上記のように直接呼び出されるのは$cb()です。 call_user_funcおよびcall_user_func_arrayは、上記をすべてサポートしています。

参照:http://php.net/manual/en/language.types.callable.php

注意/警告:

  1. 機能/クラスが名前空間されている場合は、文字列が完全修飾名が含まれている必要があります。例えば。 ['Vendor\Package\Foo', 'method']
  2. call_user_funcは非オブジェクトを参照渡しをサポートしていないため、call_user_func_arrayを使用するか、それ以降のPHPバージョンではコールバックをvarに保存して直接構文を使用します。$cb();
  3. __invoke()メソッド(匿名関数を含む)を持つオブジェクトは、「呼び出し可能」カテゴリに属し、同じ方法で使用できますが、私は個人的にこれを従来の「コールバック」用語に関連付けません。
  4. レガシーcreate_function()はグローバル関数を作成し、その名前を返します。 eval()のラッパーで、代わりに匿名関数を使用する必要があります。
+1

これは、OPの受け入れられた自己回答よりもはるかに優れています。 – chaos

+0

実際、関数を使用するのは適切な方法です。変数を使用して呼び出している間、受け入れられた答えに示唆されているように、クールですが、それは醜いですし、コードではうまくスケールされません。 – icco

+3

受け入れられた回答が変更されました。コメントに同意し、これは素晴らしい答えです。 –

26

実装はそう

// This function uses a callback function. 
function doIt($callback) 
{ 
    $data = "this is my data"; 
    $callback($data); 
} 


// This is a sample callback function for doIt(). 
function myCallback($data) 
{ 
    print 'Data is: ' . $data . "\n"; 
} 


// Call doIt() and pass our sample callback function's name. 
doIt('myCallback'); 

表示のように行われます。データは次のとおりです。これは私のデータである

+16

ああ私の神。それは標準ですか?それはひどい! –

+0

上記のようにいくつかの方法があります。私は本当に同じと思った。 –

+0

うわー、これは従来のプログラミングにいくつかの新しい紆余曲折を追加します。 –

9

私が最近見つけた一つの気の利いたトリックが匿名を作成するためにPHPのcreate_function()を使用することです/ lambda関数を使用してワンショットで使用できます。 array_map()preg_replace_callback()、またはusort()のようなPHP関数で、カスタム処理にコールバックを使用する場合に便利です。カバーの下にあるeval()のように見えますが、PHPを使用するにはまだ素晴らしい機能スタイルの方法です。

+6

残念ながら、ガベージコレクタはこの構造体ではあまりうまく動かず、メモリリークの可能性があります。 パフォーマンスが不足している場合は、create_function()を使用しないでください。 – fuxia

+1

ヘッドアップをありがとう。 – yukondude

3

私がcreate_function()をPHPで使用するたびに私はうんざりします。

パラメータは、昏睡区切りの文字列で、文字列内の関数本体全体... Argh ...私は彼らが試しても醜いことはできなかったと思います。

名前を付けられた関数を作成することが問題になるわけではありませんが、残念ながらそれは唯一の選択肢です。 5.3で、我々は彼らと無名関数

http://wiki.php.net/rfc/closures

+0

そしてもちろん、ランタイム文字列evalです。したがって、コンパイル時に有効な構文やその他のものがないかどうかチェックされません。 – hobbs

7

はよく...地平線上の5.3で、すべてはあなたがどんなあなたの呼び出しを検証したいと思うでしょう、良くなります有効です。例えば、関数が存在する場合、特定の機能の場合には、あなたがチェックして見たいと思ってます。

function doIt($callback) { 
    if(function_exists($callback)) { 
     $callback(); 
    } else { 
     // some error handling 
    } 
} 
7

をクロージャを取得しますので

+0

コールバックが関数ではなく、オブジェクトとメソッドを保持する配列の場合はどうなりますか? –

+0

またはむしろ 'is_callable($ callback)' –

+0

どちらも良い提案です - コールバックのやり方の独自の実装を確認する必要があります。私は致命的な原因で存在しなかったものを呼び出すことに対して警告することを望んでいました。私は答えをもっと一般的にしました – SeanDowney

4

create_functionはクラス内では機能しませんでした。私はcall_user_funcを使用しなければならなかった。

その後
<?php 

class Dispatcher { 
    //Added explicit callback declaration. 
    var $callback; 

    public function Dispatcher($callback){ 
     $this->callback = $callback; 
    } 

    public function asynchronous_method(){ 
     //do asynch stuff, like fwrite...then, fire callback. 
     if (isset($this->callback)) { 
      if (function_exists($this->callback)) call_user_func($this->callback, "File done!"); 
     } 
    } 

} 

、使用する:

<?php 
include_once('Dispatcher.php'); 
$d = new Dispatcher('do_callback'); 
$d->asynchronous_method(); 

function do_callback($data){ 
    print 'Data is: ' . $data . "\n"; 
} 
?> 

を[編集] が不足している括弧を追加しました。 また、コールバック宣言を追加しました。私はそれを好む方法です。

+1

Dispatcherクラスで$ this-> callback = $ callbackが動作するための属性が必要ですか? –

+0

@james poulson:PHPは動的言語なので動作します。しかし、私は怠け者でした。私は通常、プロパティを宣言し、すべての人生を楽にします。あなたの質問で、私はそのコードをもう一度見て、構文エラーを見つけました。ありがとう – goliatone

+0

私はこれが可能であることを知らなかった。答えをありがとう:)。 –

59

PHP 5.3で、あなたは今、これを行うことができます。それを行うには

function doIt($callback) { $callback(); } 

doIt(function() { 
    // this will be done 
}); 

最後に良い方法を。コールバックが素晴らしいので、PHPの素晴らしい追加。

+4

かなりきれいです – Ulterior

1

PHP < 5.4との互換性を壊さないよう気にする人は、タイプヒントを使用してよりクリーンな実装をお勧めします。

function call_with_hello_and_append_world(callable $callback) 
{ 
    // No need to check $closure because of the type hint 
    return $callback("hello")."world"; 
} 

function append_space($string) 
{ 
    return $string." "; 
} 

$output1 = call_with_hello_and_append_world(function($string) { return $string." "; }); 
var_dump($output1); // string(11) "hello world" 

$output2 = call_with_hello_and_append_world("append_space"); 
var_dump($output2); // string(11) "hello world" 

$old_lambda = create_function('$string', 'return $string." ";'); 
$output3 = call_with_hello_and_append_world($old_lambda); 
var_dump($output3); // string(11) "hello world" 
関連する問題