PHP 匿名関数とアロー関数
アノニマス関数(Anonymous functions)は、クロージャ(closures)とも呼ばれ、名前を指定しない関数のことを指します。これらはバリアブル(変数)に代入してそのバリアブル経由で呼び出したり、他の関数に引数として渡したりすることができます。PHP 7.4 ではアロー関数(Arrow functions)が導入され、シンプルな匿名関数をより簡潔なシンタックスで記述できるようになりました。
これら 2 つのコア概念により、関数を「値」として扱うことが可能になり、コードの再利用性と柔軟性が劇的に向上します。本章では、これら強力な機能の使い方をマスターしていきましょう。
1. 匿名関数(クロージャ)
匿名関数は function キーワードを使用してデリクレア(宣言)されますが、関数名は指定しません。これらは定義された時点のスコープ内にあるバリアブルをキャプチャする能力を持っており、これが「クロージャ」と呼ばれる理由です。この能力により、匿名関数は親スコープのバリアブルを「封じ込める(エンクローズ)」ことができ、親スコープの実行が完了した後でも、それらのバリアブルに内部からアクセスし続けることができます。
1.1 基礎構文と使用方法
匿名関数を定義する際は、function キーワードの後に(必要な場合は)引数を続け、関数体を記述します。定義全体を一つのバリアブルに代入するのが一般的です。
<?php
// 匿名関数をバリアブルに代入
$greet = function($name) {
return "こんにちは、" . $name . "さん!";
};
// バリアブル経由で匿名関数を呼び出し
echo $greet("Alice"); // 出力: こんにちは、Aliceさん!
echo "\n";
// 匿名関数をコールバック引数として渡す
function processArray(array $numbers, callable $callback) {
$results = [];
foreach ($numbers as $number) {
$results[] = $callback($number);
}
return $results;
}
$numbers = [1, 2, 3, 4, 5];
// 匿名関数を渡し、各数値を2倍にする
$doubledNumbers = processArray($numbers, function($num) {
return $num * 2;
});
print_r($doubledNumbers);
/* 出力:
Array(
[0] => 2
[1] => 4
[2] => 6
[3] => 8
[4] => 10
)
*/
?>processArray の例では、匿名関数が直接 $callback 引数として渡されています。これは高階関数(他の関数を引数として受け取る関数)におけるクラシックな活用法です。
1.2 use キーワードによるバリアブルの継承
匿名関数は use キーワードを使用して、親スコープからバリアブルを継承できます。これにより、クロージャが実行される際、その定義時に存在していたバリアブルにアクセスすることが可能になります。
デフォルトでは、use を通じて継承されるバリアブルは「値渡し」です。つまり、親スコープで元のバリアブルを修正しても、クロージャ内部のコピーには影響しません。「参照渡し」で継承する必要がある場合は、バリアブル名の前にアンパサンド(&)を付けます。
<?php
$multiplier = 2;
$double = function($number) use ($multiplier) {
return $number * $multiplier;
};
echo $double(5); // 出力: 10
echo "\n";
// 'use' バリアブルの値渡しのデモンストレーション
$increment = 10;
$addIncrement = function($number) use ($increment) {
return $number + $increment;
};
echo $addIncrement(5); // 出力: 15
echo "\n";
$increment = 20; // 元のバリアブルの値を変更
echo $addIncrement(5); // 依然として 15 を出力 (クロージャ内の $increment コピーは 10 のまま)
echo "\n";
// 'use' バリアブルの参照渡しのデモンストレーション
$counter = 0;
$incrementCounter = function() use (&$counter) {
$counter++;
};
$incrementCounter();
$incrementCounter();
$incrementCounter();
echo $counter; // 出力: 3 (クロージャが外部の元のバリアブル $counter を修正)
echo "\n";
?>匿名関数が親スコープのバリアブルを修正し、その変更を関数の外部にも反映させたい場合、use (&$variable) を使用することが極めて重要です。
1.3 匿名関数の実践的なユースケース
匿名関数は、イベントリスナー、アレイのソート、非同期オペレーションなど、コールバック関数(callback)が必要なシーンで特に威力を発揮します。
usort() を使用したアレイのソート: カスタムロジックでアレイをソートしたい場合、usort() は匿名関数を比較関数として受け取ります。
<?php
$products = [
['name' => 'ノートパソコン', 'price' => 1200],
['name' => 'マウス', 'price' => 25],
['name' => 'キーボード', 'price' => 75],
];
// 価格に基づいてプロダクトを昇順にソート
usort($products, function($a, $b) {
return $a['price'] <=> $b['price']; // <=> は宇宙船演算子 (PHP 7+ 導入)
});
print_r($products);
/* 出力:
Array(
[0] => Array
(
[name] => マウス
[price] => 25
)
[1] => Array
(
[name] => キーボード
[price] => 75
)
[2] => Array
(
[name] => ノートパソコン
[price] => 1200
)
)
*/
?>array_filter() を使用したアレイのフィルタリング: 匿名関数を使用することで、簡潔で明快なフィルタリング条件を記述できます。
<?php
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 偶数を抽出
$evenNumbers = array_filter($numbers, function($num) {
return $num % 2 == 0;
});
print_r($evenNumbers);
/* 出力:
Array(
[1] => 2
[3] => 4
[5] => 6
[7] => 8
[9] => 10
)
*/
?>2. アロー関数(短縮クロージャ)
PHP 7.4 で導入されたアロー関数は、匿名関数を記述するためのより短いシンタックスを提供します。特にシンプルな 1 行のエクスプレッション(式)のみを持つコールバック関数に最適です。これらは「インプリシット(暗黙的)スコープ」を持っており、親スコープのバリアブルを自動的に値渡しでキャプチャします。これにより、手動で use キーワードを書く手間が省けます。
2.1 構文とバリアブルの自動キャプチャ
アロー関数は fn キーワードで定義され、引数、アロー =>、そして 1 行のエクスプレッションが続きます。このエクスプレッションの結果は暗黙的にリターンされます(return キーワードは不要です)。
<?php
// $greet 匿名関数のアロー関数による等価な書き方
$greetArrow = fn($name) => "こんにちは、" . $name . "さん!";
echo $greetArrow("Bob"); // 出力: こんにちは、Bobさん!
echo "\n";
// processArray 内でのアロー関数の使用
function processArray(array $numbers, callable $callback) {
$results = [];
foreach ($numbers as $number) {
$results[] = $callback($number);
}
return $results;
}
$numbers = [10, 20, 30];
// アロー関数を使用して各数値を2倍にする
$doubledNumbersArrow = processArray($numbers, fn($num) => $num * 2);
print_r($doubledNumbersArrow);
/* 出力:
Array(
[0] => 20
[1] => 40
[2] => 60)
*/
echo "\n";
// アロー関数は親スコープのバリアブルを値渡しで自動キャプチャする
$factor = 3;
$multiplyByFactor = fn($number) => $number * $factor;
echo $multiplyByFactor(4); // 出力: 12
echo "\n";
$factor = 5; // 元のバリアブルを変更
echo $multiplyByFactor(4); // 依然として 12 を出力 (クロージャ内の $factor コピーは 3 のまま)
echo "\n";
// 参照によってバリアブルを継承したい場合は、依然として 'use (&$variable)' を伴う通常の匿名関数が必要です
// アロー関数は参照による継承をサポートしていません。
?>バリアブルの自動キャプチャ機能により、アロー関数は短いデータ変換タスクにおいて無類の利便性を発揮します。ただし、これらは値渡しでキャプチャされることを忘れないでください。外部のバリアブルを修正する必要がある場合は、依然として従来の匿名関数を使用する必要があります。
2.2 アロー関数の実践的な応用
コンパクトな単一エクスプレッションのコールバックが必要なシーンで、アロー関数はその真価を発揮します。
アレイのフィルタリングとマッピング(例:array_map, array_filter)
<?php
$items = [
['name' => 'デスク', 'price' => 150, 'in_stock' => true],
['name' => 'チェア', 'price' => 50, 'in_stock' => false],
['name' => 'デスクランプ', 'price' => 30, 'in_stock' => true],
];
// 在庫があるアイテムの名称のみを取得
$inStockNames = array_map(fn($item) => $item['name'], array_filter($items, fn($item) => $item['in_stock']));
print_r($inStockNames);
/* 出力:
Array(
[0] => デスク
[1] => デスクランプ
)
*/
echo "\n";
// 全アイテムの合計金額を計算 (各数量 1 と仮定)
$totalPrice = array_reduce($items, fn($carry, $item) => $carry + $item['price'], 0);
echo "合計金額: $" . $totalPrice; // 出力: 合計金額: $230
echo "\n";
?>イベントハンドリング(シミュレーション)
シンプルなイベントディスパッチャーを構築するシーンを想定してください。アロー関数を使用してイベントハンドラーを定義できます。
<?php
class EventDispatcher {
private $listeners = [];
public function on(string $eventName, callable $callback) {
$this->listeners[$eventName][] = $callback;
}
public function dispatch(string $eventName, array $data = []) {
if (isset($this->listeners[$eventName])) {
foreach ($this->listeners[$eventName] as $listener) {
$listener($data);
}
}
}
}
$dispatcher = new EventDispatcher();
$logFile = 'app.log'; // 外部バリアブル。値渡しでキャプチャされます
// アロー関数をイベントリスナーとして使用
$dispatcher->on('user.registered', fn($eventData) =>
file_put_contents($logFile, "ユーザー '{$eventData['username']}' が " . date('Y-m-d H:i:s') . " に登録されました\n", FILE_APPEND));
$dispatcher->dispatch('user.registered', ['username' => 'john.doe', 'email' => '[email protected]']);
// これにより app.log に "ユーザー 'john.doe' が 2026-05-08 20:19:00 に登録されました" といったログが追記されます
echo "ユーザー登録イベントがディスパッチされ、ログに記録されました。\n";
?>この例では、アロー関数 fn($eventData) => ... が簡潔なイベントリスナーとして機能し、親スコープ内の $logFile バリアブルを自動的にキャプチャしています。