web-dev-qa-db-ja.com

PHPでのクロージャー...具体的には何ですか。いつ使用する必要がありますか?

だから私は、ニースの最新のオブジェクト指向の方法でプログラミングをしています。私は定期的にOOP that PHPが実装されている)のさまざまな側面を利用していますが、いつクロージャーを使用する必要があるのか​​疑問に思っています。クロージャを実装することがいつ役立つかについていくつかのライト?

78
rg88

PHPは、5.3でネイティブにクロージャーをサポートします。クロージャーは、小さな特定の目的でのみ使用されるローカル関数が必要な場合に適しています。 クロージャのRFC は良い例です:

_function replace_spaces ($text) {
    $replacement = function ($matches) {
        return str_replace ($matches[1], ' ', ' ').' ';
    };
    return preg_replace_callback ('/( +) /', $replacement, $text);
}
_

これにより、replacement関数をreplace_spaces()内でローカルに定義できるため、次のようになりません。
1)グローバル名前空間を混乱させる
2)他の1つの関数の内部でのみ使用される関数がグローバルに定義されているのはなぜかを3年後に説明する

整理された状態を保ちます。関数自体に名前がないことに注意してください。単に定義され、_$replacement_への参照として割り当てられます。

しかし、覚えておいてくださいPHP 5.3 :)

キーワードuseを使用して、スコープ外の変数にクロージャにアクセスすることもできます。この例を考えてみましょう。

_// Set a multiplier  
 $multiplier = 3;

// Create a list of numbers  
 $numbers = array(1,2,3,4);

// Use array_walk to iterate  
 // through the list and multiply  
 array_walk($numbers, function($number) use($multiplier){  
 echo $number * $multiplier;  
 }); 
_

ここに優れた説明があります phpのラムダとクロージャとは

75
dirtside

あなたが今決めたタスクを実行する機能が将来必要になるとき。

たとえば、設定ファイルを読み取り、パラメータの1つで、アルゴリズムの_hash_method_がmultiplyではなくsquareであると通知された場合、次のようなクロージャを作成できます。あなたが何かをハッシュする必要があるところはどこでも使用されます。

クロージャは(たとえば)config_parser()で作成できます。 do_hash_method()にローカルな変数を使用して、config_parser()という関数を作成します(構成ファイルから)。 do_hash_method()が呼び出されると、そのスコープで呼び出されていなくても、ローカルスコープofconfig_parser()の変数にアクセスできます。

うまくいけば良い架空の例:

_function config_parser()
{
    // Do some code here
    // $hash_method is in config_parser() local scope
    $hash_method = 'multiply';

    if ($hashing_enabled)
    {
        function do_hash_method($var)
        {
            // $hash_method is from the parent's local scope
            if ($hash_method == 'multiply')
                return $var * $var;
            else
                return $var ^ $var;
        }
    }
}


function hashme($val)
{
    // do_hash_method still knows about $hash_method
    // even though it's not in the local scope anymore
    $val = do_hash_method($val)
}
_
16
Dan Udey

技術的な詳細は別として、クロージャは、関数指向プログラミングとして知られるプログラミングスタイルの基本的な前提条件です。クロージャは、オブジェクト指向プログラミングでオブジェクトを使用するのとほぼ同じ目的で使用されます。データ(変数)をいくつかのコード(関数)とバインドし、他の場所に渡すことができます。そのため、プログラムの記述方法に影響を与えるか、プログラムの記述方法を変更しない場合は、まったく影響がありません。

PHPは、クラスベースのオブジェクト指向パラダイムと古い手続き型パラダイムに既に重くなっているため、PHPのコンテキストでは少し奇妙です。通常、クロージャを使用する言語は、完全な字句スコープを持っています。後方互換性を維持するために、PHPはこれを取得しません。つまり、ここでのクロージャーは他の言語とは少し異なります。まだそれらがどのように使用されるかを正確に確認していません。

15
troelskn

Troelsknの投稿が提供するコンテキストが好きです。 PHPでDan Udeyの例のようなことをしたいときは、OO戦略パターンを使用します。私の意見では、これは実行時に動作が決定される新しいグローバル関数を導入するよりもはるかに優れています。

http://en.wikipedia.org/wiki/Strategy_pattern

PHPでメソッド名を保持する変数を使用して関数やメソッドを呼び出すこともできます。これはすばらしいことです。ダンの例をもう一度取り上げると、次のようになります。

class ConfigurableEncoder{
        private $algorithm = 'multiply';  //default is multiply

        public function encode($x){
                return call_user_func(array($this,$this->algorithm),$x);
        }

        public function multiply($x){
                return $x * 5;
        }

        public function add($x){
                return $x + 5;
        }

        public function setAlgorithm($algName){
                switch(strtolower($algName)){
                        case 'add':
                                $this->algorithm = 'add';
                                break;
                        case 'multiply':        //fall through
                        default:                //default is multiply
                                $this->algorithm = 'multiply';
                                break;
                }
        }
}

$raw = 5;
$encoder = new ConfigurableEncoder();                           // set to multiply
echo "raw: $raw\n";                                             // 5
echo "multiply: " . $encoder->encode($raw) . "\n";              // 25
$encoder->setAlgorithm('add');
echo "add: " . $encoder->encode($raw) . "\n";                   // 10

もちろん、どこでも利用できるようにしたい場合は、すべてを静的にすることができます...

10
grossvogel

クロージャは基本的に、あるコンテキストで定義を記述し、別のコンテキストで実行する関数です。 JavaScriptは至る所でJavaScriptで使用されているため、Javascriptはこれらを理解するのに大いに役立ちました。

PHPでは、関数内からの「グローバル」(または「外部」)変数のスコープとアクセス可能性が異なるため、JavaScriptほど効果的ではありません。ただし、PHP 5.4以降、クロージャはオブジェクト内で実行されると$ thisオブジェクトにアクセスできます。これにより、クロージャがより効果的になります。

これがクロージャの目的であり、上に書かれていることを理解するのに十分なはずです。

つまり、関数定義をどこかに記述し、関数定義内で$ this変数を使用して、関数定義を変数に割り当て(他の人は構文の例を示しています)、この変数をオブジェクトに渡すことができるはずです。そして、オブジェクトコンテキストでそれを呼び出すと、関数は$ thisを介してオブジェクトにアクセスして操作できます。実際には、そのオブジェクトのクラス定義ではなく、どこか他の場所で定義されている場合と同様です。

あまりはっきりしていなくても心配はいりません。使い始めるとはっきりします。

2
Rolf

基本的に、Closureは、外部変数にアクセスできる内部関数であり、匿名関数(名前のない関数)へのコールバック関数として使用されます。

 <?php
      $param='ironman';
      function sayhello(){
          $param='captain';
          $func=function () use ($param){
                $param='spiderman';
          };
       $func();
       echo  $param;
       }
      sayhello();
?>

//output captain

//and if we pass variable as a reference as(&$param) then output would be spider man;
1
Harsh Gehlot

phpでのクロージャーの例を示します

// Author: [email protected]
// Publish on: 2017-08-28

class users
{
    private $users = null;
    private $i = 5;

    function __construct(){
        // Get users from database
        $this->users = array('a', 'b', 'c', 'd', 'e', 'f');
    }

    function displayUsers($callback){
        for($n=0; $n<=$this->i; $n++){
            echo  $callback($this->users[$n], $n);
        }
    }

    function showUsers($callback){
        return $callback($this->users);

    }

    function getUserByID($id, $callback){
        $user = isset($this->users[$id]) ? $this->users[$id] : null;
        return $callback($user);
    }

}

$u = new users();

$u->displayUsers(function($username, $userID){
    echo "$userID -> $username<br>";
});

$u->showUsers(function($users){
    foreach($users as $user){
        echo strtoupper($user).' ';
    }

});

$x = $u->getUserByID(2, function($user){

    return "<h1>$user</h1>";
});

echo ($x);

出力:

0 -> a
1 -> b
2 -> c
3 -> d
4 -> e
5 -> f

A B C D E F 

c
0
Hisham Dalal