PHPのクロージャ…正確には、それらは何であり、いつ使用する必要がありますか?


82

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

回答:


82

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) 3年後に人々を驚かせるには、他の1つの関数内でのみ使用されるグローバルに定義された関数があるのはなぜか

それは物事を整理し続けます。関数自体に名前がないことに注意してください。関数は、への参照として定義および割り当てられているだけです$replacement

ただし、PHP5.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ラムダとクロージャとは何ですか


1
これは素晴らしい説明です。+1
David J Eddy

4
なぜクロージャーを使うのという説明を楽しんだ。ほとんどの人はそれを本当に理解していません。+1
キャリーケンダル

10
これは無名関数の説明であり、クロージャの説明ではありません。匿名関数は、あなたが言うように、グローバルではないことを除いて、名前付き関数と同じです。一方、クロージャは、字句スコープの自由変数(「use」で宣言)を含む関数です。すなわち。他のすべてがガベージコレクションされた後でも、宣言されているスコープから値をコピーして参照できます。
Warbo 2014年

@Warboこれは本当です。当時、私は無名関数とクロージャの違いを実際には理解していませんでした。クロージャは、匿名関数を取得して初めて意味がありますが、今日でも、クロージャとは何か(7年前の私のように;-))の「説明」があり、その範囲の側面を説明していません。
ダートサイド2015年

そのため、クロージャが頻繁に使用されるJavaScriptを確認してください。ただし、PHPでは可変スコープのルールが異なることに注意してください。
ロルフ

17

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

たとえば、構成ファイルを読み取り、パラメーターの1つが、hash_methodアルゴリズムのがmultiplyではなくでsquareあることを示している場合、何かをハッシュする必要がある場所で使用されるクロージャーを作成できます。

クロージャーは(たとえば)で作成できますconfig_parser()。(構成ファイルから)do_hash_method()ローカル変数を使用して呼び出される関数を作成しconfig_parser()ます。たびにdo_hash_method()呼ばれ、それはのローカルスコープ内の変数にアクセスすることができconfig_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)
}

この例を単純にコピーして貼り付けて実行することはできません。簡単に実行できる例をお勧めします。
キムスタック2013年

3
この答えは貧弱です。これは意味のないステートメントです。「将来、あなたが今決めたタスクを実行する機能が必要になるとき」。
キプロスでリラックス2014年

15

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

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


10

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

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

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


2

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

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

これがクロージャの目的であり、上記の内容を理解するだけで十分です。

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

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


正直なところ、著者である私にとってさえ、これはまったく明確ではありません。基本的に私が言っているのは、JavaScriptでどのクロージャがチェックアウトされているかを知るためですが、JavaScriptとPHPでは変数のスコープが異なることに注意してください。
ロルフ

1

基本的に、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;

$param='captain'funcsayhello()は、funcのローカル変数ですsayhello()$param='ironman'上記sayhello()はグローバル変数です。あなたのスクリプトで唯一の$のparam変数を作成したい場合は、呼び出す必要があります:global $param;sayhello()FUNC
vlakov

0

これがphpのクロージャの例です

// Author: HishamDalal@gamil.com
// 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

閉鎖:

MDNにはIMOの最も良い説明があります。

クロージャは、一緒にバンドルされた(囲まれた)関数と、その周囲の状態(字句環境)への参照の組み合わせです。言い換えると、クロージャを使用すると、内部関数から外部関数のスコープにアクセスできます。

つまり、クロージャは、親スコープにある変数にアクセスできる関数です。クロージャを使用すると、関数が1か所でのみ必要になる場合があるため(コールバック、呼び出し可能な引数)、その場で関数を簡単に作成できます。

例:

$arr = [1,2,3,3];
$outersScopeNr = 2;

// The second arg in array_filter is a closure
// It would be inconvenient to have this function in global namespace
// The use keyword lets us access a variable in an outer scope
$newArr = array_filter($arr, function ($el) use ($outersScopeNr) {
    return $el === 3 || $el === $outersScopeNr;
});

var_dump($newArr);
// array (size=3)
//  1 => int 2
//  2 => int 3
//  3 => int 3
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.