PHPの無名関数・クロージャ(use)をわかりやすく解説

phpアイキャッチ PHP

こんにちは、かつコーチです。

前回はデフォルト引数と可変長引数を使って、関数の引数をより柔軟に扱う方法を解説しました。

今回は少しレベルを上げて、「名前を持たない関数」である無名関数と、そこから発展したクロージャを扱います。

最初は「なぜ名前をつけないのか」がピンとこないかもしれませんが、実務のコードでは非常によく登場する書き方なので、ここでしっかり慣れておきましょう。

無名関数とは?

名前のない関数

無名関数とは、その名の通り、名前をつけずにその場限りで定義する関数のことです。

これまで学んできた関数は、必ず名前がついていました。

<?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 によるアロー関数で簡潔に書ける

次に読むべき記事

無名関数・クロージャが分かったら、次はファイルを読み込むための requireinclude の違いを整理していきましょう。

→ 次の記事:require・require_once・include・include_onceの違いを徹底解説

タイトルとURLをコピーしました