基礎

PHPのクロージャ(無名関数)|変数に代入できる関数の使い方

PHPのクロージャ(Closure)は、名前を持たない関数、いわゆる無名関数(Anonymous Function)のことです。変数に代入したり、他の関数の引数として渡したりできるため、コールバック処理や動的な関数生成に最適です。

クロージャは use キーワードを使って外部の変数を取り込む(キャプチャする)ことができ、関数型プログラミングのスタイルをPHPで実現する基盤となる機能です。

基本的な使い方

function キーワードの後に名前を付けずに定義し、変数に代入します。

PHP
<?php
// 無名関数を変数に代入
$greet = function($name) {
    return "こんにちは、{$name}さん!";
};

echo $greet("田中") . "\n";
echo $greet("佐藤") . "\n";
実行結果
こんにちは、田中さん!
こんにちは、佐藤さん!

通常の関数と異なり、定義の末尾にセミコロン ; が必要です。これは無名関数の定義が式(代入文の右辺)であるためです。変数 $greet を通じて、通常の関数と同じように呼び出せます。

useによる変数のキャプチャ

クロージャの外側にある変数を取り込むには use キーワードを使います。

PHP
<?php
$taxRate = 0.10;

$calcPrice = function($price) use ($taxRate) {
    return $price * (1 + $taxRate);
};

echo $calcPrice(1000) . "\n";
echo $calcPrice(500) . "\n";

// useは値のコピー(デフォルト)
$taxRate = 0.08;
echo $calcPrice(1000) . "\n";  // まだ0.10のまま
実行結果
1100
550
1100

デフォルトでは use は値のコピーを取り込みます。クロージャ定義後に外部変数を変更しても、クロージャ内の値は変わりません。参照渡しで取り込みたい場合は use (&$taxRate) のように & を付けます。

参照渡しのuse

PHP
<?php
$count = 0;

$increment = function() use (&$count) {
    $count++;
};

$increment();
$increment();
$increment();
echo "カウント: {$count}\n";
実行結果
カウント: 3

&$count により参照渡しでキャプチャしているため、クロージャ内での変更が外部の $count に反映されます。

コールバック関数として使う

クロージャは array_map()array_filter() などのコールバックとして頻繁に使われます。

PHP
<?php
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 偶数だけフィルタリング
$evens = array_filter($numbers, function($n) {
    return $n % 2 === 0;
});
echo "偶数: " . implode(", ", $evens) . "\n";

// 各要素を2乗
$squared = array_map(function($n) {
    return $n ** 2;
}, $numbers);
echo "2乗: " . implode(", ", $squared) . "\n";

// usortでカスタムソート
$users = [
    ["name" => "田中", "age" => 30],
    ["name" => "佐藤", "age" => 25],
    ["name" => "鈴木", "age" => 35],
];
usort($users, function($a, $b) {
    return $a["age"] - $b["age"];
});
foreach ($users as $u) {
    echo "{$u['name']}({$u['age']}歳) ";
}
echo "\n";
実行結果
偶数: 2, 4, 6, 8, 10
2乗: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100
佐藤(25歳) 田中(30歳) 鈴木(35歳) 

実用的な例

PHP
<?php
// 関数を返す関数(高階関数)
function multiplier(int $factor): Closure {
    return function(int $n) use ($factor): int {
        return $n * $factor;
    };
}

$double = multiplier(2);
$triple = multiplier(3);

echo $double(5) . "\n";   // 10
echo $triple(5) . "\n";   // 15
実行結果
10
15
ポイント

クロージャは内部的に Closure クラスのインスタンスです。Closure::bind()Closure::fromCallable() などのメソッドも利用できます。型宣言では Closure 型または callable 型で受け取れます。

注意

use で参照渡しを使うと、クロージャの外部からも値が変更される可能性があり、バグの原因になりやすいです。状態を持つクロージャが必要な場合はクラスの利用も検討してください。

まとめ

  • クロージャは名前のない関数で、変数に代入したり引数として渡せる
  • use キーワードで外部変数をキャプチャでき、デフォルトは値コピー
  • use (&$var) で参照渡しキャプチャも可能
  • array_map()array_filter() のコールバックに最適
  • 高階関数(関数を返す関数)の実装にも使える