匿名関数をパラメーターとして使用して外部変数にアクセスする


93

基本的に私はこの便利な関数を使用してdb行を処理します(PDOやその他のものに目を近づけます)

function fetch($query,$func) {
    $query = mysql_query($query);   
    while($r = mysql_fetch_assoc($query)) {
        $func($r);
    }
}

この関数を使用すると、簡単に次のことができます。

fetch("SELECT title FROM tbl", function($r){
   //> $r['title'] contains the title
});

今、私はすべてを連結する必要があるとしましょう $r['title']、(これは単なる例です)。

どうすればできますか?私はこのようなことを考えていましたが、あまりエレガントではありません:

$result = '';
fetch("SELECT title FROM tbl", function($r){
   global $result;
   $result .= $r['title'];
});

echo $result;

回答:


188

あなたは使用する必要がありますuseとしてのドキュメントで説明します

クロージャーは、親スコープから変数を継承する場合もあります。このような変数はすべて関数ヘッダーで宣言する必要があります。親スコープから変数を継承することは、グローバル変数を使用することと同じではありません。グローバル変数はグローバルスコープに存在します。これは、どの関数が実行されていても同じです。

コード:

$result = '';
fetch("SELECT title FROM tbl", function($r) use (&$result) {
   $result .= $r['title'];
});

ただし、注意してください(前のリンクのコメントの1つから取得):

use()パラメータはアーリーバインディングです。ラムダ関数が呼び出される場所(レイトバインディング)ではなく、ラムダ関数が宣言される場所で変数の値を使用します。


1
そのグローバルな減速は取り除かれるべきではないでしょうか?
aziz punjani

19
+1を強調しearly bindingます。しかし、上記の例でuse (&$result)は、参照によって渡されたとき、それは本当に問題ではないと思いますか?
Dimitry K

4
@DimitryKはい、ここで参照を使用して、デフォルトの動作(早期バインディング)をバイパスします。
Xaerxess、2014年

3
@machineaddict基本use アーリーバインディングです。レイトバインディングの回避策を意味する場合は、変数をuse参照渡しします。匿名関数(またはそれを呼び出すもの)を呼び出す前に、&=> use (&$result)を使用して$result変数を変更します
jave.web

1
クラスインスタンスは常に参照によって渡されるため、&を使用する必要はありません。(インスタンスを完全に上書きしない限り)。
Joel Harkes

0

$ fetchを1回だけ呼び出すように 'fetch'を書き換えるのはどうですか?

function fetch($query,$func) {
    $query = mysql_query($query);   
    $retVal = array();
    while($r = mysql_fetch_assoc($query)) {
        $retVal[] = $r;
    }
    $func($retVal);
}

この方法では、$ funcを1回だけ呼び出し、フェッチされた配列を再処理しますか?関数を200回呼び出したとしても、パフォーマンスについてはよくわかりません。


はい、そうです。ただし、あちこちで数ミリ秒を取得することに興味がある場合は、mysql_fetch_assoc()の代わりにmysql_fetch_row()を使用できます。列の位置を知っている必要があるため、対処するのが非常に困難です。そうすることで、30行ずつの2000リクエストで0.205から0.180に渡されます。
user103307
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.