PHPのクロージャとは、簡単に言うと変数として扱える関数です。
通常の関数は、あらかじめ名前を付けて定義します。
function greet($name) {
return "Hello, {$name}";
}
echo greet("Taro");
一方、クロージャは名前を持たない関数を変数に代入できます。
$greet = function ($name) {
return "Hello, {$name}";
};
echo $greet("Taro");
このように、関数そのものを変数に入れて、あとから呼び出せるのがクロージャの特徴です。
PHPでは、このような無名関数は内部的に Closure クラスのオブジェクトとして扱われます。
実務では、配列処理、コールバック、Laravelなどのフレームワーク、条件処理の切り替えなど、さまざまな場面で使われます。
クロージャの基本構文
変数に代入して使う
クロージャの基本形は次の通りです。
$変数名 = function (引数) {
処理
};
例えば、数値を2倍にするクロージャは次のように書けます。
$double = function ($number) {
return $number * 2;
};
echo $double(5); // 10
$double には関数が代入されているため、通常の関数のように $double(5) と呼び出せます。
型宣言もできる
クロージャでも、通常の関数と同じように引数や戻り値の型を指定できます。
$double = function (int $number): int {
return $number * 2;
};
echo $double(5); // 10
実務では、型を明示しておくことで、意図しない値が渡されたときのバグを防ぎやすくなります。
クロージャがよく使われる場面
array_map() で配列を加工する
クロージャは、配列の各要素を加工するときによく使われます。
$numbers = [1, 2, 3, 4, 5];
$doubled = array_map(function ($number) {
return $number * 2;
}, $numbers);
print_r($doubled);
実行結果は次のようになります。
Array
(
[0] => 2
[1] => 4
[2] => 6
[3] => 8
[4] => 10
)
array_map() は、配列の各要素に対して指定した処理を実行し、その結果を新しい配列として返します。
array_filter() で条件に合う要素だけ残す
条件に合う要素だけを残したい場合は、array_filter() とクロージャを組み合わせます。
$numbers = [1, 2, 3, 4, 5, 6];
$evenNumbers = array_filter($numbers, function ($number) {
return $number % 2 === 0;
});
print_r($evenNumbers);
実行結果は次のようになります。
Array
(
[1] => 2
[3] => 4
[5] => 6
)
ここでは、2で割り切れる数だけを残しています。
なお、array_filter() は元の配列のキーを維持します。
連番に振り直したい場合は、array_values() を使います。
$evenNumbers = array_values(array_filter($numbers, function ($number) {
return $number % 2 === 0;
}));
usort() で並び替える
クロージャは、配列の並び替え条件を指定するときにも使われます。
$users = [
['name' => 'Taro', 'age' => 20],
['name' => 'Jiro', 'age' => 17],
['name' => 'Saburo', 'age' => 25],
];
usort($users, function ($a, $b) {
return $a['age'] <=> $b['age'];
});
print_r($users);
<=> は宇宙船演算子と呼ばれる比較演算子です。
左辺が小さければ -1、等しければ 0、大きければ 1 を返します。
年齢の昇順ではなく降順にしたい場合は、比較する順番を逆にします。
usort($users, function ($a, $b) {
return $b['age'] <=> $a['age'];
});
外側の変数を使うには use が必要
クロージャ内では外側の変数をそのまま使えない
PHPのクロージャで特に重要なのが、外側の変数を使う場合の書き方です。
次のコードは、期待通りには動きません。
$message = "Hello";
$greet = function ($name) {
return $message . ", " . $name;
};
echo $greet("Taro");
クロージャの中では、外側で定義した $message をそのまま参照できません。
この場合、$message はクロージャ内で未定義の変数として扱われます。
外側の変数をクロージャ内で使いたい場合は、use を使います。
$message = "Hello";
$greet = function ($name) use ($message) {
return $message . ", " . $name;
};
echo $greet("Taro"); // Hello, Taro
use と引数は役割が違う
次の例を見てください。
$taxRate = 0.1;
$calculatePrice = function ($price) use ($taxRate) {
return $price + ($price * $taxRate);
};
echo $calculatePrice(1000); // 1100
このコードでは、$price と $taxRate の役割が違います。
$price は、クロージャを呼び出すときに渡される引数です。
function ($price)
一方、$taxRate は外側のスコープから取り込む変数です。
use ($taxRate)
つまり、次のように考えると分かりやすいです。
function ($price) use ($taxRate)
これは、「呼び出し時に $price を受け取り、外側にある $taxRate も使う」という意味です。
use は基本的に値渡し
クロージャ作成時点の値が取り込まれる
use ($value) で取り込んだ変数は、基本的にクロージャを作成した時点の値として扱われます。
$message = "Hello";
$greet = function ($name) use ($message) {
return $message . ", " . $name;
};
$message = "Hi";
echo $greet("Taro"); // Hello, Taro
この例では、クロージャを作成した時点で $message の値は "Hello" です。
その後、外側の $message を "Hi" に変更しても、クロージャ内で使われる値は "Hello" のままです。
そのため、use ($message) は「クロージャ作成時点の値を取り込む」と理解するとよいでしょう。
オブジェクトを取り込む場合は注意が必要
ただし、オブジェクトを use で取り込む場合は注意が必要です。
$user = new stdClass();
$user->name = 'Taro';
$fn = function () use ($user) {
$user->name = 'Jiro';
};
$fn();
echo $user->name; // Jiro
この例では、use ($user) と値渡しで取り込んでいますが、外側の $user->name も変更されています。
これは、$user という変数が同じオブジェクトを指しているためです。
use ($user) が参照渡しになっているわけではありません。
一方、クロージャ内で $user 自体を別のオブジェクトに差し替えても、外側の $user は変わりません。
$user = new stdClass();
$user->name = 'Taro';
$fn = function () use ($user) {
$user = new stdClass();
$user->name = 'Jiro';
};
$fn();
echo $user->name; // Taro
このように、オブジェクトの場合は「変数そのものがコピーされる」というより、「同じオブジェクトを指す値が取り込まれる」と理解すると正確です。
外側の変数を変更したい場合は参照渡しを使う
use (&$value) で外側の変数を変更できる
クロージャの中で外側の変数そのものを変更したい場合は、& を付けて参照渡しにします。
$count = 0;
$increment = function () use (&$count) {
$count++;
};
$increment();
$increment();
echo $count; // 2
use (&$count) と書くことで、外側の $count をクロージャ内から直接変更できます。
use ($value) では外側の変数は変わらない
一方、& を付けない場合、外側の変数は変わりません。
$count = 0;
$increment = function () use ($count) {
$count++;
};
$increment();
$increment();
echo $count; // 0
この場合、クロージャ内で $count++ を実行しても、外側の $count は 0 のままです。
さらに、値渡しで取り込んだ $count は、呼び出しのたびに保持され続けるわけではありません。
$count = 0;
$fn = function () use ($count) {
$count++;
echo $count . PHP_EOL;
};
$fn(); // 1
$fn(); // 1
echo $count; // 0
外側の値を継続的に変更・保持したい場合は、use (&$count) を使います。
アロー関数 fn との違い
アロー関数は短く書けるクロージャ
PHP 7.4以降では、アロー関数を使えます。
通常のクロージャは次のように書きます。
$numbers = [1, 2, 3];
$doubled = array_map(function ($number) {
return $number * 2;
}, $numbers);
アロー関数を使うと、次のように短く書けます。
$numbers = [1, 2, 3];
$doubled = array_map(fn($number) => $number * 2, $numbers);
単純な処理であれば、アロー関数のほうが読みやすくなります。
アロー関数は外側の変数を自動で取り込む
通常のクロージャでは、外側の変数を使うために use が必要です。
$taxRate = 0.1;
$prices = [1000, 2000, 3000];
$withTax = array_map(function ($price) use ($taxRate) {
return $price + ($price * $taxRate);
}, $prices);
アロー関数では、外側の変数を自動的に取り込めます。
$taxRate = 0.1;
$prices = [1000, 2000, 3000];
$withTax = array_map(
fn($price) => $price + ($price * $taxRate),
$prices
);
use ($taxRate) を書かなくても、外側の $taxRate を使えます。
アロー関数の取り込みは値渡し
アロー関数は外側の変数を自動で取り込みますが、その取り込みは値渡しです。
そのため、外側の変数を書き換える用途には向いていません。
$count = 0;
$fn = fn() => $count++;
$fn();
echo $count; // 0
外側の $count を変更したい場合は、通常のクロージャで use (&$count) を使います。
$count = 0;
$fn = function () use (&$count) {
$count++;
};
$fn();
echo $count; // 1
アロー関数は短い処理に向いている
アロー関数は、1つの式で表せる短い処理に向いています。
$ids = array_map(fn($user) => $user['id'], $users);
$activeUsers = array_filter(fn($user) => $user['active'], $users);
ただし、処理が複数行になる場合や、条件分岐が複雑になる場合は、通常のクロージャやメソッドに切り出したほうが読みやすくなります。
$formattedUsers = array_map(function ($user) {
$name = trim($user['name']);
$email = strtolower($user['email']);
return [
'name' => $name,
'email' => $email,
];
}, $users);
クロージャと $this
クラス内のクロージャでは $this を使える
クラスのメソッド内でクロージャを作ると、通常は $this を使えます。
class UserService
{
private string $prefix = 'User: ';
public function formatNames(array $names): array
{
return array_map(function ($name) {
return $this->prefix . $name;
}, $names);
}
}
$service = new UserService();
print_r($service->formatNames(['Taro', 'Jiro']));
この例では、クロージャ内から $this->prefix を参照しています。
クラスのメソッド内で作られた非staticなクロージャは、通常そのオブジェクトに束縛されるため、$this を使えます。
static function では $this を使えない
クロージャに static を付けると、$this は使えません。
class UserService
{
private string $prefix = 'User: ';
public function formatNames(array $names): array
{
return array_map(static function ($name) {
return $this->prefix . $name; // エラー
}, $names);
}
}
$this が不要な処理では、static function を使うことで「このクロージャはオブジェクトに依存しない」と明示できます。
class UserService
{
public function formatNames(array $names): array
{
return array_map(static function ($name) {
return strtoupper($name);
}, $names);
}
}
実務では、クラスのプロパティやメソッドを使わないクロージャは static function にしておくと、意図が明確になります。
クロージャを返す関数
関数からクロージャを返せる
クロージャは、関数の戻り値として返すこともできます。
function makeMultiplier($factor)
{
return function ($number) use ($factor) {
return $number * $factor;
};
}
$double = makeMultiplier(2);
$triple = makeMultiplier(3);
echo $double(10); // 20
echo $triple(10); // 30
この例では、makeMultiplier() がクロージャを返しています。
makeMultiplier(2) で作られたクロージャは、$factor に 2 を取り込んでいます。
makeMultiplier(3) で作られたクロージャは、$factor に 3 を取り込んでいます。
作成時の値を保持できる
重要なのは、makeMultiplier() の実行が終わったあとも、クロージャが $factor の値を保持していることです。
$double = makeMultiplier(2);
echo $double(10); // 20
このように、クロージャは作成時の周辺情報を保持できます。
「クロージャ」という名前は、外側の変数を閉じ込める、つまり閉包する性質に由来します。
実務で使えるクロージャの例
条件を動的に作る
クロージャを使うと、条件を動的に作ることができます。
例えば、指定したロールのユーザーだけを抽出する条件を作る関数は、次のように書けます。
function filterByRole(string $role): Closure
{
return function (array $user) use ($role): bool {
return $user['role'] === $role;
};
}
$users = [
['name' => 'Taro', 'role' => 'admin'],
['name' => 'Jiro', 'role' => 'editor'],
['name' => 'Saburo', 'role' => 'admin'],
];
$admins = array_filter($users, filterByRole('admin'));
print_r($admins);
filterByRole('admin') は、「role が admin のユーザーだけを通すクロージャ」を返します。
このように、クロージャを使うと、条件や処理内容をあとから柔軟に組み立てられます。
Laravelのルーティングで使う
Laravelでは、ルーティングでクロージャをよく使います。
Route::get('/hello', function () {
return 'Hello World';
});
これは、/hello にアクセスされたときに実行する処理を、クロージャとして渡している例です。
Laravelのクエリビルダで使う
Laravelのクエリビルダでも、条件をまとめるためにクロージャがよく使われます。
$users = User::where(function ($query) {
$query->where('role', 'admin')
->orWhere('role', 'editor');
})->get();
このように、複数の条件をグループ化したいときにもクロージャは便利です。
callable と Closure の違い
callable は呼び出し可能なもの全般を表す
PHPには callable という型があります。
callable は、関数として呼び出せるもの全般を表します。
例えば、次のようなものが callable として扱えます。
function hello() {
return 'Hello';
}
$callback1 = 'hello';
$callback2 = function () {
return 'Hello';
};
class Greeter
{
public function hello()
{
return 'Hello';
}
}
$greeter = new Greeter();
$callback3 = [$greeter, 'hello'];
$callback1 は関数名の文字列です。
$callback2 はクロージャです。
$callback3 はオブジェクトのメソッドを指定した配列です。
これらはいずれも、関数のように呼び出せるため callable として扱えます。
Closure はクロージャオブジェクトを表す
一方、Closure は無名関数やアロー関数などによって作られるクロージャオブジェクトを表します。
$callback = function () {
return 'Hello';
};
この $callback は Closure オブジェクトです。
型指定としては、広く受け取りたいなら callable、クロージャだけを受け取りたいなら Closure を使います。
function execute(callable $callback): mixed
{
return $callback();
}
function executeClosure(Closure $callback): mixed
{
return $callback();
}
実務では、関数名やメソッドも受け取りたい場合は callable、無名関数やアロー関数に限定したい場合は Closure、という使い分けになります。
Closure::fromCallable() と first-class callable syntax
既存の関数をクロージャに変換する
既存の関数やメソッドを Closure に変換したい場合は、Closure::fromCallable() を使えます。
function greet(string $name): string
{
return "Hello, {$name}";
}
$closure = Closure::fromCallable('greet');
echo $closure('Taro'); // Hello, Taro
PHP 8.1以降では ... を使える
PHP 8.1以降では、first-class callable syntax を使って、より簡潔にクロージャを作れます。
function greet(string $name): string
{
return "Hello, {$name}";
}
$closure = greet(...);
echo $closure('Taro'); // Hello, Taro
メソッドでも同じように使えます。
class Greeter
{
public function greet(string $name): string
{
return "Hello, {$name}";
}
}
$greeter = new Greeter();
$closure = $greeter->greet(...);
echo $closure('Taro');
クロージャを即時実行する
作ってすぐ実行できる
PHPでは、クロージャを作ってすぐに実行することもできます。
$result = (function () {
$a = 10;
$b = 20;
return $a + $b;
})();
echo $result; // 30
このような書き方は、JavaScriptのIIFEに近い使い方です。
スコープを分けたいときに使える
例えば、一時的な変数を外側に出したくない場合に使えます。
$config = (function () {
$env = getenv('APP_ENV');
if ($env === 'production') {
return ['debug' => false];
}
return ['debug' => true];
})();
ただし、PHPではこの書き方を多用するよりも、通常の関数やメソッドに分けたほうが読みやすい場合も多いです。
クロージャのメリット
処理を値として渡せる
クロージャの大きなメリットは、処理そのものを値として渡せることです。
function applyDiscount(array $prices, callable $discount): array
{
return array_map($discount, $prices);
}
$prices = [1000, 2000, 3000];
$result = applyDiscount($prices, function ($price) {
return $price * 0.9;
});
この例では、割引処理を外から渡しています。
処理内容を差し替えられるため、柔軟なコードを書きやすくなります。
小さな処理をその場に書ける
クロージャを使うと、短い処理をその場で書けます。
$activeUsers = array_filter($users, function ($user) {
return $user['status'] === 'active';
});
わざわざ isActiveUser() のような関数を別に作らなくても、処理の意図をその場で表現できます。
外側の値を保持できる
クロージャは、作成時に外側の値を取り込み、その値を保持できます。
function makePrefixer(string $prefix): Closure
{
return function (string $text) use ($prefix): string {
return $prefix . $text;
};
}
$errorPrefixer = makePrefixer('[ERROR] ');
echo $errorPrefixer('Something went wrong.');
このように、設定値や条件を持った関数を作れるのもクロージャの便利な点です。
クロージャの注意点
複雑な処理を書くと読みづらくなる
クロージャは便利ですが、長くなりすぎると可読性が下がります。
$result = array_map(function ($user) use ($settings, $logger, $formatter) {
if (!$user['active']) {
$logger->info('inactive user');
return null;
}
if ($settings['uppercase']) {
$user['name'] = strtoupper($user['name']);
}
$user['label'] = $formatter->format($user);
return $user;
}, $users);
このように処理が長い場合は、メソッドに切り出したほうが読みやすくなります。
$result = array_map([$this, 'formatUser'], $users);
参照渡しの多用に注意する
use (&$value) を使うと外側の変数を変更できますが、多用すると処理の流れが追いにくくなります。
$count = 0;
$fn = function () use (&$count) {
$count++;
};
外側の状態を変更するコードは、副作用が分かりにくくなることがあります。
可能であれば、外側の変数を直接変更するよりも、戻り値として結果を返す設計のほうが安全です。
$this を意図せず保持する場合がある
クラス内でクロージャを作ると、$this を保持する場合があります。
class ReportService
{
public function getFormatter(): Closure
{
return function ($value) {
return $this->format($value);
};
}
private function format($value): string
{
return strtoupper($value);
}
}
この場合、返されたクロージャは ReportService のインスタンスに依存します。
$this が不要な場合は、static function を使うことで、不要な束縛を避けられます。
return static function ($value) {
return strtoupper($value);
};
クロージャ・アロー関数・通常関数の使い分け
短い処理ならアロー関数
1行で意味が分かる短い処理なら、アロー関数が向いています。
$ids = array_map(fn($user) => $user['id'], $users);
$activeUsers = array_filter($users, fn($user) => $user['active']);
複数行の処理なら通常のクロージャ
複数行の処理を書く場合は、通常のクロージャのほうが読みやすいです。
$formattedUsers = array_map(function ($user) {
$name = trim($user['name']);
$email = strtolower($user['email']);
return [
'name' => $name,
'email' => $email,
];
}, $users);
再利用するなら関数やメソッドに切り出す
同じ処理を複数箇所で使う場合や、処理に名前を付けたほうが分かりやすい場合は、関数やメソッドに切り出すのがおすすめです。
$formattedUsers = array_map([$this, 'formatUser'], $users);
private function formatUser(array $user): array
{
return [
'name' => trim($user['name']),
'email' => strtolower($user['email']),
];
}
PHPのクロージャでよくあるミス
use を書き忘れる
外側の変数を使うのに use を書き忘れるケースはよくあります。
$rate = 0.1;
$prices = array_map(function ($price) {
return $price * (1 + $rate);
}, [1000, 2000]);
この場合、$rate はクロージャ内で未定義になります。
正しくは次のように書きます。
$rate = 0.1;
$prices = array_map(function ($price) use ($rate) {
return $price * (1 + $rate);
}, [1000, 2000]);
値渡しなのに外側の変数が変わると思ってしまう
次のコードでは、外側の $count は変わりません。
$count = 0;
$fn = function () use ($count) {
$count++;
};
$fn();
echo $count; // 0
外側の $count を変更したい場合は、参照渡しにします。
$count = 0;
$fn = function () use (&$count) {
$count++;
};
$fn();
echo $count; // 1
アロー関数で複雑な処理を書こうとする
アロー関数は短く書けて便利ですが、複雑な処理には向いていません。
$result = array_map(fn($user) => [
'name' => strtoupper(trim($user['name'])),
'status' => $user['active'] ? 'active' : 'inactive',
'score' => $user['score'] * 1.1,
], $users);
この程度であればまだ読めますが、条件分岐やログ出力、例外処理が増える場合は、通常のクロージャやメソッドに切り出したほうがよいです。
まとめ
クロージャは関数を値として扱う仕組み
PHPのクロージャは、関数を変数に入れたり、引数として渡したり、戻り値として返したりできる仕組みです。
基本形は次の通りです。
$fn = function ($arg) use ($outerValue) {
return $outerValue . $arg;
};
use の理解が重要
クロージャを理解するうえで、特に重要なのは use です。
外側の変数をクロージャ内で使いたい場合は、次のように書きます。
$message = 'Hello';
$fn = function ($name) use ($message) {
return "{$message}, {$name}";
};
外側の変数を変更したい場合は、参照渡しにします。
$count = 0;
$fn = function () use (&$count) {
$count++;
};
アロー関数との使い分けも大切
短い処理ならアロー関数が便利です。
fn($x) => $x * 2
複数行の処理や、明示的に use を使いたい場合は通常のクロージャが向いています。
function ($x) use ($y) {
return $x + $y;
}
処理が長い場合や再利用する場合は、関数やメソッドに切り出すと読みやすくなります。
PHPのクロージャは、配列処理やコールバック、Laravelなどのフレームワークで頻繁に使われる重要な機能です。
特に use、値渡し、参照渡し、アロー関数との違いを押さえておくと、実務でも迷わず使えるようになります。
以上、PHPのクロージャについてでした。
最後までお読みいただき、ありがとうございました。










