PHP - クロージャー

公開日:2020-12-22 更新日:2020-12-22
[PHP]

1. 概要

正しい定義をよくわかっていませんが、
外部の変数を参照している無名関数をクロージャーと呼びます。
(それとも、変数の値を保持する関数?)

通常、関数で定義した変数は、関数終了時に破棄されますが、
誰かが変数を参照している場合は、破棄されずに維持されます。
そこで、関数で使用する変数を無名関数で参照させて、
その無名関数を戻り値で返すと、変数の値を維持した無名関数を作成することができます。

2. 説明

PHP で無名関数から外部の変数の値を使う場合、
使う変数を use() で指定する必要があります。
もし use() で指定しない場合は、外部の変数とは別の変数として扱われます。

次の例から「use ($value)」をはずすと、結果が NULL になります。
$value = 5;
$f = function() use ($value) {
    return $value;
};
var_dump( $f() );
実行結果
int(5)

use により、無名関数の中で外部の変数の値が使えるようになりましたが、
これは、外部の変数を直接参照している訳ではなく、コピーされた値を使用しています。

例えば次の例では、use() を使って外部の変数 $i を使っていますが、
for の ループ変数 $i と、無名関数の中の $i とは、別の変数と言う扱いです。
実行結果を見ると、$i は 0, 1, 2 となるため、期待通りの結果のように見えますが、
これは、無名関数がループ変数 $i を直接参照していないことを意味します。
無名関数の実行時には、ループ変数 $i は、ループが終了して、3 となっているため、
もし直接参照していた場合は、結果は全て 3 になります。
(言語によっては、常に直接参照する形になる場合があります)
$funcArray = [];
for ($i = 0; $i < 3; $i++) {
    $funcArray[] = function() use ($i) {
        var_dump($i);
    };
}
print("\$i = " . $i . "\n");
$funcArray[0]();
$funcArray[1]();
$funcArray[2]();
実行結果
$i = 3
int(0)
int(1)
int(2)

無名関数から外部の変数の参照を受け取るには、use() で指定する変数の前に「&」を付けます。
上記の use の箇所を以下のようにすると、実行結果が変わります。
$funcArray[] = function() use (&$i) {
実行結果
$i = 3
int(3)
int(3)
int(3)

3. 使用例

次の例では、getCounter() で、カウントアップする関数を取得しています。
もう一度 getCounter() で取得すると、異なるカウンターが取得できます。
この時、2つのカウンター(無名関数)は、getCounter() の $c を参照していますが、
この変数の中身は、getCounter() を実行する度に毎回新しく生成されているため、
2つのカウンターが参照している $c は、異なるものとなります。
function getCounter() {
    $c = 0;
    return function() use (&$c) {
        $c = $c + 1;
        return $c;
    };
}

$counter1 = getCounter();
var_dump($counter1());
var_dump($counter1());
var_dump($counter1());

$counter2 = getCounter();
var_dump($counter2());
var_dump($counter2());
var_dump($counter2());
実行結果
int(1)
int(2)
int(3)
int(1)
int(2)
int(3)