2009-06-30 7 views
317

私はいくつかのPHP 5.3.0の機能をチェックアウトし、かなりおかしいのサイトにいくつかのコード全体に走っている:anonymous functions上の例のひとつとしてPHP 5.3.0では、関数 "use"識別子は何ですか?

public function getTotal($tax) 
{ 
    $total = 0.00; 

    $callback = 
     /* This line here: */ 
     function ($quantity, $product) use ($tax, &$total) 
     { 
      $pricePerItem = constant(__CLASS__ . "::PRICE_" . 
       strtoupper($product)); 
      $total += ($pricePerItem * $quantity) * ($tax + 1.0); 
     }; 

    array_walk($this->products, $callback); 
    return round($total, 2); 
} 

誰でもこれについて知っていますか?どんなドキュメンテーションですか?そしてそれはそれが使用されるべきであれば悪く見える?

答えて

282

これは、PHPがclosureを表す方法です。これはまったく悪ではなく、実際には非常に強力で有用です。

基本的にこれが何を意味するかは、あなたがそれの範囲外のローカル(この場合は、$tax$totalを参照)の変数を「キャプチャ」とその値(または$totalの場合を維持するために匿名関数を許可しているということです$total自体を参照してください)を匿名関数自体の状態として返します。

+1

これはクロージャーにのみ使用されますか? 説明をありがとう、無名関数とクロージャの違いをわかりませんでした – SeanDowney

+108

'use'キーワードは、[aliasing namespaces](http://php.net/manual/en/language.namespaces.importing .php)。 PHP 5.3.0のリリースから3年以上経過しても、 'function ... use'という構文は公式には文書化されていないので、クロージャは文書化されていない機能になっています。文書でさえ、[匿名関数とクロージャを混乱させる](http://php.net/manual/en/functions.anonymous.php)。私がphp.netで見つけた 'use()'の唯一の(ベータと非公式の)ドキュメントは、[RFC for closures](https://wiki.php.net/rfc/closures)でした。 –

+1

So [関数使用クロージャがPHPで実装されたのはいつですか](http://stackoverflow.com/questions/20411631/when-was-function-closures-implemented-in-php)PHP 5.3 ?それは今何とかPHPマニュアルに書かれていますか? – rubo77

47

閉鎖は美しいです!彼らは匿名の関数に付随する多くの問題を解決し、(少なくとも私たちがPHPについて話している限り)本当にエレガントなコードを可能にします。

javascriptプログラマーは、バインドされた変数が明示的に定義されていないために、時にはそれを知らなくても常にクローズを使用します。

上記のものよりも実際の例があります。サブディメンションで多次元配列をソートしなければならないと言っても、キーは変わります。

<?php 
    function generateComparisonFunctionForKey($key) { 
     return function ($left, $right) use ($key) { 
      if ($left[$key] == $right[$key]) 
       return 0; 
      else 
       return ($left[$key] < $right[$key]) ? -1 : 1; 
     }; 
    } 

    $myArray = array(
     array('name' => 'Alex', 'age' => 70), 
     array('name' => 'Enrico', 'age' => 25) 
    ); 

    $sortByName = generateComparisonFunctionForKey('name'); 
    $sortByAge = generateComparisonFunctionForKey('age'); 

    usort($myArray, $sortByName); 

    usort($myArray, $sortByAge); 
?> 

警告:未テストコードは(私はPHP5.3インストール気圧を持っていない)が、それはそのようなことのようになります。

そこには1つの欠点があります。多くのPHPデベロッパーは、クロージャで対処すれば、少し力がないかもしれません。

閉鎖のnice-tyをもっと理解するために、私はあなたに別の例を与えます - 今度はjavascriptで。問題の1つはスコープとブラウザー固有の非同期です。特にwindow.setTimeout();(または-interval)の場合は特にそうです。したがって、setTimeoutに関数を渡しますが、パラメータを指定するとコードが実行されるため、実際にパラメータを与えることはできません!

function getFunctionTextInASecond(value) { 
    return function() { 
     document.getElementsByName('body')[0].innerHTML = value; // "value" is the bound variable! 
    } 
} 

var textToDisplay = prompt('text to show in a second', 'foo bar'); 

// this returns a function that sets the bodys innerHTML to the prompted value 
var myFunction = getFunctionTextInASecond(textToDisplay); 

window.setTimeout(myFunction, 1000); 

myFunctionは、種類が事前定義されたパラメータを持つ関数を返します。

正直言って、私は5.3より多くのPHPと匿名の関数/クロージャが好きです。名前空間はもっと重要かもしれませんが、ですが、セクシーではありません。

+4

ohhhhhhhhを使用するので、Usesは* extra *変数を渡すために使用されています。ありがとう! – SeanDowney

+29

注意してください。関数が呼び出されたときにパラメータを渡します。関数が定義されている場合、クロージャは値を「渡す」ために使用されます。 – stefs

+0

Javascriptでは、関数の初期引数を指定するために[bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)を使用することができます。部分的に適用される関数](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Partially_applied_functions)を参照してください。 –

374

より簡単な答えです。

function ($quantity) use ($tax, &$total) { .. };

  1. クロージャは、変数に割り当てられた機能なので、あなたはクロージャが別の名前空間で周り
  2. それを渡すことができ、通常、あなたは、この名前空間の外で定義された変数にアクセスすることはできません。は、あなたが(使用)にアクセスすることができます
  3. 使用クロージャ内の後続の変数:使用キーワードが存在しています。
  4. は初期バインディングです。つまり、変数値はクロージャを定義するとCOPIEDされます。したがって、オブジェクトのようにポインタでない限り、クロージャ内の$taxを変更することは外部的な効果はありません。
  5. &$totalのようにポインタとして変数を渡すことができます。このように、$totalの値を変更すると外部効果があり、元の変数の値が変更されます。
  6. クロージャ内で定義された変数には、クロージャの外側からもアクセスできません。
  7. クロージャと関数のスピードは同じです。はい、スクリプト全体で使用できます。

@Mytskine pointed outとして、おそらく最も詳細な説明はRFC for closuresです。

+14

ありがとう、最高の答えはあなたです! – PHPst

+17

あなたは早い綴りを言及していいですか? – usoban

+3

useステートメントのasキーワードは私にPHP 5.5で構文エラーを与えています: '$ closure = function($ value)use($ localVar as $ alias){// stuff};' エラーがあります: 'Parse:構文エラー、予期しない '(T_AS)、expecting'、 'or') '' –

11

Zupaは、 '使用'のクロージャと、 '使用済み'の変数のEarlyBindingとReferencingとの違いを説明してくれました。

だから私は、初期の変数の結合(=コピー)とのコード例を作った:

<?php 

$a = 1; 
$b = 2; 

$closureExampleEarlyBinding = function() use ($a, $b){ 
    $a++; 
    $b++; 
    echo "Inside \$closureExampleEarlyBinding() \$a = ".$a."<br />"; 
    echo "Inside \$closureExampleEarlyBinding() \$b = ".$b."<br />";  
}; 

echo "Before executing \$closureExampleEarlyBinding() \$a = ".$a."<br />"; 
echo "Before executing \$closureExampleEarlyBinding() \$b = ".$b."<br />"; 

$closureExampleEarlyBinding(); 

echo "After executing \$closureExampleEarlyBinding() \$a = ".$a."<br />"; 
echo "After executing \$closureExampleEarlyBinding() \$b = ".$b."<br />"; 

/* this will output: 
Before executing $closureExampleEarlyBinding() $a = 1 
Before executing $closureExampleEarlyBinding() $b = 2 
Inside $closureExampleEarlyBinding() $a = 2 
Inside $closureExampleEarlyBinding() $b = 3 
After executing $closureExampleEarlyBinding() $a = 1 
After executing $closureExampleEarlyBinding() $b = 2 
*/ 

?> 

例変数を参照すると(変数の前に「&」文字に注目してください)。

<?php 

$a = 1; 
$b = 2; 

$closureExampleReferencing = function() use (&$a, &$b){ 
    $a++; 
    $b++; 
    echo "Inside \$closureExampleReferencing() \$a = ".$a."<br />"; 
    echo "Inside \$closureExampleReferencing() \$b = ".$b."<br />"; 
}; 

echo "Before executing \$closureExampleReferencing() \$a = ".$a."<br />"; 
echo "Before executing \$closureExampleReferencing() \$b = ".$b."<br />"; 

$closureExampleReferencing(); 

echo "After executing \$closureExampleReferencing() \$a = ".$a."<br />"; 
echo "After executing \$closureExampleReferencing() \$b = ".$b."<br />";  

/* this will output: 
Before executing $closureExampleReferencing() $a = 1 
Before executing $closureExampleReferencing() $b = 2 
Inside $closureExampleReferencing() $a = 2 
Inside $closureExampleReferencing() $b = 3 
After executing $closureExampleReferencing() $a = 2 
After executing $closureExampleReferencing() $b = 3 
*/ 

?> 
30

function() use() {}は、PHPのため閉鎖され、あなたは親functionの変数を含めることuseを使用する必要があります。

<?php 
$message = "hello\n"; 


$example = function() { 
    echo $message; 
}; 
// Notice: Undefined variable: message 
$example(); 


$example = function() use ($message) { 
    echo $message; 
}; 
// "hello" 
$example(); 


// Inherited variable's value is from when the function is defined, not when called 
$message = "world\n"; 
// "hello" 
$example(); 


// Inherit by-reference 
$message = "hello\n"; 
$example = function() use (&$message) { 
    echo $message; 
}; 
// "hello" 
$example(); 
// The changed value in the parent scope is reflected inside the function call 
$message = "world\n"; 
// "world" 
$example(); 


// Closures can also accept regular arguments 
$example = function ($arg) use ($message) { 
    echo $arg . ' ' . $message; 
}; 
// "hello world" 
$example("hello"); 
+1

はるかに簡単な説明をありがとうございます。 –

-1

$tax$totalの範囲は機能getTotal()範囲内です。コールバック関数を呼び出しています。したがって、useに電話する必要はありません。

+2

実際、スコープはコールバック内で異なります - デモンストレーション[here](https://www.tehplayground.com/KyxlnmC2C8BCuva4)を参照してください。おそらくあなたはJavaScriptのような言語を考えています。匿名関数が親スコープ内の変数にアクセスすることができます。 –

+0

BTW - これは本当に質問に答えないので、答えではないと私は主張しました。どんなドキュメンテーションですか?それは悪く見えますか? –

関連する問題