こんにちは、かつコーチです。
前回はデフォルト引数と可変長引数を使って、関数の引数をより柔軟に扱う方法を解説しました。
今回は少しレベルを上げて、「名前を持たない関数」である無名関数と、そこから発展したクロージャを扱います。
最初は「なぜ名前をつけないのか」がピンとこないかもしれませんが、実務のコードでは非常によく登場する書き方なので、ここでしっかり慣れておきましょう。
無名関数とは?
名前のない関数
無名関数とは、その名の通り、名前をつけずにその場限りで定義する関数のことです。
これまで学んできた関数は、必ず名前がついていました。
<?php
function add($a, $b) {
return $a + $b;
}
echo add(3, 5); // 8
?>
無名関数は、これを名前なしで定義して、変数に代入する形で使います。
<?php
$add = function ($a, $b) {
return $a + $b;
};
echo $add(3, 5); // 8
?>
function の後ろに関数名を書かず、そのまま $add という変数に代入している点がポイントです。
呼び出すときは、変数名の後ろに () をつければ、普通の関数と同じように使えます。
なぜ無名関数が必要なのか
「わざわざ名前をつけない意味があるのか」と最初は疑問に思うかもしれません。
無名関数が特に力を発揮するのは、「関数の中で、一度だけ使う小さな処理」を渡したいときです。
前回・前々回で扱った配列を例に見てみましょう。
<?php
$numbers = [1, 2, 3, 4, 5];
// 配列の各要素に処理を適用するarray_map()
$doubled = array_map(function ($n) {
return $n * 2;
}, $numbers);
print_r($doubled); // [2, 4, 6, 8, 10]
?>
array_map() は「配列の各要素に、指定した関数を適用する」という関数です。
このとき渡す処理は、ここでしか使わない使い捨ての処理であることがほとんどです。
わざわざ function double($n) { return $n * 2; } のように名前をつけて別の場所に定義するより、その場に無名関数で書いてしまった方が、コードの意図がその場で分かりやすくなります。
無名関数の基本的な使い方
変数に代入して使う
無名関数は、通常の関数と同じように引数や戻り値を持てます。
<?php
$greet = function ($name) {
return "こんにちは、" . $name . "さん!";
};
echo $greet("かつコーチ"); // こんにちは、かつコーチさん!
?>
配列関数と組み合わせる
無名関数がよく使われる代表例が、配列を操作する関数との組み合わせです。
<?php
$prices = [1000, 2000, 3000];
// array_filter():条件に合う要素だけを残す
$expensive = array_filter($prices, function ($price) {
return $price >= 2000;
});
print_r($expensive); // [2000, 3000](添字は元のまま保持される)
// array_map():各要素を加工する
$taxIncluded = array_map(function ($price) {
return (int)($price * 1.1);
}, $prices);
print_r($taxIncluded); // [1100, 2200, 3300]
?>
array_filter() は「条件に合う要素だけを残す」、array_map() は「各要素を加工する」関数です。
どちらも「どう判定するか」「どう加工するか」の部分を無名関数として渡すことで、汎用的な処理を自分の目的に合わせてカスタマイズできます。
クロージャとuseキーワード
クロージャとは
クロージャとは、無名関数の中から、外側のスコープにある変数を取り込んで使えるようにしたものです。
PHPでは use キーワードを使って、外側の変数を無名関数の中に持ち込みます。
<?php
$taxRate = 0.1;
$calcTax = function ($price) use ($taxRate) {
return (int)($price * $taxRate);
};
echo $calcTax(1000); // 100
?>
use ($taxRate) と書くことで、外側で定義した $taxRate を無名関数の中でも使えるようになります。
use を書かないと、無名関数の中から外側の変数を参照することはできません。
use がないとどうなるか
use の必要性を、実際にエラーになる例で確認してみましょう。
<?php
$taxRate = 0.1;
$calcTax = function ($price) {
return (int)($price * $taxRate); // useしていないので$taxRateが見えない
};
echo $calcTax(1000);
?>
これを実行すると、次のような警告が出ます。
Warning: Undefined variable $taxRate in /path/to/file.php on line 5
私が初めてクロージャを使ったときにまさにこのエラーに遭遇し、「関数の外で定義した変数がなぜ見えないんだ」と混乱しました。
PHPの無名関数は、デフォルトでは外側のスコープの変数を自動的には見にいかない設計になっている、というのが理由です。
use を書いて明示的に「この変数を持ち込む」と宣言する必要があります。
値渡しと参照渡し
use には、値をコピーして渡す「値渡し」と、元の変数そのものを共有する「参照渡し」の2種類があります。
❌ Before:値渡しのつもりで参照渡しの動きを期待してしまう
<?php
$count = 0;
$increment = function () use ($count) {
$count++; // クロージャ内の$countはコピーなので、外側には影響しない
echo "内部: " . $count . "\n";
};
$increment(); // 内部: 1
$increment(); // 内部: 1(毎回コピーされ直すため、常に1のまま)
echo "外側: " . $count; // 外側: 0(外の$countは変化していない)
?>
use ($count) は、呼び出されるたびに外側の $count の値をコピーして使うだけなので、クロージャの中でいくら変更しても外側の変数には影響しません。
私はこの挙動を知らず、「カウントアップする処理のはずなのに、なぜかカウントが増えない」というバグに遭遇したことがあります。
✅ After:use (&$count) で参照渡しにする
<?php
$count = 0;
$increment = function () use (&$count) {
$count++; // &をつけると外側の$countそのものを共有する
echo "内部: " . $count . "\n";
};
$increment(); // 内部: 1
$increment(); // 内部: 2
echo "外側: " . $count; // 外側: 2(外の$countも一緒に更新されている)
?>
use (&$count) のように変数名の前に & をつけると、コピーではなく元の変数そのものを共有する「参照渡し」になります。
「外側の値をそのまま更新したいのか、あくまで呼び出し時点のスナップショットが欲しいだけなのか」を意識して、値渡しと参照渡しを使い分けましょう。
よくあるつまずきポイント・エラー対処
アロー関数との違いに戸惑う
PHP 7.4以降では、無名関数をさらに簡潔に書ける「アロー関数(fn)」も使えます。
<?php
$taxRate = 0.1;
// 従来のクロージャ(useが必要)
$calcTax1 = function ($price) use ($taxRate) {
return (int)($price * $taxRate);
};
// アロー関数(useを書かなくても外側の変数を自動で使える)
$calcTax2 = fn($price) => (int)($price * $taxRate);
echo $calcTax1(1000); // 100
echo $calcTax2(1000); // 100
?>
アロー関数は use を書かなくても、自動的に外側の変数を(値渡しで)取り込んでくれます。
ただし、アロー関数は本体を return を使わない1行の式で書く必要があり、複数行の複雑な処理には使えません。
「短い1行で済む処理はアロー関数、複数行にわたる処理は従来のクロージャ」という使い分けが基本になります。
応用・一歩先の使い方
実務でよく使われるコールバックとしてのクロージャ
無名関数・クロージャは、「他の関数に処理を渡す」ためのコールバックとして使われる場面が非常に多いです。
<?php
$users = [
["name" => "かつコーチ", "age" => 30],
["name" => "佐藤さん", "age" => 17],
["name" => "鈴木さん", "age" => 25],
];
// 20歳以上のユーザーだけを抽出する
$adults = array_filter($users, function ($user) {
return $user["age"] >= 20;
});
foreach ($adults as $user) {
echo $user["name"] . "\n";
}
// 出力:
// かつコーチ
// 鈴木さん
?>
これは、これまでの記事で扱ってきた「連想配列」「配列の配列」「関数」の知識がすべてつながる例です。
こうした「配列を渡して、条件やロジックはその場でクロージャとして渡す」というスタイルは、この後に学ぶLaravelのコレクション操作でも頻繁に登場します。
今のうちに慣れておくと、フレームワークの学習がぐっとスムーズになります。
まとめ
この記事のポイント
- 無名関数は名前をつけずにその場で定義する関数で、変数に代入して使う
- クロージャは、
useを使って外側のスコープの変数を無名関数の中に取り込んだもの use ($変数)は値渡し(コピー)、use (&$変数)は参照渡し(元の変数を共有)useを書き忘れるとUndefined variableの警告が出て、外側の変数を参照できない- 1行で済む簡単な処理は
fnによるアロー関数で簡潔に書ける
次に読むべき記事
無名関数・クロージャが分かったら、次はファイルを読み込むための require ・ include の違いを整理していきましょう。
→ 次の記事:require・require_once・include・include_onceの違いを徹底解説